From 36a62ae3403f7cfb02bd8dde43b6b2fa96fe867b Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sat, 14 May 2016 20:27:56 +0200 Subject: Make all components use the same Random instance and add seed method on DrumGizmo class. Added dgreftest application for doing reference midifile rendering tests. --- configure.ac | 3 +- src/drumgizmo.cc | 9 +- src/drumgizmo.h | 3 + src/drumkitloader.cc | 6 +- src/drumkitloader.h | 3 +- src/drumkitparser.cc | 5 +- src/drumkitparser.h | 3 +- src/instrument.cc | 4 +- src/instrument.h | 6 +- src/powerlist.cc | 3 +- src/powerlist.h | 4 +- src/random.cc | 6 +- src/random.h | 2 + src/velocity.cc | 3 +- src/velocity.h | 4 +- test/Makefile.am | 2 + test/dgreftest/Makefile.am | 36 +++++++ test/dgreftest/compareoutputengine.cc | 138 ++++++++++++++++++++++++ test/dgreftest/compareoutputengine.h | 55 ++++++++++ test/dgreftest/dgreftest.cc | 136 ++++++++++++++++++++++++ test/dgreftest/midiinputengine.cc | 192 ++++++++++++++++++++++++++++++++++ test/dgreftest/midiinputengine.h | 64 ++++++++++++ test/dgreftest/wavfileoutputengine.cc | 125 ++++++++++++++++++++++ test/dgreftest/wavfileoutputengine.h | 54 ++++++++++ 24 files changed, 847 insertions(+), 19 deletions(-) create mode 100644 test/dgreftest/Makefile.am create mode 100644 test/dgreftest/compareoutputengine.cc create mode 100644 test/dgreftest/compareoutputengine.h create mode 100644 test/dgreftest/dgreftest.cc create mode 100644 test/dgreftest/midiinputengine.cc create mode 100644 test/dgreftest/midiinputengine.h create mode 100644 test/dgreftest/wavfileoutputengine.cc create mode 100644 test/dgreftest/wavfileoutputengine.h diff --git a/configure.ac b/configure.ac index f44226a..b28830e 100644 --- a/configure.ac +++ b/configure.ac @@ -582,7 +582,8 @@ AC_CONFIG_FILES( plugingui/Makefile include/Makefile man/Makefile - test/Makefile + test/Makefile + test/dgreftest/Makefile drumgizmo/Makefile) AC_OUTPUT() diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index 1ccefc5..7d9d88c 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -50,7 +50,7 @@ DrumGizmo::DrumGizmo(Settings& settings, AudioOutputEngine *o, AudioInputEngine *i) - : loader(settings, kit, *i, resamplers) + : loader(settings, kit, *i, resamplers, rand) , oe(o) , ie(i) , kit() @@ -115,6 +115,11 @@ void DrumGizmo::setFreeWheel(bool freewheel) } } +void DrumGizmo::setRandomSeed(unsigned int seed) +{ + rand.setSeed(seed); +} + void DrumGizmo::run(int endpos) { size_t pos = 0; @@ -143,6 +148,8 @@ void DrumGizmo::run(int endpos) bool DrumGizmo::run(size_t pos, sample_t *samples, size_t nsamples) { + std::lock_guard guard(resamplers.mutex); + setFrameSize(nsamples); ie->pre(); diff --git a/src/drumgizmo.h b/src/drumgizmo.h index e25db2f..ea04603 100644 --- a/src/drumgizmo.h +++ b/src/drumgizmo.h @@ -70,6 +70,8 @@ public: void setFreeWheel(bool freewheel); + void setRandomSeed(unsigned int seed); + private: static constexpr int MAX_NUM_CHANNELS = 64; static constexpr int RESAMPLER_OUTPUT_BUFFER = 4096; @@ -101,4 +103,5 @@ protected: std::vector events; Settings& settings; SettingsGetter getter{settings}; + Random rand; }; diff --git a/src/drumkitloader.cc b/src/drumkitloader.cc index a9b87f0..814a98d 100644 --- a/src/drumkitloader.cc +++ b/src/drumkitloader.cc @@ -36,12 +36,14 @@ DrumKitLoader::DrumKitLoader(Settings& settings, DrumKit& kit, AudioInputEngine& ie, - Resamplers& resamplers) + Resamplers& resamplers, + Random& rand) : settings(settings) , getter(settings) , kit(kit) , ie(ie) , resamplers(resamplers) + , rand(rand) { run(); run_semaphore.wait(); // Wait for the thread to actually start. @@ -80,7 +82,7 @@ bool DrumKitLoader::loadkit(const std::string& file) settings.drumkit_load_status.store(LoadStatus::Loading); - DrumKitParser parser(settings, kit); + DrumKitParser parser(settings, kit, rand); if(parser.parseFile(file)) { ERR(drumgizmo, "Drumkit parser failed: %s\n", file.c_str()); diff --git a/src/drumkitloader.h b/src/drumkitloader.h index f99f439..cca11af 100644 --- a/src/drumkitloader.h +++ b/src/drumkitloader.h @@ -51,7 +51,7 @@ class DrumKitLoader public: //! The constrcutor starts the loader thread. DrumKitLoader(Settings& settings, DrumKit& kit, AudioInputEngine& ie, - Resamplers& resamplers); + Resamplers& resamplers, Random& rand); //! The destructor signals the thread to stop and waits to merge before //! returning (ie. deleting the object will garantuee that the thread has @@ -91,4 +91,5 @@ protected: AudioInputEngine& ie; Resamplers& resamplers; MemChecker memchecker; + Random& rand; }; diff --git a/src/drumkitparser.cc b/src/drumkitparser.cc index 048a05b..073d240 100644 --- a/src/drumkitparser.cc +++ b/src/drumkitparser.cc @@ -34,10 +34,11 @@ #include "path.h" #include "drumgizmo.h" -DrumKitParser::DrumKitParser(Settings& settings, DrumKit& k) +DrumKitParser::DrumKitParser(Settings& settings, DrumKit& k, Random& rand) : kit(k) , refs(REFSFILE) , settings(settings) + , rand(rand) { } @@ -183,7 +184,7 @@ void DrumKitParser::endTag(const std::string& name) { if(name == "instrument") { - Instrument* instrument = new Instrument(settings); + Instrument* instrument = new Instrument(settings, rand); instrument->setGroup(instr_group); InstrumentParser parser(*instrument); diff --git a/src/drumkitparser.h b/src/drumkitparser.h index 0adccb9..444b459 100644 --- a/src/drumkitparser.h +++ b/src/drumkitparser.h @@ -34,7 +34,7 @@ class DrumKitParser : public SAXParser { public: - DrumKitParser(Settings& setting, DrumKit& kit); + DrumKitParser(Settings& setting, DrumKit& kit, Random& rand); virtual int parseFile(const std::string& filename) override; @@ -53,4 +53,5 @@ private: ConfigFile refs; Settings& settings; + Random& rand; }; diff --git a/src/instrument.cc b/src/instrument.cc index cc052e9..9a4c0b7 100644 --- a/src/instrument.cc +++ b/src/instrument.cc @@ -30,8 +30,10 @@ #include "sample.h" -Instrument::Instrument(Settings& settings) +Instrument::Instrument(Settings& settings, Random& rand) : settings(settings) + , rand(rand) + , powerlist(rand) { DEBUG(instrument, "new %p\n", this); mod = 1.0; diff --git a/src/instrument.h b/src/instrument.h index a531aec..621dddb 100644 --- a/src/instrument.h +++ b/src/instrument.h @@ -42,7 +42,7 @@ class Instrument { friend class InstrumentParser; public: - Instrument(Settings& settings); + Instrument(Settings& settings, Random& rand); ~Instrument(); Sample* sample(level_t level, size_t pos); @@ -69,7 +69,6 @@ private: VersionStr version; RangeMap samples; - PowerList powerlist; void addSample(level_t a, level_t b, Sample* s); void finalise(); ///< Signal instrument that no more samples will be added. @@ -79,7 +78,8 @@ private: size_t lastpos; float mod; Settings& settings; - Random rand; + Random& rand; + PowerList powerlist; }; // typedef std::map< std::string, Instrument > Instruments; diff --git a/src/powerlist.cc b/src/powerlist.cc index f94dbb2..8fec8ce 100644 --- a/src/powerlist.cc +++ b/src/powerlist.cc @@ -51,7 +51,8 @@ #define SIZE 500 -PowerList::PowerList() +PowerList::PowerList(Random& rand) + : rand(rand) { power_max = 0; power_min = 100000000; diff --git a/src/powerlist.h b/src/powerlist.h index a3af475..53a42b8 100644 --- a/src/powerlist.h +++ b/src/powerlist.h @@ -34,7 +34,7 @@ class PowerList { public: - PowerList(); + PowerList(Random& rand); void add(Sample* s); void finalise(); ///< Call this when no more samples will be added. @@ -49,7 +49,7 @@ private: float power; }; - Random rand; + Random& rand; std::vector samples; float power_max; diff --git a/src/random.cc b/src/random.cc index 1df9a62..3d94a25 100644 --- a/src/random.cc +++ b/src/random.cc @@ -31,10 +31,14 @@ Random::Random() : Random(std::chrono::system_clock::now().time_since_epoch().count()) { - } Random::Random(unsigned int seed) +{ + setSeed(seed); +} + +void Random::setSeed(unsigned int seed) { generator.seed(seed); } diff --git a/src/random.h b/src/random.h index 9eaefad..c7fd599 100644 --- a/src/random.h +++ b/src/random.h @@ -35,6 +35,8 @@ public: Random(); Random(unsigned int seed); + void setSeed(unsigned int seed); + //! \return random int in range [, ]. int intInRange(int lower_bound, int upper_bound); diff --git a/src/velocity.cc b/src/velocity.cc index c8faa32..2d9bf8f 100644 --- a/src/velocity.cc +++ b/src/velocity.cc @@ -28,10 +28,11 @@ #include -Velocity::Velocity(unsigned int lower, unsigned int upper) +Velocity::Velocity(unsigned int lower, unsigned int upper, Random& rand) : lower{lower} , upper{upper} , samples{} + , rand(rand) { } diff --git a/src/velocity.h b/src/velocity.h index 19284a4..8392494 100644 --- a/src/velocity.h +++ b/src/velocity.h @@ -34,7 +34,7 @@ class Velocity { public: - Velocity(unsigned int lower, unsigned int upper); + Velocity(unsigned int lower, unsigned int upper, Random& rand); void addSample(Sample* sample, float probability); Sample* getSample(); @@ -46,5 +46,5 @@ private: typedef std::map Samples; Samples samples; - Random rand; + Random& rand; }; diff --git a/test/Makefile.am b/test/Makefile.am index a3a6294..594aa0d 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,4 +1,6 @@ # Rules for the test code (use `make check` to execute) +SUBDIRS = dgreftest + include $(top_srcdir)/src/Makefile.am.drumgizmo TESTS = resource engine gui resampler lv2 configfile audiocache \ diff --git a/test/dgreftest/Makefile.am b/test/dgreftest/Makefile.am new file mode 100644 index 0000000..cacf26a --- /dev/null +++ b/test/dgreftest/Makefile.am @@ -0,0 +1,36 @@ +if ENABLE_CLI +if HAVE_INPUT_MIDIFILE + +include $(top_srcdir)/src/Makefile.am.drumgizmo + +bin_PROGRAMS = dgreftest + +dgreftest_LDADD = $(DRUMGIZMO_LIBS) $(PTHREAD_LIBS) -ldl $(JACK_LIBS) \ + $(SMF_LIBS) + +dgreftest_LDFLAGS = + +dgreftest_CXXFLAGS = $(SNDFILE_CXXFLAGS) $(PTHREAD_CFLAGS) $(EXPAT_CFLAGS) \ + -I$(top_srcdir)/include -I$(top_srcdir)/src \ + -I$(top_srcdir)/hugin -DWITH_HUG_MUTEX -DWITH_HUG_FILTER \ + $(JACK_CFLAGS) $(SSEFLAGS) $(SMF_CFLAGS) + +dgreftest_CFLAGS = -DWITH_HUG_MUTEX -DWITH_HUG_FILTER + +dgreftest_SOURCES = \ + dgreftest.cc \ + midiinputengine.cc \ + wavfileoutputengine.cc \ + compareoutputengine.cc \ + $(DRUMGIZMO_SOURCES) \ + $(top_srcdir)/hugin/hugin.c \ + $(top_srcdir)/hugin/hugin_filter.c + +endif # HAVE_INPUT_MIDIFILE + +EXTRA_DIST = \ + midiinputengine.h \ + wavfileoutputengine.h \ + compareoutputengine.h + +endif # ENABLE_CLI diff --git a/test/dgreftest/compareoutputengine.cc b/test/dgreftest/compareoutputengine.cc new file mode 100644 index 0000000..4b13566 --- /dev/null +++ b/test/dgreftest/compareoutputengine.cc @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * compareoutputengine.cc + * + * Sat May 14 13:27:04 CEST 2016 + * Copyright 2016 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * DrumGizmo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with DrumGizmo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "compareoutputengine.h" + +#include + +CompareOutputEngine::CompareOutputEngine() + : AudioOutputEngine{} + , info{} + , file{"output"} +{ + info.samplerate = 44100; + info.channels = 1; + info.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; +} + +CompareOutputEngine::~CompareOutputEngine() +{ + std::cout << "diff_samples: " << diff_samples << std::endl; + sf_close(handle); +} + +bool CompareOutputEngine::init(const Channels& data) +{ + info.channels = data.size(); + + handle = sf_open(file.c_str(), SFM_READ, &info); + if(handle == nullptr) + { + std::cerr << "[CompareOutputEngine] Failed to open " + << file << " for writing.\n"; + return false; + } + + return true; +} + +void CompareOutputEngine::setParm(const std::string& parm, const std::string& value) +{ + if(parm == "file") + { + // apply output filename prefix + file = value; + } + else if(parm == "srate") + { + // try to apply samplerate + try + { + info.samplerate = std::stoi(value); + } + catch(...) + { + std::cerr << "[CompareOutputEngine] Invalid samplerate " << value + << "\n"; + } + } + else + { + std::cerr << "[CompareOutputEngine] Unsupported parameter '" << parm + << "'\n"; + } +} + +bool CompareOutputEngine::start() +{ + return true; +} + +void CompareOutputEngine::stop() +{ +} + +void CompareOutputEngine::pre(size_t nsamples) +{ +} + +void CompareOutputEngine::run(int ch, sample_t* samples, size_t nsamples) +{ + if(ch >= info.channels) + { + std::cerr << "[CompareOutputEngine] cannot access channel #" << ch + << " (" << info.channels << " channels available)\n"; + return; + } + + for(std::size_t i = 0; i < nsamples; ++i) + { + buffer[i * info.channels + ch] = samples[i]; + } +} + +void CompareOutputEngine::post(size_t nsamples) +{ + sample_t ref_buffer[sizeof(buffer) / sizeof(sample_t)]; + sf_readf_float(handle, ref_buffer, nsamples); + + for(std::size_t i = 0; i < nsamples; ++i) + { + for(std::size_t ch = 0; ch < (std::size_t)info.channels; ++ch) + { + if(buffer[i * info.channels + ch] != ref_buffer[i * info.channels + ch]) + { + ++diff_samples; + } + } + } + +} + +size_t CompareOutputEngine::getSamplerate() const +{ + return info.samplerate; +} diff --git a/test/dgreftest/compareoutputengine.h b/test/dgreftest/compareoutputengine.h new file mode 100644 index 0000000..fb610d6 --- /dev/null +++ b/test/dgreftest/compareoutputengine.h @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * compareoutputengine.h + * + * Sat May 14 13:27:04 CEST 2016 + * Copyright 2016 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * DrumGizmo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with DrumGizmo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#pragma once + +#include +#include + +class CompareOutputEngine + : public AudioOutputEngine +{ +public: + CompareOutputEngine(); + ~CompareOutputEngine(); + + // based on AudioOutputEngine + bool init(const Channels& data) override; + void setParm(const std::string& parm, const std::string& value) override; + bool start() override; + void stop() override; + void pre(size_t nsamples) override; + void run(int ch, sample_t* samples, size_t nsamples) override; + void post(size_t nsamples) override; + size_t getSamplerate() const override; + +private: + SF_INFO info; + SNDFILE* handle; + std::string file; + sample_t buffer[4096 * 16]; + std::size_t diff_samples{0}; +}; diff --git a/test/dgreftest/dgreftest.cc b/test/dgreftest/dgreftest.cc new file mode 100644 index 0000000..072dcdc --- /dev/null +++ b/test/dgreftest/dgreftest.cc @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * dgreftest.cc + * + * Thu May 12 17:50:08 CEST 2016 + * Copyright 2016 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * DrumGizmo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with DrumGizmo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include + +#include +#include +#include +#include + +#include +#include + +#include "compareoutputengine.h" +#include "midiinputengine.h" +#include "wavfileoutputengine.h" + +#include // required for c++11 + +std::unique_ptr createOutput(const std::string& name) +{ + struct stat sb; + if((stat(name.c_str(), &sb) == -1) && (errno == ENOENT)) + { + // Ref file doesn't exist - create it. + return std::make_unique(); + } + else + { + // Ref file exists, do compare. + return std::make_unique(); + } +} + +int main(int argc, char* argv[]) +{ + if(argc < 4) + { + std::cerr << "Usage: " << argv[0] << " testname drumkit midimap [seed]" << std::endl; + return 1; + } + + Settings settings; + + std::string test = argv[1]; + std::string kitfile = argv[2]; + std::string midimap = argv[3]; + + unsigned int seed = 0; + if(argc == 5) + { + seed = atoi(argv[4]); + } + + std::string midifile = test + ".mid"; + std::string reffile = test + ".wav"; + + MidifileInputEngine ie; + ie.setParm("file", midifile.c_str()); + + auto oe = createOutput(reffile); + + oe->setParm("file", reffile.c_str()); + oe->setParm("srate", "44100"); + + DrumGizmo drumgizmo(settings, oe.get(), &ie); + drumgizmo.setRandomSeed(seed); + drumgizmo.setFreeWheel(true); // Run in-sync with disk-cache + drumgizmo.setFrameSize(oe->getBufferSize()); + +// settings.enable_resampling.store(false); // Bypass resampler - BROKEN + + settings.drumkit_file.store(kitfile); + settings.midimap_file.store(midimap); + + printf("Loading drumkit, this may take a while:\n"); + + while(settings.drumkit_load_status.load() != LoadStatus::Done) + { + usleep(10000); + + int total = settings.number_of_files.load(); + int loaded = settings.number_of_files_loaded.load(); + + printf("\r%d of %d ", loaded, total); + fflush(stdout); + + if(settings.drumkit_load_status.load() == LoadStatus::Error) + { + printf("\nFailed to load \"%s\".\n", kitfile.c_str()); + return 1; + } + + if(loaded == total) + { + //break; + } + } + printf("\ndone\n"); + + drumgizmo.setSamplerate(oe->getSamplerate()); + + + if(!drumgizmo.init()) + { + printf("Failed init engine.\n"); + return 1; + } + + drumgizmo.run(-1); + + return 0; +} diff --git a/test/dgreftest/midiinputengine.cc b/test/dgreftest/midiinputengine.cc new file mode 100644 index 0000000..bc0fa87 --- /dev/null +++ b/test/dgreftest/midiinputengine.cc @@ -0,0 +1,192 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * midiinputengine.cc + * + * Sat May 14 13:26:23 CEST 2016 + * Copyright 2016 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * DrumGizmo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with DrumGizmo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "midiinputengine.h" + +#include + +static int const NOTE_ON = 0x90; + +MidifileInputEngine::MidifileInputEngine() + : AudioInputEngineMidi{} + , smf{nullptr} + , current_event{nullptr} + , file{} + , speed{1.f} + , track{-1} // all tracks + , loop{false} + , offset{0.0} + , samplerate{44100.0} // todo: via ctor arg +{ +} + +MidifileInputEngine::~MidifileInputEngine() +{ + if(smf != nullptr) + { + smf_delete(smf); + } +} + +bool MidifileInputEngine::init(const Instruments& instruments) +{ + if(file == "") + { + std::cerr << "[MidifileInputEngine] Missing midi filename\n"; + return false; + } + //if(midimap_filename == "") + //{ + // std::cerr << "[MidifileInputEngine] Missing midimap filename\n"; + // return false; + //} + smf = smf_load(file.c_str()); + if(smf == nullptr) + { + std::cerr << "[MidifileInputEngine] Failed to load midifile '" << file + << "'\n"; + return false; + } + //if(!loadMidiMap(midimap_filename, instruments)) + //{ + // std::cerr << "[MidifileInputEngine] Failed to parse midimap '" + // << midimap_filename << "'\n"; + // return false; + //} + + return true; +} + +void MidifileInputEngine::setParm(const std::string& parm, const std::string& value) +{ + if(parm == "file") + { + // apply midi input filename + file = value; + } + else if(parm == "speed") + { + // try to apply speed + try + { + speed = std::stof(value); + } + catch(...) + { + std::cerr << "[MidifileInputEngine] Invalid speed " << value + << "\n"; + } + } + //else if(parm == "midimap") + //{ + // // apply midimap filename + // midimap_filename = value; + //} + else if(parm == "loop") + { + // apply looping + loop = true; + } + else + { + std::cerr << "[MidifileInputEngine] Unsupported parameter '" << parm + << "'\n"; + } +} + +bool MidifileInputEngine::start() +{ + return true; +} + +void MidifileInputEngine::stop() +{ +} + +void MidifileInputEngine::pre() +{ +} + +void MidifileInputEngine::run(size_t pos, size_t len, std::vector& events) +{ + assert(events.empty()); + + double current_max_time = (1.0 + pos + len) / (samplerate / speed); + current_max_time -= offset; + + if(!current_event) + { + current_event = smf_get_next_event(smf); + } + + while(current_event && current_event->time_seconds < current_max_time) + { + if(!smf_event_is_metadata(current_event)) + { + if((current_event->midi_buffer_length == 3) && + ((current_event->midi_buffer[0] & NOTE_ON) == NOTE_ON) && + (track == -1 || current_event->track_number == track) && + current_event->midi_buffer[2] > 0) + { + int key = current_event->midi_buffer[1]; + int velocity = current_event->midi_buffer[2]; + + events.emplace_back(); + auto& event = events.back(); + event.type = TYPE_ONSET; + size_t evpos = current_event->time_seconds * (samplerate / speed); + event.offset = evpos - pos; + + int i = mmap.lookup(key); + if(i != -1) + { + event.instrument = i; + event.velocity = velocity / 127.0; + } + } + } + + current_event = smf_get_next_event(smf); + } + + if(!current_event) + { + if(loop) + { + smf_rewind(smf); + offset += current_max_time; + } + else + { + assert(len >= 1); + events.push_back({TYPE_STOP, 0, len-1, 0.f}); + } + } +} + +void MidifileInputEngine::post() +{ +} diff --git a/test/dgreftest/midiinputengine.h b/test/dgreftest/midiinputengine.h new file mode 100644 index 0000000..3740d44 --- /dev/null +++ b/test/dgreftest/midiinputengine.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * midiinputengine.h + * + * Sat May 14 13:26:22 CEST 2016 + * Copyright 2016 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * DrumGizmo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with DrumGizmo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +class MidifileInputEngine + : public AudioInputEngineMidi +{ +public: + MidifileInputEngine(); + ~MidifileInputEngine(); + + // based on AudioInputEngineMidi + bool init(const Instruments& instruments) override; + void setParm(const std::string& parm, const std::string& value) override; + bool start() override; + void stop() override; + void pre() override; + void run(size_t pos, size_t len, std::vector& events) override; + void post() override; + +private: + smf_t* smf; + smf_event_t* current_event; + + std::string midimap_filename; + std::string file; + float speed; + int track; + bool loop; + double offset, samplerate; +}; diff --git a/test/dgreftest/wavfileoutputengine.cc b/test/dgreftest/wavfileoutputengine.cc new file mode 100644 index 0000000..6f971ad --- /dev/null +++ b/test/dgreftest/wavfileoutputengine.cc @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * wavfileoutputengine.cc + * + * Sat May 14 13:26:51 CEST 2016 + * Copyright 2016 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * DrumGizmo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with DrumGizmo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "wavfileoutputengine.h" + +#include + +WavfileOutputEngine::WavfileOutputEngine() + : AudioOutputEngine{} + , info{} + , file{"output"} +{ + info.samplerate = 44100; + info.channels = 1; + info.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; +} + +WavfileOutputEngine::~WavfileOutputEngine() +{ + sf_close(handle); +} + +bool WavfileOutputEngine::init(const Channels& data) +{ + info.channels = data.size(); + + handle = sf_open(file.c_str(), SFM_WRITE, &info); + if(handle == nullptr) + { + std::cerr << "[WavfileOutputEngine] Failed to open " + << file << " for writing.\n"; + return false; + } + + return true; +} + +void WavfileOutputEngine::setParm(const std::string& parm, const std::string& value) +{ + if(parm == "file") + { + // apply output filename prefix + file = value; + } + else if(parm == "srate") + { + // try to apply samplerate + try + { + info.samplerate = std::stoi(value); + } + catch(...) + { + std::cerr << "[WavfileOutputEngine] Invalid samplerate " << value + << "\n"; + } + } + else + { + std::cerr << "[WavfileOutputEngine] Unsupported parameter '" << parm + << "'\n"; + } +} + +bool WavfileOutputEngine::start() +{ + return true; +} + +void WavfileOutputEngine::stop() +{ +} + +void WavfileOutputEngine::pre(size_t nsamples) +{ +} + +void WavfileOutputEngine::run(int ch, sample_t* samples, size_t nsamples) +{ + if(ch >= info.channels) + { + std::cerr << "[WavfileOutputEngine] cannot access channel #" << ch + << " (" << info.channels << " channels available)\n"; + return; + } + + for(std::size_t i = 0; i < nsamples; ++i) + { + buffer[i * info.channels + ch] = samples[i]; + } +} + +void WavfileOutputEngine::post(size_t nsamples) +{ + + sf_writef_float(handle, buffer, nsamples); +} + +size_t WavfileOutputEngine::getSamplerate() const +{ + return info.samplerate; +} diff --git a/test/dgreftest/wavfileoutputengine.h b/test/dgreftest/wavfileoutputengine.h new file mode 100644 index 0000000..6937ed2 --- /dev/null +++ b/test/dgreftest/wavfileoutputengine.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * wavfileoutputengine.h + * + * Sat May 14 13:26:51 CEST 2016 + * Copyright 2016 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * DrumGizmo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with DrumGizmo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#pragma once + +#include +#include + +class WavfileOutputEngine + : public AudioOutputEngine +{ +public: + WavfileOutputEngine(); + ~WavfileOutputEngine(); + + // based on AudioOutputEngine + bool init(const Channels& data) override; + void setParm(const std::string& parm, const std::string& value) override; + bool start() override; + void stop() override; + void pre(size_t nsamples) override; + void run(int ch, sample_t* samples, size_t nsamples) override; + void post(size_t nsamples) override; + size_t getSamplerate() const override; + +private: + SF_INFO info; + SNDFILE* handle; + std::string file; + sample_t buffer[4096 * 16]; +}; -- cgit v1.2.3