summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2016-05-14 20:27:56 +0200
committerBent Bisballe Nyeng <deva@aasimon.org>2016-05-14 20:27:56 +0200
commit36a62ae3403f7cfb02bd8dde43b6b2fa96fe867b (patch)
treec4ea5f2f97e94688bd0ac96dbb198a8f1eb67491
parent7bbc7520ecb2ff4fc020189c1fdfebd7d3bb8e42 (diff)
Make all components use the same Random instance and add seed method on DrumGizmo class. Added dgreftest application for doing reference midifile rendering tests.
-rw-r--r--configure.ac3
-rw-r--r--src/drumgizmo.cc9
-rw-r--r--src/drumgizmo.h3
-rw-r--r--src/drumkitloader.cc6
-rw-r--r--src/drumkitloader.h3
-rw-r--r--src/drumkitparser.cc5
-rw-r--r--src/drumkitparser.h3
-rw-r--r--src/instrument.cc4
-rw-r--r--src/instrument.h6
-rw-r--r--src/powerlist.cc3
-rw-r--r--src/powerlist.h4
-rw-r--r--src/random.cc6
-rw-r--r--src/random.h2
-rw-r--r--src/velocity.cc3
-rw-r--r--src/velocity.h4
-rw-r--r--test/Makefile.am2
-rw-r--r--test/dgreftest/Makefile.am36
-rw-r--r--test/dgreftest/compareoutputengine.cc138
-rw-r--r--test/dgreftest/compareoutputengine.h55
-rw-r--r--test/dgreftest/dgreftest.cc136
-rw-r--r--test/dgreftest/midiinputengine.cc192
-rw-r--r--test/dgreftest/midiinputengine.h64
-rw-r--r--test/dgreftest/wavfileoutputengine.cc125
-rw-r--r--test/dgreftest/wavfileoutputengine.h54
24 files changed, 847 insertions, 19 deletions
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<std::mutex> 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<event_t> 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<level_t, Sample*> 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<PowerListItem> 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,11 +31,15 @@
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 [<lower_bound>, <upper_bound>].
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 <stdlib.h>
-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<Sample*, float> 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 <iostream>
+
+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 <sndfile.h>
+#include <audiooutputengine.h>
+
+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 <drumgizmo.h>
+
+#include <string>
+#include <iostream>
+#include <cassert>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "compareoutputengine.h"
+#include "midiinputengine.h"
+#include "wavfileoutputengine.h"
+
+#include <cpp11fix.h> // required for c++11
+
+std::unique_ptr<AudioOutputEngine> 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<WavfileOutputEngine>();
+ }
+ else
+ {
+ // Ref file exists, do compare.
+ return std::make_unique<CompareOutputEngine>();
+ }
+}
+
+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 <iostream>
+
+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<event_t>& 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 <audioinputenginemidi.h>
+#include <midimapper.h>
+#include <midimapparser.h>
+#include <string>
+#include <vector>
+
+#include <event.h>
+#include <smf.h>
+
+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<event_t>& 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 <iostream>
+
+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 <sndfile.h>
+#include <audiooutputengine.h>
+
+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];
+};