Added nlwav to tools. nlwav converts netlist logs into wav files.

Example usage:

./nlwav -i netlist.log_RO.1.log -o tt.wav

./nlwav -h 

[Couriersud]
This commit is contained in:
couriersud 2015-07-26 20:02:50 +02:00
parent f3686bb125
commit 8b388e9131
4 changed files with 408 additions and 4 deletions

View File

@ -34,7 +34,7 @@ NETLIST_START(dummy)
// .END
SOLVER(Solver, 24000)
PARAM(Solver.ACCURACY, 1e-7)
PARAM(Solver.ACCURACY, 1e-9)
PARAM(Solver.NR_LOOPS, 90)
PARAM(Solver.SOR_FACTOR, 0.001)
PARAM(Solver.GS_LOOPS, 1)
@ -50,8 +50,8 @@ NETLIST_START(dummy)
//CLOCK(I_CONGA_H0, 2)
TTL_INPUT(I_CONGA_L0, 0)
//CLOCK(I_CONGA_L0, 2)
//TTL_INPUT(I_GORILLA0, 0)
CLOCK(I_GORILLA0, 2)
TTL_INPUT(I_GORILLA0, 0)
//CLOCK(I_GORILLA0, 2)
TTL_INPUT(I_RIM0, 0)
//CLOCK(I_RIM0, 2)

View File

@ -618,6 +618,42 @@ files {
MAME_DIR .. "src/tools/nltool.c",
}
--------------------------------------------------
-- nlwav
--------------------------------------------------
project("nlwav")
uuid ("7c5396d1-2a1a-4c93-bed6-6b8fa182054a")
kind "ConsoleApp"
options {
"ForceCPP",
}
flags {
"Symbols", -- always include minimum symbols for executables
}
if _OPTIONS["SEPARATE_BIN"]~="1" then
targetdir(MAME_DIR)
end
links {
"utils",
"ocore_" .. _OPTIONS["osd"],
"netlist",
}
includedirs {
MAME_DIR .. "src/osd",
MAME_DIR .. "src/lib/util",
MAME_DIR .. "src/emu/netlist",
}
files {
MAME_DIR .. "src/tools/nlwav.c",
}
--------------------------------------------------
-- castool
--------------------------------------------------

View File

