diff --git a/src/lib/netlist/buildVS/netlist.sln b/src/lib/netlist/buildVS/netlist.sln index a06f407d3c2..433d034cf6f 100755 --- a/src/lib/netlist/buildVS/netlist.sln +++ b/src/lib/netlist/buildVS/netlist.sln @@ -1,12 +1,14 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30011.22 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "netlistlib", "netlistlib.vcxproj", "{A374399B-B87F-4E0F-9525-6C099600705F}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nltool", "nltool.vcxproj", "{9204EC28-A29B-4A36-9E47-2C46041D67D3}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nlwav", "nlwav.vcxproj", "{48D0ADAF-62EC-472E-A51B-8D837280649D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -31,8 +33,19 @@ Global {9204EC28-A29B-4A36-9E47-2C46041D67D3}.Release|x64.Build.0 = Release|x64 {9204EC28-A29B-4A36-9E47-2C46041D67D3}.Release|x86.ActiveCfg = Release|Win32 {9204EC28-A29B-4A36-9E47-2C46041D67D3}.Release|x86.Build.0 = Release|Win32 + {48D0ADAF-62EC-472E-A51B-8D837280649D}.Debug|x64.ActiveCfg = Debug|x64 + {48D0ADAF-62EC-472E-A51B-8D837280649D}.Debug|x64.Build.0 = Debug|x64 + {48D0ADAF-62EC-472E-A51B-8D837280649D}.Debug|x86.ActiveCfg = Debug|Win32 + {48D0ADAF-62EC-472E-A51B-8D837280649D}.Debug|x86.Build.0 = Debug|Win32 + {48D0ADAF-62EC-472E-A51B-8D837280649D}.Release|x64.ActiveCfg = Release|x64 + {48D0ADAF-62EC-472E-A51B-8D837280649D}.Release|x64.Build.0 = Release|x64 + {48D0ADAF-62EC-472E-A51B-8D837280649D}.Release|x86.ActiveCfg = Release|Win32 + {48D0ADAF-62EC-472E-A51B-8D837280649D}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {992701A0-B9F5-4F7D-BF3F-5B0EBEA51F04} + EndGlobalSection EndGlobal diff --git a/src/lib/netlist/buildVS/netlistlib.vcxproj.user b/src/lib/netlist/buildVS/netlistlib.vcxproj.user deleted file mode 100755 index abe8dd8961e..00000000000 --- a/src/lib/netlist/buildVS/netlistlib.vcxproj.user +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/lib/netlist/buildVS/nlwav.vcxproj b/src/lib/netlist/buildVS/nlwav.vcxproj new file mode 100755 index 00000000000..7b191fedccd --- /dev/null +++ b/src/lib/netlist/buildVS/nlwav.vcxproj @@ -0,0 +1,162 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + {a374399b-b87f-4e0f-9525-6c099600705f} + + + + 16.0 + {48D0ADAF-62EC-472E-A51B-8D837280649D} + Win32Proj + nlwav + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + false + + + true + + + true + + + false + + + + + + Level3 + true + true + + + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)..\..\ + + + Console + true + true + true + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + \ No newline at end of file diff --git a/src/lib/netlist/buildVS/nlwav.vcxproj.filters b/src/lib/netlist/buildVS/nlwav.vcxproj.filters new file mode 100755 index 00000000000..7b114761bb4 --- /dev/null +++ b/src/lib/netlist/buildVS/nlwav.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/src/lib/netlist/plib/poptions.h b/src/lib/netlist/plib/poptions.h index 3a807c3c7f0..2db0e7e9585 100755 --- a/src/lib/netlist/plib/poptions.h +++ b/src/lib/netlist/plib/poptions.h @@ -12,6 +12,7 @@ #include "pstonum.h" #include "pstring.h" #include "putil.h" +#include "pfmtlog.h" namespace plib { @@ -27,7 +28,7 @@ namespace plib { PCOPYASSIGNMOVE(option_base, delete) - pstring help() const { return m_help; } + virtual pstring help() const { return m_help; } private: pstring m_help; }; @@ -169,16 +170,23 @@ namespace plib { public: option_num(options &parent, const pstring &ashort, const pstring &along, T defval, const pstring &help, - T minval = std::numeric_limits::min(), + T minval = std::numeric_limits::lowest(), T maxval = std::numeric_limits::max() ) : option(parent, ashort, along, help, true) , m_val(defval) , m_min(minval) , m_max(maxval) + , m_def(defval) {} T operator ()() const { return m_val; } + pstring help() const override + { + auto hs(option::help()); + return plib::pfmt(hs)(m_def, m_min, m_max); + } + protected: int parse(const pstring &argument) override { @@ -191,6 +199,7 @@ namespace plib { T m_val; T m_min; T m_max; + T m_def; }; class option_vec : public option diff --git a/src/lib/netlist/prg/nltool.cpp b/src/lib/netlist/prg/nltool.cpp index ca05dc689b7..f2d18092629 100644 --- a/src/lib/netlist/prg/nltool.cpp +++ b/src/lib/netlist/prg/nltool.cpp @@ -1195,14 +1195,9 @@ int tool_app_t::execute() return 1; } } - catch (netlist::nl_exception &e) - { - perr("Netlist exception caught: {}\n", e.text()); - return 2; - } catch (plib::pexception &e) { - perr("plib exception caught: {}\n", e.text()); + perr("Exception caught: {}\n", e.text()); return 2; } diff --git a/src/lib/netlist/prg/nlwav.cpp b/src/lib/netlist/prg/nlwav.cpp old mode 100644 new mode 100755 index 0606c583e07..a444e2f9ef9 --- a/src/lib/netlist/prg/nlwav.cpp +++ b/src/lib/netlist/prg/nlwav.cpp @@ -1,14 +1,21 @@ // license:GPL-2.0+ // copyright-holders:Couriersud -#include "plib/pstring.h" +#include "netlist/plib/pstring.h" #include "netlist/nl_setup.h" -#include "plib/plists.h" -#include "plib/pmain.h" -#include "plib/ppmf.h" -#include "plib/pstream.h" +#include "netlist/plib/plists.h" +#include "netlist/plib/pmain.h" +#include "netlist/plib/ppmf.h" +#include "netlist/plib/pstream.h" #include +// see below - this belongs somewhere else! +#ifdef _WIN32 +#include +#include +#include +#endif + // From: https://ffmpeg.org/pipermail/ffmpeg-devel/2007-October/038122.html // The most compatible way to make a wav header for unknown length is to put // 0xffffffff in the header. 0 as the RIFF length and 0 as the data chunk length @@ -24,12 +31,20 @@ class wav_t { public: - // XXNOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) - wav_t(std::ostream &strm, bool is_seekable, std::size_t sr, std::size_t channels) + + enum format + { + s16, + s32, + f32 + }; + + wav_t(std::ostream &strm, bool is_seekable, format fmt, std::size_t sr, std::size_t channels) : m_f(strm) , m_stream_is_seekable(is_seekable) + , m_format(fmt) // force "play" to play and warn about eof instead of being silent - , m_fmt(static_cast(channels), static_cast(sr)) + , m_fmt(static_cast(channels), static_cast(sr), fmt) , m_data(is_seekable ? 0 : 0xffffffff) { @@ -60,17 +75,43 @@ public: template void write(const T &val) { + static_assert(sizeof(std::ostream::char_type) == 1, "char_type size must be 1"); auto ptr(reinterpret_cast(&val)); m_f.write(ptr, sizeof(T)); } - void write_sample(const int *sample) + template + void write_sample_int(double sample) + { + const auto mmax(static_cast(plib::numeric_limits::max())); + const auto mmin(static_cast(plib::numeric_limits::min())); + + sample *= mmax; + sample = std::max(mmin, sample); + sample = std::min(mmax, sample); + const auto dest(static_cast(sample)); + write(dest); + } + + // expects normalized samples between -1.0 to 1.0 for s16 and s32 + void write_samples(double *sample) { m_data.len += m_fmt.block_align; for (std::size_t i = 0; i < channels(); i++) { - auto ps = static_cast(sample[i]); // 16 bit sample, FIXME: Endianess? - write(ps); + switch (m_format) + { + case s16: + write_sample_int(sample[i]); + break; + case s32: + write_sample_int(sample[i]); + break; + case f32: + const auto df32(static_cast(sample[i])); + write(df32); + break; + } } } @@ -84,8 +125,23 @@ private: struct riff_format_t { - riff_format_t(uint16_t achannels, uint32_t asample_rate) + riff_format_t(uint16_t achannels, uint32_t asample_rate, format fm) { + switch (fm) + { + case s16: + format_tag = 0x0001; // PCM + bits_sample = 16; + break; + case s32: + format_tag = 0x0001; // PCM + bits_sample = 32; + break; + case f32: + format_tag = 0x0003; // FLOAT + bits_sample = 32; + break; + } channels = achannels; sample_rate = asample_rate; block_align = channels * ((bits_sample + 7) / 8); @@ -93,12 +149,12 @@ private: } std::array signature = {{'f','m','t',' '}}; uint32_t fmt_length = 16; - uint16_t format_tag = 0x0001; // PCM + uint16_t format_tag; uint16_t channels; uint32_t sample_rate; uint32_t bytes_per_second; uint16_t block_align; - uint16_t bits_sample = 16; + uint16_t bits_sample; }; struct riff_data_t @@ -111,6 +167,7 @@ private: std::ostream &m_f; bool m_stream_is_seekable; + format m_format; riff_chunk_t m_fh; riff_format_t m_fmt; @@ -240,52 +297,130 @@ private: std::vector cursam; }; +struct filter_hp +{ + using callback_type = plib::pmfp; + + filter_hp(double freq, bool boost, std::size_t channels, callback_type cb) + : m_channels(channels) + , m_cb(cb) + , m_hp_omega(plib::constants::two() * plib::constants::pi() * freq) + , m_boost(boost) + , m_lt(channels, 0.0) + , m_in(channels, 0.0) + , m_cap(channels, 0.0) + { } + void process(std::size_t chan, double time, double val) + { + // based on CR filter + auto dt(time - m_lt[chan]); + + double omega = ((m_boost && (time < 1.0/m_hp_omega)) ? 1e12 : m_hp_omega); + auto m(1.0 - plib::exp(-dt * omega)); + m_cap[chan] += m * (m_in[chan] - m_cap[chan]); + // out = in - vcap + m_cb(chan, time, m_in[chan] - m_cap[chan]); + + m_in[chan] = val; + m_lt[chan] = time; + } + +private: + std::size_t m_channels; + callback_type m_cb; + double m_hp_omega; + bool m_boost; + std::vector m_lt; + std::vector m_in; + std::vector m_cap; +}; + +struct filter_lp +{ + using callback_type = plib::pmfp; + + filter_lp(double freq, std::size_t channels, callback_type cb) + : m_channels(channels) + , m_cb(cb) + , m_lp_omega(plib::constants::two() * plib::constants::pi() * freq) + , m_lt(channels, 0.0) + , m_in(channels, 0.0) // lp filter + , m_cap(channels, 0.0) // hp filter + { } + void process(std::size_t chan, double time, double val) + { + // based on RC filter + auto dt(time - m_lt[chan]); + + auto m(1.0 - plib::exp(-dt * m_lp_omega)); + + m_cap[chan] += m * (m_in[chan] - m_cap[chan]); + // out = vcap + m_cb(chan, time, m_cap[chan]); + + m_in[chan] = val; + m_lt[chan] = time; + } + +private: + std::size_t m_channels; + callback_type m_cb; + double m_lp_omega; + std::vector m_lt; + std::vector m_in; + std::vector m_cap; +}; + class wavwriter { public: - wavwriter(std::ostream &fo, bool is_seekable, std::size_t channels, std::size_t sample_rate, double ampa) - : mean(channels, 0.0) - , means(channels, 0.0) - , maxsam(channels, -1e9) + wavwriter(std::ostream &fo, bool is_seekable, wav_t::format fmt, + std::size_t channels, std::size_t sample_rate, double ampa) + : maxsam(channels, -1e9) , minsam(channels, 1e9) , m_n(channels, 0) , m_samples(channels, 0) , m_last_time(0) , m_fo(fo) - , m_amp(ampa) - , m_wo(m_fo, is_seekable, sample_rate, channels) + , m_amp(ampa <= 0.0 ? 1.0e6 : ampa) + , m_auto(ampa <= 0.0) + , m_wo(m_fo, is_seekable, fmt, sample_rate, channels) { } void process(std::size_t chan, double time, double outsam) { if (time > m_last_time) - m_wo.write_sample(m_samples.data()); + m_wo.write_samples(m_samples.data()); m_last_time = time; - means[chan] += outsam; maxsam[chan] = std::max(maxsam[chan], outsam); minsam[chan] = std::min(minsam[chan], outsam); m_n[chan]++; - //mean = means / (double) m_n; - mean[chan] += 5.0 / static_cast(m_wo.sample_rate()) * (outsam - mean[chan]); - outsam = (outsam - mean[chan]) * m_amp; - outsam = std::max(-32000.0, outsam); - outsam = std::min(32000.0, outsam); - m_samples[chan] = static_cast(outsam); + auto val(outsam * m_amp); + if (m_auto && plib::abs(val) > 1.0) + { + do + { + m_amp /= 2.0; + val = outsam * m_amp; + } while (plib::abs(val) > 1.0); + // FIXME: log this in state and provide on verbose output + //printf("dynamp adjusted to %f at %f\n", m_amp, time); + } + m_samples[chan] = val; } - std::vector mean; - std::vector means; std::vector maxsam; std::vector minsam; std::vector m_n; - std::vector m_samples; + std::vector m_samples; double m_last_time; private: std::ostream &m_fo; double m_amp; + bool m_auto; wav_t m_wo; }; @@ -441,18 +576,23 @@ class nlwav_app : public plib::app public: nlwav_app() : plib::app(), - opt_fmt(*this, "f", "format", 0, std::vector({"wav","vcda","vcdd", "tab"}), - "output format. Available options are wav|vcda|vcdd|tab." - " wav : multichannel wav output" - " vcda : analog VCD output" - " vcdd : digital VCD output" - " tab : sampled output" + opt_fmt(*this, "f", "format", 0, std::vector({"wav16s","wav32s","wav32f","vcda","vcdd", "tab"}), + "output format. Available options are wav16s|wav32s|wav32f|vcda|vcdd|tab.\n" + " wav16s : multichannel wav output 16 bit signed\n" + " wav32s : multichannel wav output 32 bit signed\n" + " wav32f : multichannel wav output 32 bit float\n" + " vcda : analog VCD output\n" + " vcdd : digital VCD output\n" + " tab : sampled output\n" " Digital signals are created using the --high and --low options" ), opt_out(*this, "o", "output", "-", "output file"), opt_grp1(*this, "wav options", "These options apply to wav output only"), opt_rate(*this, "r", "rate", 48000, "sample rate of output file"), opt_amp(*this, "a", "amp", 10000.0, "amplification after mean correction"), + opt_lowpass(*this, "", "lowpass", 20000.0, "lowpass filter frequency.\nDefault {1:.0} Hz."), + opt_highpass(*this, "", "highpass", 20.0, "highpass filter frequency.\nDefault is {1:.0} Hz."), + opt_hpboost(*this, "", "hpboost", "enable highpass boost to filter out initial click."), opt_grp2(*this, "vcdd options", "These options apply to vcdd output only"), opt_high(*this, "u", "high", 2.0, "minimum input for high level"), opt_low(*this, "l", "low", 1.0, "maximum input for low level"), @@ -468,8 +608,8 @@ public: opt_help(*this, "h", "help", "display help and exit"), opt_ex1(*this, "./nlwav -f vcdd -o x.vcd log_V*", "convert all files starting with \"log_V\" into a digital vcd file"), - opt_ex2(*this, "./nlwav -f wav -o x.wav log_V*", - "convert all files starting with \"log_V\" into a multichannel wav file"), + opt_ex2(*this, "./nlwav -f wav16s -o x.wav log_V*", + "convert all files starting with \"log_V\" into a multichannel wav file (16bit, signed)"), opt_ex3(*this, "./nlwav -f tab -o x.tab -s 0.0000005 -i 0.000001 -n 256 log_BLUE.log", "convert file log_BLUE.log to sampled output. First sample at 500ns " "followed by 255 samples every micro-second.") @@ -479,7 +619,7 @@ public: pstring usage() override; private: - void convert_wav(std::ostream &ostrm); + void convert_wav(std::ostream &ostrm, wav_t::format fmt); void convert_vcd(std::ostream &ostrm, vcdwriter::format_e format); void convert_tab(std::ostream &ostrm); void convert(const pstring &outname); @@ -489,6 +629,9 @@ private: plib::option_group opt_grp1; plib::option_num opt_rate; plib::option_num opt_amp; + plib::option_num opt_lowpass; + plib::option_num opt_highpass; + plib::option_bool opt_hpboost; plib::option_group opt_grp2; plib::option_num opt_high; plib::option_num opt_low; @@ -509,16 +652,20 @@ private: std::vector> m_instrms; }; -void nlwav_app::convert_wav(std::ostream &ostrm) +void nlwav_app::convert_wav(std::ostream &ostrm, wav_t::format fmt) { double dt = plib::reciprocal(static_cast(opt_rate())); + auto nchan = m_instrms.size(); - plib::unique_ptr wo = plib::make_unique(ostrm, opt_out() != "-", m_instrms.size(), opt_rate(), opt_amp()); - plib::unique_ptr ago = plib::make_unique(m_instrms.size(), dt, aggregator::callback_type(&wavwriter::process, wo.get())); - aggregator::callback_type agcb = log_processor::callback_type(&aggregator::process, ago.get()); + auto wo = plib::make_unique(ostrm, opt_out() != "-", fmt, nchan, opt_rate(), opt_amp()); + auto ago = plib::make_unique(nchan, dt, aggregator::callback_type(&wavwriter::process, wo.get())); + auto fgo_hp = plib::make_unique(opt_highpass(), opt_hpboost(), nchan, filter_hp::callback_type(&aggregator::process, ago.get())); + auto fgo_lp = plib::make_unique(opt_lowpass(), nchan, filter_lp::callback_type(&filter_hp::process, fgo_hp.get())); - log_processor lp(m_instrms.size(), agcb); + auto topcb = log_processor::callback_type(&filter_lp::process, fgo_lp.get()); + + log_processor lp(nchan, topcb); lp.process(m_instrms); @@ -591,6 +738,10 @@ static void open_ostream_and_exec(pstring fname, bool binary, F func) else { std::cout.imbue(std::locale::classic()); + // FIXME: switch to binary on windows +#ifdef _WIN32 + _setmode(_fileno(stdout), _O_BINARY); +#endif func(std::cout); } } @@ -600,15 +751,21 @@ void nlwav_app::convert(const pstring &outname) switch (opt_fmt()) { case 0: - open_ostream_and_exec(outname, true, [this](std::ostream &ostrm) { convert_wav(ostrm); }); + open_ostream_and_exec(outname, true, [this](std::ostream &ostrm) { convert_wav(ostrm, wav_t::s16); }); break; case 1: - open_ostream_and_exec(outname, false, [this](std::ostream &ostrm) { convert_vcd(ostrm, vcdwriter::ANALOG); }); + open_ostream_and_exec(outname, true, [this](std::ostream &ostrm) { convert_wav(ostrm, wav_t::s32); }); break; case 2: - open_ostream_and_exec(outname, false, [this](std::ostream &ostrm) { convert_vcd(ostrm, vcdwriter::DIGITAL); }); + open_ostream_and_exec(outname, true, [this](std::ostream &ostrm) { convert_wav(ostrm, wav_t::f32); }); break; case 3: + open_ostream_and_exec(outname, false, [this](std::ostream &ostrm) { convert_vcd(ostrm, vcdwriter::ANALOG); }); + break; + case 4: + open_ostream_and_exec(outname, false, [this](std::ostream &ostrm) { convert_vcd(ostrm, vcdwriter::DIGITAL); }); + break; + case 5: open_ostream_and_exec(outname, false, [this](std::ostream &ostrm) { convert_tab(ostrm); }); break; default: @@ -637,23 +794,34 @@ int nlwav_app::execute() return 0; } - for (const auto &oi: opt_args()) + try { - plib::unique_ptr fin; - if (oi == "-") + for (const auto &oi: opt_args()) { - auto temp(plib::make_unique()); - plib::copystream(*temp, std::cin); - fin = std::move(temp); + plib::unique_ptr fin; + if (oi == "-") + { + auto temp(plib::make_unique()); + plib::copystream(*temp, std::cin); + fin = std::move(temp); + } + else + { + fin = plib::make_unique(plib::filesystem::u8path(oi), std::ios::in); + if (fin->fail()) + throw plib::file_open_e(oi); + } + fin->imbue(std::locale::classic()); + m_instrms.push_back(std::move(fin)); } - else - fin = plib::make_unique(plib::filesystem::u8path(oi)); - fin->imbue(std::locale::classic()); - m_instrms.push_back(std::move(fin)); + + convert(opt_out()); + } + catch (plib::pexception &e) + { + perr("Exception caught: {}\n", e.text()); + return 1; } - - convert(opt_out()); - return 0; }