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. --- 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 ++++++++++ 8 files changed, 800 insertions(+) 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 (limited to 'test/dgreftest') 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