@ -104,6 +104,7 @@ public:
opt_file("f", "file", "-", "file to process (default is stdin)", this),
opt_type("y", "type", "spice", "spice:eagle", "type of file to be converted: spice,eagle", this),
opt_cmd ("c", "cmd", "run", "run|convert|listdevices", this),
opt_inp( "i", "input", "", "input file to process (default is none)", this),
opt_verb("v", "verbose", "be verbose - this produces lots of output", this),
opt_quiet("q", "quiet", "be quiet - no warnings", this),
opt_help("h", "help", "display help", this)
@ -115,6 +116,7 @@ public:
poption_str opt_file;
poption_str_limit opt_type;
poption_str opt_cmd;
poption_str opt_inp;
poption_bool opt_verb;
poption_bool opt_quiet;
poption_bool opt_help;
@ -263,6 +265,66 @@ void usage(tool_options_t &opts)
fprintf(stderr, "%s\n", opts.help().cstr());
}
struct input_t
{
netlist::netlist_time m_time;
netlist::param_t *m_param;
double m_value;
input_t()
{
}
input_t(netlist::netlist_t *netlist, const pstring &line)
{
char buf[400];
double t;
int e = sscanf(line.cstr(), "%lf,%[^,],%lf", &t, buf, &m_value);
if ( e!= 3)
throw netlist::fatalerror_e("error %d scanning line %s\n", e, line.cstr());
m_time = netlist::netlist_time::from_double(t);
m_param = netlist->setup().find_param(buf, true);
}
void setparam()
{
switch (m_param->param_type())
{
case netlist::param_t::MODEL:
case netlist::param_t::STRING:
throw netlist::fatalerror_e("param %s is not numeric\n", m_param->name().cstr());
case netlist::param_t::DOUBLE:
static_cast<netlist::param_double_t*>(m_param)->setTo(m_value);
break;
case netlist::param_t::INTEGER:
static_cast<netlist::param_int_t*>(m_param)->setTo((int)m_value);
break;
case netlist::param_t::LOGIC:
static_cast<netlist::param_logic_t*>(m_param)->setTo((int) m_value);
break;
}
}
};
plist_t<input_t> *read_input(netlist::netlist_t *netlist, pstring fname)
{
plist_t<input_t> *ret = palloc(plist_t<input_t>());
if (fname != "")
{
pstring_list_t lines(filetobuf(fname) , "\n");
for (unsigned i=0; i<lines.size(); i++)
{
pstring l = lines[i].trim();
if (l != "")
{
input_t inp(netlist, l);
ret->add(inp);
}
}
}
return ret;
}
static void run(tool_options_t &opts)
{
netlist_tool_t nt;
@ -271,14 +333,28 @@ static void run(tool_options_t &opts)
nt.m_opts = &opts;
nt.init();
nt.read_netlist(filetobuf(opts.opt_file()), opts.opt_name());
plist_t<input_t> *inps = read_input(&nt, opts.opt_inp());
double ttr = opts.opt_ttr();
printf("startup time ==> %5.3f\n", (double) (osd_ticks() - t) / (double) osd_ticks_per_second() );
printf("runnning ...\n");
t = osd_ticks();
nt.process_queue(netlist::netlist_time::from_double(ttr));
unsigned pos = 0;
netlist::netlist_time nlt = netlist::netlist_time::zero;
while (pos < inps->size() && (*inps)[pos].m_time < netlist::netlist_time::from_double(ttr))
{
nt.process_queue((*inps)[pos].m_time - nlt);
(*inps)[pos].setparam();
nlt = (*inps)[pos].m_time;
pos++;
}
nt.process_queue(netlist::netlist_time::from_double(ttr) - nlt);
nt.stop();
pfree(inps);
double emutime = (double) (osd_ticks() - t) / (double) osd_ticks_per_second();
printf("%f seconds emulation took %f real time ==> %5.2f%%\n", ttr, emutime, ttr/emutime*100.0);

292
src/tools/nlwav.c Normal file
View File

@ -0,0 +1,292 @@
#include <cstdio>
#include <cstring>
#include "plib/poptions.h"
#include "plib/pstring.h"
#include "plib/plists.h"
#include "nl_setup.h"
class nlwav_options_t : public poptions
{
public:
nlwav_options_t() :
poptions(),
#if 0
opt_ttr ("t", "time_to_run", 1.0, "time to run the emulation (seconds)", this),
opt_name("n", "name", "", "netlist in file to run; default is first one", this),
opt_logs("l", "logs", "", "colon separated list of terminals to log", this),
opt_file("f", "file", "-", "file to process (default is stdin)", this),
opt_type("y", "type", "spice", "spice:eagle", "type of file to be converted: spice,eagle", this),
opt_cmd ("c", "cmd", "run", "run|convert|listdevices", this),
opt_inp( "i", "input", "", "input file to process (default is none)", this),
#endif
opt_inp( "i", "input", "", "input file", this),
opt_out( "o", "output", "", "output file", this),
opt_amp( "a", "amp", 10000.0, "amplification after mean correction", this),
opt_verb("v", "verbose", "be verbose - this produces lots of output", this),
opt_quiet("q", "quiet", "be quiet - no warnings", this),
opt_help("h", "help", "display help", this)
{}
#if 0
poption_double opt_ttr;
poption_str opt_name;
poption_str opt_logs;
poption_str opt_file;
poption_str_limit opt_type;
poption_str opt_cmd;
#endif
poption_str opt_inp;
poption_str opt_out;
poption_double opt_amp;
poption_bool opt_verb;
poption_bool opt_quiet;
poption_bool opt_help;
};
/* http://de.wikipedia.org/wiki/RIFF_WAVE */
class wav_t
{
public:
wav_t(const pstring &fn, unsigned sr)
{
m_f = std::fopen(fn.cstr(),"w");
if (m_f==NULL)
throw netlist::fatalerror_e("Error opening output file: %s", fn.cstr());
initialize(sr);
std::fwrite(&m_fh, sizeof(m_fh), 1, m_f);
std::fwrite(&m_fmt, sizeof(m_fmt), 1, m_f);
std::fwrite(&m_data, sizeof(m_data), 1, m_f);
}
~wav_t()
{
close();
}
unsigned channels() { return m_fmt.channels; }
unsigned sample_rate() { return m_fmt.sample_rate; }
void write_sample(int sample)
{
m_data.len += m_fmt.block_align;
short ps = sample; /* 16 bit sample, FIXME: powerpc? */
std::fwrite(&ps, sizeof(ps), 1, m_f);
}
void close()
{
if (m_f != NULL)
{
std::fseek(m_f, 0, SEEK_SET);
std::fwrite(&m_fh, sizeof(m_fh), 1, m_f);
std::fwrite(&m_fmt, sizeof(m_fmt), 1, m_f);
//data.len = fmt.block_align * n;
std::fwrite(&m_data, sizeof(m_data), 1, m_f);
std::fclose(m_f);
m_f = NULL;
}
}
private:
struct riff_chunk_t
{
char group_id[4];
unsigned filelen;
char rifftype[4];
};
struct riff_format_t
{
char signature[4];
unsigned fmt_length;
short format_tag;
short channels;
unsigned sample_rate;
unsigned bytes_per_second;
short block_align;
short bits_sample;
};
struct riff_data_t
{
char signature[4];
unsigned len;
// data follows
};
void initialize(unsigned sr)
{
std::strncpy(m_fh.group_id, "RIFF", 4);
m_fh.filelen = 0; // Fixme
std::strncpy(m_fh.rifftype, "WAVE", 4);
std::strncpy(m_fmt.signature, "fmt ", 4);
m_fmt.fmt_length = 16;
m_fmt.format_tag = 0x0001; //PCM
m_fmt.channels = 1;
m_fmt.sample_rate = sr;
m_fmt.bits_sample = 16;
m_fmt.block_align = m_fmt.channels * ((m_fmt.bits_sample + 7) / 8);
m_fmt.bytes_per_second = m_fmt.sample_rate * m_fmt.block_align;
std::strncpy(m_data.signature, "data", 4);
m_data.len = m_fmt.bytes_per_second * 2 * 0;
}
riff_chunk_t m_fh;
riff_format_t m_fmt;
riff_data_t m_data;
FILE *m_f;
};
void convert(nlwav_options_t &opts)
{
wav_t wo(opts.opt_out(), 48000);
FILE *FIN = std::fopen(opts.opt_inp(),"r");
if (FIN==NULL)
throw netlist::fatalerror_e("Error opening input file: %s", opts.opt_inp().cstr());
double dt = 1.0 / (double) wo.sample_rate();
double ct = dt;
//double mean = 2.4;
double amp = opts.opt_amp();
double mean = 0.0;
double means = 0.0;
double cursam = 0.0;
double outsam = 0.0;
double lt = 0.0;
double maxsam = -1e9;
double minsam = 1e9;
int n = 0;
//short sample = 0;
while(!std::feof(FIN))
{
#if 1
float t = 0.0; float v = 0.0;
fscanf(FIN, "%f %f", &t, &v);
while (t >= ct)
{
outsam += (ct - lt) * cursam;
outsam = outsam / dt;
if (t>0.0)
{
means += outsam;
maxsam = std::max(maxsam, outsam);
minsam = std::min(minsam, outsam);
n++;
//mean = means / (double) n;
mean += 5.0 / (double) wo.sample_rate() * (outsam - mean);
}
outsam = (outsam - mean) * amp;
outsam = std::max(-32000.0, outsam);
outsam = std::min(32000.0, outsam);
wo.write_sample((int) outsam);
outsam = 0.0;
lt = ct;
ct += dt;
}
outsam += (t-lt)*cursam;
lt = t;
cursam = v;
#else
float t = 0.0; float v = 0.0;
fscanf(FIN, "%f %f", &t, &v);
while (ct <= t)
{
wo.write_sample(sample);
n++;
ct += dt;
}
means += v;
mean = means / (double) n;
v = v - mean;
v = v * amp;
if (v>32000.0)
v = 32000.0;
else if (v<-32000.0)
v = -32000.0;
sample = v;
//printf("%f %f\n", t, v);
#endif
}
printf("Mean (low freq filter): %f\n", mean);
printf("Mean (static): %f\n", means / (double) n);
printf("Amp + %f\n", 32000.0 / (maxsam- mean));
printf("Amp - %f\n", -32000.0 / (minsam- mean));
wo.close();
fclose(FIN);
}
void usage(nlwav_options_t &opts)
{
fprintf(stderr,
"Usage:\n"
" nltool -help\n"
" nltool [options]\n"
"\n"
"Where:\n"
);
fprintf(stderr, "%s\n", opts.help().cstr());
}
int main(int argc, char *argv[])
{
#if (!PSTANDALONE)
track_memory(true);
{
#endif
nlwav_options_t opts;
int ret;
if ((ret = opts.parse(argc, argv)) != argc)
{
fprintf(stderr, "Error parsing %s\n", argv[ret]);
usage(opts);
return 1;
}
if (opts.opt_help())
{
usage(opts);
return 1;
}
convert(opts);
#if (!PSTANDALONE)
}
dump_unfreed_mem();
#endif
return 0;
}
/*
Der Daten-Abschnitt enthält die Abtastwerte:
Offset Länge Inhalt Beschreibung
36 (0x24) 4 'data' Header-Signatur
40 (0x28) 4 <length> Länge des Datenblocks, max. <Dateigröße>44
0 (0x00) char 4 'RIFF'
4 (0x04) unsigned 4 <Dateigröße>8
8 (0x08) char 4 'WAVE'
Der fmt-Abschnitt (24 Byte) beschreibt das Format der einzelnen Abtastwerte:
Offset Länge Inhalt Beschreibung
12 (0x0C) 4 'fmt ' Header-Signatur (folgendes Leerzeichen beachten)
16 (0x10) 4 <fmt length> Länge des restlichen fmt-Headers (16 Bytes)
20 (0x14) 2 <format tag> Datenformat der Abtastwerte (siehe separate Tabelle weiter unten)
22 (0x16) 2 <channels> Anzahl der Kanäle: 1 = mono, 2 = stereo; mittlerweile sind auch mehr als 2 Kanäle (z. B. für Raumklang) möglich.[2]
24 (0x18) 4 <sample rate> Samples pro Sekunde je Kanal (z. B. 44100)
28 (0x1C) 4 <bytes/second> Abtastrate·Frame-Größe
32 (0x20) 2 <block align> Frame-Größe = <Anzahl der Kanäle>·((<Bits/Sample (eines Kanals)>+7)/8) (Division ohne Rest)
34 (0x22) 2 <bits/sample> Anzahl der Datenbits pro Samplewert je Kanal (z. B. 12)
*/