summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore8
-rw-r--r--drumgizmo/drumgizmoc.cc2
-rw-r--r--lv2/drumgizmo.ttl47
-rw-r--r--lv2/input_lv2.cc2
-rw-r--r--lv2/lv2.cc69
-rw-r--r--lv2/lv2_instance.h2
-rw-r--r--src/Makefile.am4
-rw-r--r--src/Makefile.am.drumgizmo6
-rw-r--r--src/audiocache.cc276
-rw-r--r--src/audiocache.h113
-rw-r--r--src/audiocacheeventhandler.cc326
-rw-r--r--src/audiocacheeventhandler.h114
-rw-r--r--src/audiocachefile.cc180
-rw-r--r--src/audiocachefile.h98
-rw-r--r--src/audiocacheidmanager.cc124
-rw-r--r--src/audiocacheidmanager.h93
-rw-r--r--src/audiofile.cc277
-rw-r--r--src/audiofile.h72
-rw-r--r--src/drumgizmo.cc1253
-rw-r--r--src/drumgizmo.h71
-rw-r--r--src/drumkitloader.cc230
-rw-r--r--src/drumkitloader.h86
-rw-r--r--src/events.h6
-rw-r--r--src/mutex.cc120
-rw-r--r--src/mutex.h30
-rw-r--r--src/semaphore.cc8
-rw-r--r--test/Makefile.am61
-rw-r--r--test/audiocacheeventhandlertest.cc52
-rw-r--r--test/audiocachefiletest.cc240
-rw-r--r--test/audiocacheidmanagertest.cc101
-rw-r--r--test/audiocachetest.cc180
-rw-r--r--test/engine.cc1
-rw-r--r--test/kit/ride-multi-channel.wavbin0 -> 28591492 bytes
-rw-r--r--test/kit/ride-single-channel.wavbin0 -> 694144 bytes
-rw-r--r--test/lv2.cc67
-rw-r--r--test/lv2_test_host.cc178
-rw-r--r--test/lv2_test_host.h61
-rw-r--r--vst/Makefile.mingw32.in12
-rw-r--r--vst/drumgizmo_vst.cc466
39 files changed, 3538 insertions, 1498 deletions
diff --git a/.gitignore b/.gitignore
index 7f6691a..362c682 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,9 +2,10 @@ Makefile
Makefile.in
aclocal.m4
autom4te.cache/
+compile
config.guess
config.h
-config.h.in
+config.h.in*
config.log
config.status
config.sub
@@ -37,3 +38,8 @@ test/engine
test/gui
test/lv2
test/resource
+drumgizmo-*.tar.gz
+tst
+vst/Makefile.mingw32
+test/*.log
+test/*.trs
diff --git a/drumgizmo/drumgizmoc.cc b/drumgizmo/drumgizmoc.cc
index 1ccc151..10dcda5 100644
--- a/drumgizmo/drumgizmoc.cc
+++ b/drumgizmo/drumgizmoc.cc
@@ -328,6 +328,8 @@ int CliMain::run(int argc, char *argv[])
DrumGizmo gizmo(oe, ie);
+ gizmo.setFrameSize(oe->getBufferSize());
+
if(kitfile == "" || !gizmo.loadkit(kitfile)) {
printf("Failed to load \"%s\".\n", kitfile.c_str());
return 1;
diff --git a/lv2/drumgizmo.ttl b/lv2/drumgizmo.ttl
index 23345f3..6ce3e91 100644
--- a/lv2/drumgizmo.ttl
+++ b/lv2/drumgizmo.ttl
@@ -37,108 +37,119 @@
lv2:optionalFeature <http://lv2plug.in/ns/ext/uri-map> ;
lv2:optionalFeature <http://lv2plug.in/ns/ext/event> ;
lv2:extensionData state:interface ;
-lv2:port [
+ lv2:port [
+ a lv2:InputPort, lv2:ControlPort ;
+ lv2:index 0 ;
+ lv2:symbol "lv2_freewheel" ;
+ lv2:name "Freewheel" ;
+ lv2:default 0.0 ;
+ lv2:minimum 0.0 ;
+ lv2:maximum 1.0 ;
+ lv2:designation <http://lv2plug.in/ns/lv2core#freeWheeling> ;
+ lv2:portProperty lv2:toggled ;
+ lv2:portProperty epp:hasStrictBounds;
+ ] , [
a atom:AtomPort ,
lv2:InputPort;
atom:bufferType atom:Sequence ;
atom:supports <http://lv2plug.in/ns/ext/midi#MidiEvent> ;
- lv2:index 0 ;
+ lv2:index 1 ;
lv2:symbol "control" ;
lv2:name "Control"
] , [
a lv2:AudioPort ,
lv2:OutputPort ;
- lv2:index 1 ;
+ lv2:index 2 ;
lv2:symbol "out1" ;
lv2:name "Out1"
], [
a lv2:AudioPort ,
lv2:OutputPort ;
- lv2:index 2 ;
+ lv2:index 3 ;
lv2:symbol "out2" ;
lv2:name "Out2"
], [
a lv2:AudioPort ,
lv2:OutputPort ;
- lv2:index 3 ;
+ lv2:index 4 ;
lv2:symbol "out3" ;
lv2:name "Out3"
], [
a lv2:AudioPort ,
lv2:OutputPort ;
- lv2:index 4 ;
+ lv2:index 5 ;
lv2:symbol "out4" ;
lv2:name "Out4"
], [
a lv2:AudioPort ,
lv2:OutputPort ;
- lv2:index 5 ;
+ lv2:index 6 ;
lv2:symbol "out5" ;
lv2:name "Out5"
], [
a lv2:AudioPort ,
lv2:OutputPort ;
- lv2:index 6 ;
+ lv2:index 7 ;
lv2:symbol "out6" ;
lv2:name "Out6"
], [
a lv2:AudioPort ,
lv2:OutputPort ;
- lv2:index 7 ;
+ lv2:index 8 ;
lv2:symbol "out7" ;
lv2:name "Out7"
], [
a lv2:AudioPort ,
lv2:OutputPort ;
- lv2:index 8 ;
+ lv2:index 9 ;
lv2:symbol "out8" ;
lv2:name "Out8"
], [
a lv2:AudioPort ,
lv2:OutputPort ;
- lv2:index 9 ;
+ lv2:index 10 ;
lv2:symbol "out9" ;
lv2:name "Out9"
], [
a lv2:AudioPort ,
lv2:OutputPort ;
- lv2:index 10 ;
+ lv2:index 11 ;
lv2:symbol "out10" ;
lv2:name "Out10"
], [
a lv2:AudioPort ,
lv2:OutputPort ;
- lv2:index 11 ;
+ lv2:index 12 ;
lv2:symbol "out11" ;
lv2:name "Out11"
], [
a lv2:AudioPort ,
lv2:OutputPort ;
- lv2:index 12 ;
+ lv2:index 13 ;
lv2:symbol "out12" ;
lv2:name "Out12"
], [
a lv2:AudioPort ,
lv2:OutputPort ;
- lv2:index 13 ;
+ lv2:index 14 ;
lv2:symbol "out13" ;
lv2:name "Out13"
], [
a lv2:AudioPort ,
lv2:OutputPort ;
- lv2:index 14 ;
+ lv2:index 15 ;
lv2:symbol "out14" ;
lv2:name "Out14"
], [
a lv2:AudioPort ,
lv2:OutputPort ;
- lv2:index 15 ;
+ lv2:index 16 ;
lv2:symbol "out15" ;
lv2:name "Out15"
], [
a lv2:AudioPort ,
lv2:OutputPort ;
- lv2:index 16 ;
+ lv2:index 17 ;
lv2:symbol "out16" ;
lv2:name "Out16"
] .
diff --git a/lv2/input_lv2.cc b/lv2/input_lv2.cc
index e70d293..f65cf83 100644
--- a/lv2/input_lv2.cc
+++ b/lv2/input_lv2.cc
@@ -86,7 +86,7 @@ event_t *InputLV2::run(size_t pos, size_t len, size_t *nevents)
if ((data[0] & 0xF0) == 0x80) { // note off
int key = data[1];
-
+ (void)key;
DEBUG(lv2input, "Event (off) key:%d\n", key);
}
diff --git a/lv2/lv2.cc b/lv2/lv2.cc
index d87665d..9722fad 100644
--- a/lv2/lv2.cc
+++ b/lv2/lv2.cc
@@ -35,6 +35,12 @@
#include <hugin.hpp>
+enum {
+ FREE_WHEEL_PORT = 0,
+ MIDI_PORT,
+ AUDIO_PORT_BASE
+};
+
#define DRUMGIZMO_URI "http://drumgizmo.org/lv2"
#define NS_DG DRUMGIZMO_URI "/atom#"
@@ -47,12 +53,11 @@ static DrumGizmo *dg_get_pci(LV2_Handle instance)
return dglv2->dg;
}
-LV2_State_Status
-dg_save(LV2_Handle instance,
- LV2_State_Store_Function store,
- LV2_State_Handle handle,
- uint32_t flags,
- const LV2_Feature *const * features)
+LV2_State_Status dg_save(LV2_Handle instance,
+ LV2_State_Store_Function store,
+ LV2_State_Handle handle,
+ uint32_t flags,
+ const LV2_Feature *const * features)
{
DGLV2 *dglv2 = (DGLV2 *)instance;
@@ -77,12 +82,11 @@ dg_save(LV2_Handle instance,
return LV2_STATE_SUCCESS;
}
-LV2_State_Status
-dg_restore(LV2_Handle instance,
- LV2_State_Retrieve_Function retrieve,
- LV2_State_Handle handle,
- uint32_t flags,
- const LV2_Feature *const * features)
+LV2_State_Status dg_restore(LV2_Handle instance,
+ LV2_State_Retrieve_Function retrieve,
+ LV2_State_Handle handle,
+ uint32_t flags,
+ const LV2_Feature *const * features)
{
DGLV2 *dglv2 = (DGLV2 *)instance;
@@ -127,6 +131,9 @@ LV2_Handle instantiate(const struct _LV2_Descriptor *descriptor,
{
DGLV2 *dglv2 = new DGLV2;
+ dglv2->free_wheel_port = NULL; // Not assigned
+ dglv2->pos = 0; // Start from the beginning
+
dglv2->map = NULL;
for (int i = 0 ; features[i] ; i++) {
if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) {
@@ -148,19 +155,22 @@ LV2_Handle instantiate(const struct _LV2_Descriptor *descriptor,
return (LV2_Handle)dglv2;
}
-void connect_port(LV2_Handle instance,
- uint32_t port,
- void *data_location)
+void connect_port(LV2_Handle instance, uint32_t port, void *data_location)
{
DGLV2 *dglv2 = (DGLV2 *)instance;
- if(port == 0) {// MIDI in
+ if(port == FREE_WHEEL_PORT) {
+ dglv2->free_wheel_port = (float*)data_location;
+ }
+
+ if(port == MIDI_PORT) { // MIDI in
dglv2->in->eventPort = (LV2_Atom_Sequence*)data_location;
- } else {// Audio Port
- if(port - 1 < NUM_OUTPUTS) {
- dglv2->out->outputPorts[port - 1].samples = (sample_t*)data_location;
- dglv2->out->outputPorts[port - 1].size = 0;
- }
+ }
+
+ if(port >= AUDIO_PORT_BASE) { // Audio Port
+ uint32_t audio_port = port - AUDIO_PORT_BASE;
+ dglv2->out->outputPorts[audio_port].samples = (sample_t*)data_location;
+ dglv2->out->outputPorts[audio_port].size = 0;
}
}
@@ -171,20 +181,25 @@ void activate(LV2_Handle instance)
(void)dglv2;
}
-void run(LV2_Handle instance,
- uint32_t sample_count)
+void run(LV2_Handle instance, uint32_t sample_count)
{
- static size_t pos = 0;
DGLV2 *dglv2 = (DGLV2 *)instance;
- dglv2->dg->run(pos, dglv2->buffer, sample_count);
+ if(dglv2->free_wheel_port) {
+ dglv2->dg->setFreeWheel(*dglv2->free_wheel_port);
+ }
+
+ if(dglv2->buffer_size != sample_count) {
+ dglv2->buffer_size = sample_count;
+ dglv2->dg->setFrameSize(sample_count);
+ }
+ dglv2->dg->run(dglv2->pos, dglv2->buffer, sample_count);
- pos += sample_count;
+ dglv2->pos += sample_count;
}
void deactivate(LV2_Handle instance)
{
- // We don't really need to do anything here.
DGLV2 *dglv2 = (DGLV2 *)instance;
dglv2->dg->stop();
}
diff --git a/lv2/lv2_instance.h b/lv2/lv2_instance.h
index e050e22..0ce98bb 100644
--- a/lv2/lv2_instance.h
+++ b/lv2/lv2_instance.h
@@ -43,6 +43,8 @@ typedef struct {
sample_t *buffer;
size_t buffer_size;
LV2_URID_Map* map;
+ float* free_wheel_port;
+ size_t pos;
} DGLV2;
#endif/*__DRUMGIZMO_LV2_INSTANCE_H__*/
diff --git a/src/Makefile.am b/src/Makefile.am
index df9f4ca..cb44909 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -9,6 +9,7 @@ EXTRA_DIST = \
channelmixer.h \
chresampler.h \
configuration.h \
+ cachemanager.h \
configparser.h \
drumgizmo.h \
drumkit.h \
@@ -39,6 +40,7 @@ EXTRA_DIST = \
audioinputenginemidi.cc \
audiooutputengine.cc \
beatmapper.cc \
+ cachemanager.cc \
channel.cc \
channelmixer.cc \
chresampler.cc \
@@ -63,4 +65,4 @@ EXTRA_DIST = \
semaphore.cc \
thread.cc \
velocity.cc \
- versionstr.cc \ No newline at end of file
+ versionstr.cc
diff --git a/src/Makefile.am.drumgizmo b/src/Makefile.am.drumgizmo
index 1a3c857..57b6362 100644
--- a/src/Makefile.am.drumgizmo
+++ b/src/Makefile.am.drumgizmo
@@ -1,4 +1,8 @@
DRUMGIZMO_SOURCES = \
+ $(top_srcdir)/src/audiocachefile.cc \
+ $(top_srcdir)/src/audiocache.cc \
+ $(top_srcdir)/src/audiocacheeventhandler.cc \
+ $(top_srcdir)/src/audiocacheidmanager.cc \
$(top_srcdir)/src/audioinputenginemidi.cc \
$(top_srcdir)/src/audiofile.cc \
$(top_srcdir)/src/channel.cc \
@@ -28,4 +32,4 @@ DRUMGIZMO_SOURCES = \
$(top_srcdir)/src/velocity.cc \
$(top_srcdir)/src/versionstr.cc
-DRUMGIZMO_LIBS = $(ZITA_LIBS) $(SNDFILE_LIBS) $(EXPAT_LIBS) $(SAMPLERATE_LIBS) \ No newline at end of file
+DRUMGIZMO_LIBS = $(ZITA_LIBS) $(SNDFILE_LIBS) $(EXPAT_LIBS) $(SAMPLERATE_LIBS)
diff --git a/src/audiocache.cc b/src/audiocache.cc
new file mode 100644
index 0000000..237a3ff
--- /dev/null
+++ b/src/audiocache.cc
@@ -0,0 +1,276 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * audiocache.cc
+ *
+ * Fri Apr 10 10:39:24 CEST 2015
+ * Copyright 2015 Jonas Suhr Christensen
+ * jsc@umbraculum.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 General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 "audiocache.h"
+
+#include <mutex>
+
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <hugin.hpp>
+
+#include "audiocachefile.h"
+
+#define CHUNKSIZE(x) (x * CHUNK_MULTIPLIER)
+
+AudioCache::~AudioCache()
+{
+ DEBUG(cache, "~AudioCache() pre\n");
+
+ deinit();
+ delete[] nodata;
+
+ DEBUG(cache, "~AudioCache() post\n");
+}
+
+void AudioCache::init(size_t poolsize)
+{
+ setAsyncMode(true);
+
+ id_manager.init(poolsize);
+ event_handler.start();
+}
+
+void AudioCache::deinit()
+{
+ event_handler.stop();
+}
+
+// Invariant: initial_samples_needed < preloaded audio data
+sample_t* AudioCache::open(AudioFile* file, size_t initial_samples_needed,
+ int channel, cacheid_t& id)
+{
+ if(!file->isValid())
+ {
+ // File preload not yet ready - skip this sample.
+ id = CACHE_DUMMYID;
+ assert(nodata);
+ return nodata;
+ }
+
+ // Register a new id for this cache session.
+ id = id_manager.registerID({});
+
+ // If we are out of available ids we get CACHE_DUMMYID
+ if(id == CACHE_DUMMYID)
+ {
+ // Use nodata buffer instead.
+ assert(nodata);
+ return nodata;
+ }
+
+ // Get the cache_t connected with the registered id.
+ cache_t& c = id_manager.getCache(id);
+
+ c.afile = &event_handler.openFile(file->filename);
+ c.channel = channel;
+
+ // Next call to 'next()' will read from this point.
+ c.localpos = initial_samples_needed;
+
+ c.front = nullptr; // This is allocated when needed.
+ c.back = nullptr; // This is allocated when needed.
+
+ // cropped_size is the preload chunk size cropped to sample length.
+ size_t cropped_size = file->preloadedsize - c.localpos;
+ cropped_size /= framesize;
+ cropped_size *= framesize;
+ cropped_size += initial_samples_needed;
+
+ if(file->preloadedsize == file->size)
+ {
+ // We have preloaded the entire file, so use it.
+ cropped_size = file->preloadedsize;
+ }
+
+ c.preloaded_samples = file->data;
+ c.preloaded_samples_size = cropped_size;
+
+ // Next read from disk will read from this point.
+ c.pos = cropped_size;//c.preloaded_samples_size;
+
+ // Only load next buffer if there are more data in the file to be loaded...
+ if(c.pos < file->size)
+ {
+ if(c.back == nullptr)
+ {
+ c.back = new sample_t[CHUNKSIZE(framesize)];
+ }
+
+ event_handler.pushLoadNextEvent(c.afile, c.channel, c.pos,
+ c.back, &c.ready);
+ }
+
+ return c.preloaded_samples; // return preloaded data
+}
+
+sample_t* AudioCache::next(cacheid_t id, size_t& size)
+{
+ size = framesize;
+
+ if(id == CACHE_DUMMYID)
+ {
+ assert(nodata);
+ return nodata;
+ }
+
+ cache_t& c = id_manager.getCache(id);
+
+ if(c.preloaded_samples)
+ {
+
+ // We are playing from memory:
+ if(c.localpos < c.preloaded_samples_size)
+ {
+ sample_t* s = c.preloaded_samples + c.localpos;
+ c.localpos += framesize;
+ return s;
+ }
+
+ c.preloaded_samples = nullptr; // Start using samples from disk.
+
+ }
+ else
+ {
+
+ // We are playing from cache:
+ if(c.localpos < CHUNKSIZE(framesize))
+ {
+ sample_t* s = c.front + c.localpos;
+ c.localpos += framesize;
+ return s;
+ }
+ }
+
+ // Check for buffer underrun
+ if(!c.ready)
+ {
+ // Just return silence.
+ ++number_of_underruns;
+ return nodata;
+ }
+
+ // Swap buffers
+ std::swap(c.front, c.back);
+
+ // Next time we go here we have already read the first frame.
+ c.localpos = framesize;
+
+ c.pos += CHUNKSIZE(framesize);
+
+ // Does the file have remaining unread samples?
+ if(c.pos < c.afile->getSize())
+ {
+ // Do we have a back buffer to read into?
+ if(c.back == nullptr)
+ {
+ c.back = new sample_t[CHUNKSIZE(framesize)];
+ }
+
+ event_handler.pushLoadNextEvent(c.afile, c.channel, c.pos,
+ c.back, &c.ready);
+ }
+
+ // We should always have a front buffer at this point.
+ assert(c.front);
+
+ return c.front;
+}
+
+bool AudioCache::isReady(cacheid_t id)
+{
+ if(id == CACHE_DUMMYID)
+ {
+ return true;
+ }
+
+ cache_t& cache = id_manager.getCache(id);
+ return cache.ready;
+}
+
+void AudioCache::close(cacheid_t id)
+{
+ if(id == CACHE_DUMMYID)
+ {
+ return;
+ }
+
+ event_handler.pushCloseEvent(id);
+}
+
+void AudioCache::setFrameSize(size_t framesize)
+{
+ DEBUG(cache, "%s\n", __PRETTY_FUNCTION__);
+
+ // Make sure the event handler thread is stalled while we set the framesize
+ // state.
+ std::lock_guard<AudioCacheEventHandler> event_handler_lock(event_handler);
+
+ // NOTE: Not threaded...
+ //std::lock_guard<AudioCacheIDManager> id_manager_lock(id_manager);
+
+ if(framesize > this->framesize)
+ {
+ delete[] nodata;
+ nodata = new sample_t[framesize];
+
+ for(size_t i = 0; i < framesize; ++i)
+ {
+ nodata[i] = 0.0f;
+ }
+ }
+
+ this->framesize = framesize;
+
+ event_handler.setChunkSize(CHUNKSIZE(framesize));
+}
+
+size_t AudioCache::frameSize() const
+{
+ return framesize;
+}
+
+void AudioCache::setAsyncMode(bool async)
+{
+ event_handler.setThreaded(async);
+}
+
+bool AudioCache::asyncMode() const
+{
+ return event_handler.getThreaded();
+}
+
+size_t AudioCache::getNumberOfUnderruns() const
+{
+ return number_of_underruns;
+}
+
+void AudioCache::resetNumberOfUnderruns()
+{
+ number_of_underruns = 0;
+}
diff --git a/src/audiocache.h b/src/audiocache.h
new file mode 100644
index 0000000..004fcf8
--- /dev/null
+++ b/src/audiocache.h
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * audiocache.h
+ *
+ * Fri Apr 10 10:39:24 CEST 2015
+ * Copyright 2015 Jonas Suhr Christensen
+ * jsc@umbraculum.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 General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 <string>
+#include <list>
+#include <vector>
+
+#include "audiotypes.h"
+#include "audiofile.h"
+
+#include "audiocachefile.h"
+#include "audiocacheidmanager.h"
+#include "audiocacheeventhandler.h"
+
+#define CHUNK_MULTIPLIER 16
+
+class AudioCache {
+public:
+ AudioCache() = default;
+
+ //! Destroy object and stop thread if needed.
+ ~AudioCache();
+
+ //! Initialise cache manager and allocate needed resources
+ //! This method starts the cache manager thread.
+ //! This method blocks until the thread has been started.
+ //! \param poolsize The maximum number of parellel events supported.
+ void init(size_t poolsize);
+
+ //! Stop thread and clean up resources.
+ //! This method blocks until the thread has stopped.
+ void deinit();
+
+ //! Register new cache entry.
+ //! Prepares an entry in the cache manager for future disk streaming.
+ //! \param file A pointer to the file which is to be streamed from.
+ //! \param initial_samples_needed The number of samples needed in the first
+ //! read that is not nessecarily of framesize. This is the number of samples
+ //! from the input event offset to the end of the frame in which it resides.
+ //! initial_samples_needed <= framesize.
+ //! \param channel The channel to which the cache id will be bound.
+ //! \param [out] new_id The newly created cache id.
+ //! \return A pointer to the first buffer containing the
+ //! 'initial_samples_needed' number of samples.
+ sample_t* open(AudioFile* file, size_t initial_samples_needed, int channel,
+ cacheid_t& new_id);
+
+ //! Get next buffer.
+ //! Returns the next buffer for reading based on cache id.
+ //! This function will (if needed) schedule a new disk read to make sure that
+ //! data is available in the next call to this method.
+ //! \param id The cache id to read from.
+ //! \param [out] size The size of the returned buffer.
+ //! \return A pointer to the buffer.
+ sample_t* next(cacheid_t id, size_t &size);
+
+ //! Returns if the next chunk of the supplied id has been read from disk.
+ bool isReady(cacheid_t id);
+
+ //! Unregister cache entry.
+ //! Close associated file handles and free associated buffers.
+ //! \param id The cache id to close.
+ void close(cacheid_t id);
+
+ //! Set/get internal framesize used when iterating through cache buffers.
+ void setFrameSize(size_t framesize);
+ size_t frameSize() const;
+
+ //! Control/get reader threaded mode.
+ //! True means reading happening threaded, false means all reading done
+ //! synchronious.
+ void setAsyncMode(bool async);
+ bool asyncMode() const;
+
+ //! Return the number of chunks that were read too late.
+ size_t getNumberOfUnderruns() const;
+
+ //! Set underrun counter to 0.
+ void resetNumberOfUnderruns();
+
+private:
+ size_t framesize{0};
+ sample_t *nodata{nullptr};
+ size_t number_of_underruns{0};
+
+ AudioCacheIDManager id_manager;
+ AudioCacheEventHandler event_handler{id_manager};
+};
diff --git a/src/audiocacheeventhandler.cc b/src/audiocacheeventhandler.cc
new file mode 100644
index 0000000..7322785
--- /dev/null
+++ b/src/audiocacheeventhandler.cc
@@ -0,0 +1,326 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * audiocacheeventhandler.cc
+ *
+ * Sun Jan 3 19:57:55 CET 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 General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 "audiocacheeventhandler.h"
+
+#include <assert.h>
+
+#include <hugin.hpp>
+
+#include "audiocachefile.h"
+#include "audiocache.h"
+#include "audiocacheidmanager.h"
+
+enum class EventType {
+ LoadNext,
+ Close,
+};
+
+class CacheEvent {
+public:
+ EventType eventType;
+
+ // For close event:
+ cacheid_t id;
+
+ // For load next event:
+ size_t pos;
+ AudioCacheFile* afile;
+ CacheChannels channels;
+};
+
+AudioCacheEventHandler::AudioCacheEventHandler(AudioCacheIDManager& id_manager)
+ // Hack to be able to forward declare CacheEvent:
+ : eventqueue(new std::list<CacheEvent>())
+ , id_manager(id_manager)
+{
+}
+
+AudioCacheEventHandler::~AudioCacheEventHandler()
+{
+ // Close all ids already enqueued to be closed.
+ clearEvents();
+
+ auto active_ids = id_manager.getActiveIDs();
+ for(auto id : active_ids)
+ {
+ handleCloseCache(id);
+ }
+
+ // Hack to be able to forward declare CacheEvent:
+ delete eventqueue;
+}
+
+void AudioCacheEventHandler::start()
+{
+ if(running)
+ {
+ return;
+ }
+
+ running = true;
+ run();
+ sem_run.wait();
+}
+
+void AudioCacheEventHandler::stop()
+{
+ if(!running)
+ {
+ return;
+ }
+
+ running = false;
+
+ sem.post();
+ wait_stop();
+}
+
+void AudioCacheEventHandler::setThreaded(bool threaded)
+{
+ if(this->threaded == threaded)
+ {
+ return;
+ }
+
+ if(threaded && !running)
+ {
+ start();
+ }
+
+ if(!threaded && running)
+ {
+ stop();
+ }
+
+ this->threaded = threaded;
+}
+
+bool AudioCacheEventHandler::getThreaded() const
+{
+ return threaded;
+}
+
+void AudioCacheEventHandler::lock()
+{
+ mutex.lock();
+}
+
+void AudioCacheEventHandler::unlock()
+{
+ mutex.unlock();
+}
+
+void AudioCacheEventHandler::pushLoadNextEvent(AudioCacheFile* afile,
+ size_t channel,
+ size_t pos, sample_t* buffer,
+ volatile bool* ready)
+{
+ CacheEvent cache_event;
+ cache_event.eventType = EventType::LoadNext;
+ cache_event.pos = pos;
+ cache_event.afile = afile;
+
+ CacheChannel c;
+ c.channel = channel;
+ c.samples = buffer;
+
+ *ready = false;
+ c.ready = ready;
+
+ cache_event.channels.insert(cache_event.channels.end(), c);
+
+ pushEvent(cache_event);
+}
+
+void AudioCacheEventHandler::pushCloseEvent(cacheid_t id)
+{
+ CacheEvent cache_event;
+ cache_event.eventType = EventType::Close;
+ cache_event.id = id;
+
+ pushEvent(cache_event);
+}
+
+void AudioCacheEventHandler::setChunkSize(size_t chunksize)
+{
+ DEBUG(cache, "%s\n", __PRETTY_FUNCTION__);
+
+ // We should already locked when this method is called.
+ //assert(!mutex.try_lock());
+
+ if(this->chunksize == chunksize)
+ {
+ return;
+ }
+
+ DEBUG(cache, "1)\n");
+
+ // Remove all events from event queue.
+ clearEvents();
+
+ DEBUG(cache, "2)\n");
+
+ // Skip all active cacheids and make their buffers point at nodata.
+ id_manager.disableActive();
+
+ DEBUG(cache, "3)\n");
+
+ this->chunksize = chunksize;
+}
+
+size_t AudioCacheEventHandler::chunkSize()
+{
+ return chunksize;
+}
+
+AudioCacheFile& AudioCacheEventHandler::openFile(const std::string& filename)
+{
+ std::lock_guard<std::mutex> lock(mutex);
+ return files.getFile(filename);
+}
+
+void AudioCacheEventHandler::clearEvents()
+{
+ // Iterate all events ignoring load events and handling close events.
+ for(auto& event : *eventqueue)
+ {
+ if(event.eventType == EventType::Close)
+ {
+ handleCloseCache(event.id); // This method does not lock.
+ }
+ }
+
+ eventqueue->clear();
+}
+
+void AudioCacheEventHandler::handleLoadNextEvent(CacheEvent& cache_event)
+{
+ cache_event.afile->readChunk(cache_event.channels, cache_event.pos,
+ chunksize);
+}
+
+void AudioCacheEventHandler::handleCloseEvent(CacheEvent& cache_event)
+{
+ std::lock_guard<std::mutex> lock(mutex);
+ handleCloseCache(cache_event.id);
+}
+
+void AudioCacheEventHandler::handleCloseCache(cacheid_t cacheid)
+{
+ auto& cache = id_manager.getCache(cacheid);
+
+ files.releaseFile(cache.afile->getFilename());
+
+ delete[] cache.front;
+ delete[] cache.back;
+
+ id_manager.releaseID(cacheid);
+}
+
+void AudioCacheEventHandler::handleEvent(CacheEvent& cache_event)
+{
+ switch(cache_event.eventType)
+ {
+ case EventType::LoadNext:
+ handleLoadNextEvent(cache_event);
+ break;
+ case EventType::Close:
+ handleCloseEvent(cache_event);
+ break;
+ }
+}
+
+void AudioCacheEventHandler::thread_main()
+{
+ sem_run.post(); // Signal that the thread has been started
+
+ while(running)
+ {
+ sem.wait();
+
+ mutex.lock();
+ if(eventqueue->empty())
+ {
+ mutex.unlock();
+ continue;
+ }
+
+ CacheEvent cache_event = eventqueue->front();
+ eventqueue->pop_front();
+ mutex.unlock();
+
+ // TODO: Skip event if cache_event.pos < cache.pos
+ //if(!cache_event.active)
+ //{
+ // continue;
+ //}
+
+ handleEvent(cache_event);
+ }
+}
+
+void AudioCacheEventHandler::pushEvent(CacheEvent& cache_event)
+{
+ if(!threaded)
+ {
+ handleEvent(cache_event);
+ return;
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mutex);
+
+ bool found = false;
+
+ if(cache_event.eventType == EventType::LoadNext)
+ {
+ for(auto& queued_event : *eventqueue)
+ {
+ if((queued_event.eventType == EventType::LoadNext) &&
+ (cache_event.afile->getFilename() ==
+ queued_event.afile->getFilename()) &&
+ (cache_event.pos == queued_event.pos))
+ {
+ // Append channel and buffer to the existing event.
+ queued_event.channels.insert(queued_event.channels.end(),
+ cache_event.channels.begin(),
+ cache_event.channels.end());
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if(!found)
+ {
+ // The event was not already on the list, create a new one.
+ eventqueue->push_back(cache_event);
+ }
+ }
+
+ sem.post();
+}
diff --git a/src/audiocacheeventhandler.h b/src/audiocacheeventhandler.h
new file mode 100644
index 0000000..daf7bb9
--- /dev/null
+++ b/src/audiocacheeventhandler.h
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * audiocacheeventhandler.h
+ *
+ * Sun Jan 3 19:57:55 CET 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 General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 <list>
+#include <vector>
+#include <mutex>
+
+#include "thread.h"
+#include "semaphore.h"
+#include "mutex.h"
+
+#include "audiocachefile.h"
+#include "audiocacheidmanager.h"
+
+class CacheEvent;
+
+class AudioCacheEventHandler
+ : protected Thread
+{
+public:
+ AudioCacheEventHandler(AudioCacheIDManager& id_manager);
+ ~AudioCacheEventHandler();
+
+ //! Start event handler thread.
+ //! This method blocks until the thread has actually been started.
+ void start();
+
+ //! Stop event handler thread.
+ //! This method blocks until the thread has actually been stopped.
+ void stop();
+
+ //! Set thread status and start/stop thread accordingly.
+ //! \param threaded Set to true to start thread or false to stop it.
+ void setThreaded(bool threaded);
+
+ //! Get current threaded status.
+ bool getThreaded() const;
+
+ //! Lock thread mutex.
+ //! This methods are supplied to make this class lockable by std::lock_guard
+ void lock();
+
+ //! Unlock thread mutex.
+ //! This methods are supplied to make this class lockable by std::lock_guard
+ void unlock();
+
+ void pushLoadNextEvent(AudioCacheFile* afile, size_t channel,
+ size_t pos, sample_t* buffer,
+ volatile bool* ready);
+ void pushCloseEvent(cacheid_t id);
+
+ void setChunkSize(size_t chunksize);
+ size_t chunkSize();
+
+ AudioCacheFile& openFile(const std::string& filename);
+
+protected:
+ void clearEvents();
+
+ void handleLoadNextEvent(CacheEvent& cache_event);
+
+ //! Lock the mutex and calls handleCloseCache
+ void handleCloseEvent(CacheEvent& cache_event);
+
+ //! Close decrease the file ref and release the cache id.
+ void handleCloseCache(cacheid_t cacheid);
+
+ void handleEvent(CacheEvent& cache_event);
+
+ // From Thread
+ void thread_main() override;
+
+ void pushEvent(CacheEvent& cache_event);
+
+ AudioCacheFiles files;
+
+ std::mutex mutex;
+
+ std::list<CacheEvent>* eventqueue;
+
+ bool threaded{false};
+ Semaphore sem;
+ Semaphore sem_run;
+ bool running{false};
+
+ AudioCacheIDManager& id_manager;
+
+ size_t chunksize{1024};
+};
diff --git a/src/audiocachefile.cc b/src/audiocachefile.cc
new file mode 100644
index 0000000..916ecb7
--- /dev/null
+++ b/src/audiocachefile.cc
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * cacheaudiofile.cc
+ *
+ * Thu Dec 24 12:17:58 CET 2015
+ * Copyright 2015 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 General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 "audiocachefile.h"
+
+#include <assert.h>
+
+#include <hugin.hpp>
+
+#include <cstring>
+
+#include "audiocache.h"
+
+AudioCacheFile::AudioCacheFile(const std::string& filename)
+ : filename(filename)
+{
+ std::memset(&sf_info, 0, sizeof(SF_INFO));
+
+ fh = sf_open(filename.c_str(), SFM_READ, &sf_info);
+ if(!fh)
+ {
+ ERR(audiofile,"SNDFILE Error (%s): %s\n",
+ filename.c_str(), sf_strerror(fh));
+ return;
+ }
+
+ if(sf_info.frames == 0)
+ {
+ printf("sf_info.frames == 0\n");
+ }
+}
+
+AudioCacheFile::~AudioCacheFile()
+{
+ if(fh)
+ {
+ sf_close(fh);
+ fh = nullptr;
+ }
+}
+
+size_t AudioCacheFile::getSize() const
+{
+ return sf_info.frames;
+}
+
+const std::string& AudioCacheFile::getFilename() const
+{
+ return filename;
+}
+
+size_t AudioCacheFile::getChannelCount()
+{
+ return sf_info.channels;
+}
+
+void AudioCacheFile::readChunk(const CacheChannels& channels,
+ size_t pos, size_t num_samples)
+{
+ //assert(fh != nullptr); // File handle must never be nullptr
+ if(!fh)
+ {
+ return;
+ }
+
+ if((int)pos > sf_info.frames)
+ {
+ WARN(cache, "pos (%d) > sf_info.frames (%d)\n",
+ (int)pos, (int)sf_info.frames);
+ return;
+ }
+
+ sf_seek(fh, pos, SEEK_SET);
+
+ size_t size = sf_info.frames - pos;
+ if(size > num_samples)
+ {
+ size = num_samples;
+ }
+
+ static sample_t *read_buffer = nullptr;
+ static size_t read_buffer_size = 0;
+
+ if((size * sf_info.channels) > read_buffer_size)
+ {
+ delete[] read_buffer;
+ read_buffer_size = size * sf_info.channels;
+ read_buffer = new sample_t[read_buffer_size];
+ // TODO: This buffer is never free'd on app shutdown.
+ }
+
+ size_t read_size = sf_readf_float(fh, read_buffer, size);
+ (void)read_size;
+
+ for(auto it = channels.begin(); it != channels.end(); ++it)
+ {
+ size_t channel = it->channel;
+ sample_t *data = it->samples;
+ for (size_t i = 0; i < size; ++i)
+ {
+ data[i] = read_buffer[(i * sf_info.channels) + channel];
+ }
+ }
+
+ for(auto it = channels.begin(); it != channels.end(); ++it)
+ {
+ *(it->ready) = true;
+ }
+}
+
+AudioCacheFile& AudioCacheFiles::getFile(const std::string& filename)
+{
+ std::lock_guard<std::mutex> lock(mutex);
+
+ AudioCacheFile* cacheAudioFile = nullptr;
+
+ auto it = audiofiles.find(filename);
+ if(it == audiofiles.end())
+ {
+ cacheAudioFile = new AudioCacheFile(filename);
+ audiofiles.insert(std::make_pair(filename, cacheAudioFile));
+ }
+ else
+ {
+ cacheAudioFile = it->second;
+ }
+
+ assert(cacheAudioFile);
+
+ // Increase ref count.
+ ++cacheAudioFile->ref;
+
+ return *cacheAudioFile;
+}
+
+void AudioCacheFiles::releaseFile(const std::string& filename)
+{
+ std::lock_guard<std::mutex> lock(mutex);
+
+ auto it = audiofiles.find(filename);
+ if(it == audiofiles.end())
+ {
+ assert(false); // This should never happen!
+ return; // not open
+ }
+
+ auto audiofile = it->second;
+
+ assert(audiofile->ref); // If ref is not > 0 it shouldn't be in the map.
+
+ --audiofile->ref;
+ if(audiofile->ref == 0)
+ {
+ delete audiofile;
+ audiofiles.erase(it);
+ }
+}
diff --git a/src/audiocachefile.h b/src/audiocachefile.h
new file mode 100644
index 0000000..9910563
--- /dev/null
+++ b/src/audiocachefile.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * cacheaudiofile.h
+ *
+ * Thu Dec 24 12:17:58 CET 2015
+ * Copyright 2015 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 General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 <string>
+#include <list>
+#include <map>
+
+#include <mutex>
+#include "mutex.h"
+
+#include <sndfile.h>
+
+#include <audiotypes.h>
+
+//! Channel data container in the cache world.
+class CacheChannel {
+public:
+ size_t channel; //< Channel number
+ sample_t* samples; //< Sample buffer pointer.
+ size_t num_samples; //< Number of samples in the sample buffer
+ volatile bool* ready; //< Is set to tru when the loading is done.
+};
+
+using CacheChannels = std::list<CacheChannel>;
+
+//! This class is used to encapsulate reading from a single file source.
+//! The access is ref counted so that the file is only opened once and closed
+//! when it is no longer required.
+class AudioCacheFile {
+ friend class AudioCacheFiles;
+ friend class TestableAudioCacheFiles;
+public:
+ //! Create file handle for filename.
+ AudioCacheFile(const std::string& filename);
+
+ //! Closes file handle.
+ ~AudioCacheFile();
+
+ //! Get sample count of the file connected with this cache object.
+ size_t getSize() const;
+
+ //! Get filename of the file connected with this cache object.
+ const std::string& getFilename() const;
+
+ //! Get number of channels in the file
+ size_t getChannelCount();
+
+ //! Read audio data from the file into the supplied channel caches.
+ void readChunk(const CacheChannels& channels, size_t pos, size_t num_samples);
+
+private:
+ int ref{0};
+ SNDFILE* fh{nullptr};
+ SF_INFO sf_info;
+ std::string filename;
+};
+
+class AudioCacheFiles {
+public:
+ //! Get the CacheAudioFile object corresponding to filename.
+ //! If it does not exist it will be created.
+ //! It's ref count will be increased.
+ AudioCacheFile& getFile(const std::string& filename);
+
+ //! Release the CacheAudioFile corresponding to filename.
+ //! It's ref count will be decreased.
+ //! If the ref count reaches 0 the object will be deleted.
+ void releaseFile(const std::string& filename);
+
+protected:
+ std::map<std::string, AudioCacheFile*> audiofiles;
+ std::mutex mutex;
+};
diff --git a/src/audiocacheidmanager.cc b/src/audiocacheidmanager.cc
new file mode 100644
index 0000000..a3e16a0
--- /dev/null
+++ b/src/audiocacheidmanager.cc
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * audiocacheidmanager.cc
+ *
+ * Tue Jan 5 10:59:37 CET 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 General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 "audiocacheidmanager.h"
+
+#include <limits>
+#include <assert.h>
+
+AudioCacheIDManager::~AudioCacheIDManager()
+{
+ assert(availableids.size() == id2cache.size()); // All ids should be released.
+}
+
+void AudioCacheIDManager::init(unsigned int capacity)
+{
+ std::lock_guard<std::mutex> guard(mutex);
+
+ id2cache.resize(capacity);
+ availableids.resize(capacity);
+ for(size_t i = 0; i < capacity; ++i)
+ {
+ availableids[i] = i;
+ }
+}
+
+cache_t& AudioCacheIDManager::getCache(cacheid_t id)
+{
+ std::lock_guard<std::mutex> guard(mutex);
+
+ assert(id != CACHE_NOID);
+ assert(id != CACHE_DUMMYID);
+ assert(id >= 0);
+ assert(id < (int)id2cache.size());
+ assert(id2cache[id].id == id);
+
+ return id2cache[id];
+}
+
+cacheid_t AudioCacheIDManager::registerID(const cache_t& cache)
+{
+ std::lock_guard<std::mutex> guard(mutex);
+
+ cacheid_t id = CACHE_NOID;
+
+ if(availableids.empty())
+ {
+ return CACHE_DUMMYID;
+ }
+ else
+ {
+ id = availableids.back();
+ availableids.pop_back();
+ }
+
+ assert(id2cache[id].id == CACHE_NOID); // Make sure it is not already in use
+
+ id2cache[id] = cache;
+ id2cache[id].id = id;
+
+ return id;
+}
+
+void AudioCacheIDManager::releaseID(cacheid_t id)
+{
+ std::lock_guard<std::mutex> guard(mutex);
+
+ assert(id2cache[id].id != CACHE_NOID); // Test if it wasn't already released.
+
+ id2cache[id].id = CACHE_NOID;
+
+ availableids.push_back(id);
+}
+
+void AudioCacheIDManager::disableActive()
+{
+ // Run through all active cache_ts and disable them.
+ for(auto& cache : id2cache)
+ {
+ if(cache.id != CACHE_NOID)
+ {
+ // Force use of nodata in all of the rest of the next() calls:
+ cache.localpos = std::numeric_limits<size_t>::max();
+ cache.ready = false;
+ }
+ }
+}
+
+std::vector<cacheid_t> AudioCacheIDManager::getActiveIDs()
+{
+ std::vector<cacheid_t> active_ids;
+
+ for(auto& cache : id2cache)
+ {
+ if(cache.id != CACHE_NOID)
+ {
+ active_ids.push_back(cache.id);
+ }
+ }
+
+ return active_ids;
+}
diff --git a/src/audiocacheidmanager.h b/src/audiocacheidmanager.h
new file mode 100644
index 0000000..70f7ce1
--- /dev/null
+++ b/src/audiocacheidmanager.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * audiocacheidmanager.h
+ *
+ * Tue Jan 5 10:59:37 CET 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 General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 <stdlib.h>
+
+#include <vector>
+
+#include <audiotypes.h>
+
+#include <mutex>
+#include "mutex.h"
+
+class AudioCacheFile;
+
+#define CACHE_DUMMYID -2
+#define CACHE_NOID -1
+
+typedef int cacheid_t;
+
+typedef struct {
+ cacheid_t id{CACHE_NOID}; //< Current id of this cache_t. CACHE_NOID means not in use.
+
+ AudioCacheFile* afile{nullptr};
+ size_t channel{0};
+ size_t pos{0}; //< File position
+ volatile bool ready{false};
+ sample_t* front{nullptr};
+ sample_t* back{nullptr};
+ size_t localpos{0}; //< Intra buffer (front) position.
+
+ sample_t* preloaded_samples{nullptr}; // nullptr means preload buffer not active.
+ size_t preloaded_samples_size{0};
+} cache_t;
+
+class AudioCacheIDManager {
+ friend class AudioCacheEventHandler;
+public:
+ AudioCacheIDManager() = default;
+ ~AudioCacheIDManager();
+
+ //! Initialise id lists with specified capacity.
+ //! Exceeding this capacity will result in CACHE_DUMMYID on calls to
+ //! registerID.
+ void init(unsigned int capacity);
+
+ //! Get the cache object connected with the specified cacheid.
+ //! Note: The cacheid MUST be active.
+ cache_t& getCache(cacheid_t id);
+
+ //! Reserve a new cache object and return its cacheid.
+ //! The contents of the supplied cache object will be copied to the new
+ //! cache object.
+ cacheid_t registerID(const cache_t& cache);
+
+ //! Release a cache object and its correseponding cacheid.
+ //! After this call the cacheid can no longer be used.
+ void releaseID(cacheid_t id);
+
+protected:
+ // For AudioCacheEventHandler
+ void disableActive();
+ std::vector<cacheid_t> getActiveIDs();
+
+ std::mutex mutex;
+
+ std::vector<cache_t> id2cache;
+ std::vector<cacheid_t> availableids;
+};
diff --git a/src/audiofile.cc b/src/audiofile.cc
index 59e0c14..e9b5976 100644
--- a/src/audiofile.cc
+++ b/src/audiofile.cc
@@ -38,223 +38,116 @@
#include "configuration.h"
-AudioFile::AudioFile(std::string filename, int filechannel)
+AudioFile::AudioFile(const std::string& filename, int filechannel)
{
- is_loaded = false;
- this->filename = filename;
- this->filechannel = filechannel;
+ is_loaded = false;
+ this->filename = filename;
+ this->filechannel = filechannel;
- data = NULL;
- size = 0;
+ data = nullptr;
+ size = 0;
-#ifdef LAZYLOAD
- preloaded_data = NULL;
-#endif/*LAZYLOAD*/
-
- magic = this;
+ magic = this;
}
AudioFile::~AudioFile()
{
- magic = NULL;
- unload();
+ magic = nullptr;
+ unload();
}
bool AudioFile::isValid()
{
- return this == magic;
+ return this == magic;
}
void AudioFile::unload()
{
- // Make sure we don't unload the object while loading it...
- MutexAutolock l(mutex);
+ // Make sure we don't unload the object while loading it...
+ MutexAutolock l(mutex);
- is_loaded = false;
+ is_loaded = false;
-#ifdef LAZYLOAD
- if(data == preloaded_data) {
- delete[] data;
- data = NULL;
- size = 0;
- } else {
- size = 0;
- delete[] data;
- data = NULL;
- delete preloaded_data;
- preloaded_data = NULL;
- }
-#else
- delete[] data;
- data = NULL;
- size = 0;
-#endif/*LAZYLOAD*/
+ delete[] data;
+ data = nullptr;
+ size = 0;
}
#define BUFFER_SIZE 4092
void AudioFile::load(int num_samples)
{
- // Make sure we don't unload the object while loading it...
- MutexAutolock l(mutex);
-
- /*
- Lazy load of drum kits
- init();
- return;
- */
-
- if(data) return;
-
- SF_INFO sf_info;
- SNDFILE *fh = sf_open(filename.c_str(), SFM_READ, &sf_info);
- if(!fh) {
- ERR(audiofile,"SNDFILE Error (%s): %s\n",
- filename.c_str(), sf_strerror(fh));
- return;
- }
-
- size = sf_info.frames;
-
- double ratio = (double)Conf::samplerate / (double)sf_info.samplerate;
-
- if(num_samples != ALL_SAMPLES) {
- // Make sure we read enough samples, even after conversion.
- num_samples /= ratio;
- if((int)size > num_samples) size = num_samples;
- }
-
- sample_t* data = new sample_t[size];
- if(sf_info.channels == 1) {
- size = sf_read_float(fh, data, size);
- }
- else {
- // check filechannel exists
- if(filechannel >= sf_info.channels) {
- filechannel = sf_info.channels - 1;
- }
- sample_t buffer[BUFFER_SIZE];
- int readsize = BUFFER_SIZE / sf_info.channels;
- int totalread = 0;
- int read;
- do {
- read = sf_readf_float(fh, buffer, readsize);
- for (int i = 0; i < read; i++) {
- data[totalread++] = buffer[i * sf_info.channels + filechannel];
- }
- } while(read > 0 && totalread < (int)size);
- // set data size to total bytes read
- size = totalread;
- }
-
- DEBUG(audiofile,"Loaded %d samples %p\n", (int)size, this);
-
- sf_close(fh);
-
- this->data = data;
- is_loaded = true;
-
- //DEBUG(audiofile, "Loading of %s completed.\n", filename.c_str());
+ // Make sure we don't unload the object while loading it...
+ MutexAutolock l(mutex);
+
+ if(data)
+ {
+ return;
+ }
+
+ SF_INFO sf_info;
+ SNDFILE *fh = sf_open(filename.c_str(), SFM_READ, &sf_info);
+ if(!fh)
+ {
+ ERR(audiofile,"SNDFILE Error (%s): %s\n",
+ filename.c_str(), sf_strerror(fh));
+ return;
+ }
+
+ if(num_samples == ALL_SAMPLES)
+ {
+ num_samples = sf_info.frames;
+ }
+
+ size = sf_info.frames;
+ preloadedsize = sf_info.frames;
+
+ if(preloadedsize > (size_t)num_samples)
+ {
+ preloadedsize = num_samples;
+ }
+
+ sample_t* data = new sample_t[preloadedsize];
+ if(sf_info.channels == 1)
+ {
+ preloadedsize = sf_read_float(fh, data, preloadedsize);
+ }
+ else
+ {
+ // check filechannel exists
+ if(filechannel >= sf_info.channels)
+ {
+ filechannel = sf_info.channels - 1;
+ }
+
+ sample_t buffer[BUFFER_SIZE];
+ int readsize = BUFFER_SIZE / sf_info.channels;
+ int totalread = 0;
+ int read;
+
+ do
+ {
+ read = sf_readf_float(fh, buffer, readsize);
+ for(int i = 0; (i < read) && (totalread < num_samples); ++i)
+ {
+ data[totalread++] = buffer[i * sf_info.channels + filechannel];
+ }
+ }
+ while( (read > 0) &&
+ (totalread < (int)preloadedsize) &&
+ (totalread < num_samples) );
+
+ // set data size to total bytes read
+ preloadedsize = totalread;
+ }
+
+ sf_close(fh);
+
+ this->data = data;
+ is_loaded = true;
}
bool AudioFile::isLoaded()
{
- return is_loaded;
-}
-
-#ifdef LAZYLOAD
-#define SIZE 512*4
-void AudioFile::init()
-{
- //DEBUG(audiofile,"Initializing %p\n", this);
- if(data) {
- //DEBUG(audiofile,"\t already initialized\n");
- return;
- }
-
- SF_INFO sf_info;
- SNDFILE *fh = sf_open(filename.c_str(), SFM_READ, &sf_info);
- if(!fh) {
- ERR(audiofile,"SNDFILE Error (%s): %s\n",
- filename.c_str(), sf_strerror(fh));
- return;
- }
-
- int size = SIZE;
-
- sample_t* data = new sample_t[size];
-
- size = sf_read_float(fh, data, size);
-
- //DEBUG(audiofile,"Lazy loaded %d samples\n", size);
- sf_close(fh);
-
- mutex.lock();
- this->data = data;
- this->size = size;
- this->preloaded_data = data;
- this->is_loaded = true;
- mutex.unlock();
-}
-
-void AudioFile::loadNext()
-{
- if(this->data != this->preloaded_data) {
- //DEBUG(audiofile,"Already completely loaded %p\n", this);
- return;
- }
-
- SF_INFO sf_info;
- SNDFILE *fh = sf_open(filename.c_str(), SFM_READ, &sf_info);
- if(!fh) {
- ERR(audiofile,"SNDFILE Error (%s): %s\n",
- filename.c_str(), sf_strerror(fh));
- return;
- }
-
- int r;
-// int size_accum = 0;
- sample_t* data = new sample_t[sf_info.frames];
- memcpy(data, this->preloaded_data, this->size * sizeof(sample_t));
- this->data = data;
- sf_seek(fh, this->size, SEEK_SET);
-// sample_t* data_buf = new sample_t[SIZE];
- while(this->size < sf_info.frames) {
- //DEBUG(audiofile,"Accumulated %d of %llu\n", size_accum, sf_info.frames);
- //if( (r = sf_read_float(fh, data_buf, SIZE)) < 0) {
- if( (r = sf_read_float(fh, &data[this->size], SIZE)) < 0) {
- ERR(audiofile,"Error reading sound file\n");
- break;
- }
- //size_accum += r;
- //memcpy(data+size_accum, data_buf, sizeof(sample_t) * r);
- this->size += r;
- }
- //delete data_buf;
-
- //DEBUG(audiofile,"Finished loading %d samples %p\n", size, this);
- sf_close(fh);
-
- //mutex.lock();
- //this->data = data;
- //this->size = size;
- //mutex.unlock();
-}
-
-void AudioFile::reset()
-{
- //DEBUG(audiofile,"Resetting audio file %p\n", this);
- if(this->data == this->preloaded_data) {
- //DEBUG(audiofile,"\tNot completely loaded - skipping %p\n", this);
- return;
- }
-
- mutex.lock();
- volatile sample_t* old_data = data;
- this->size = SIZE;
- this->data = this->preloaded_data;
- //DEBUG(audiofile,"Deleting data %p\n", this);
- delete old_data;
- mutex.unlock();
+ return is_loaded;
}
-#endif
diff --git a/src/audiofile.h b/src/audiofile.h
index 98bf101..3ca8b97 100644
--- a/src/audiofile.h
+++ b/src/audiofile.h
@@ -24,8 +24,7 @@
* along with DrumGizmo; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-#ifndef __DRUMGIZMO_AUDIOFILE_H__
-#define __DRUMGIZMO_AUDIOFILE_H__
+#pragma once
#include <string>
#include <map>
@@ -36,72 +35,31 @@
#include "mutex.h"
#include "audio.h"
-/*
- Plan for lazy loading of audio (Brainstorming)
- * Encapsulate data array?
- - Speed issues?
- - Other suggestion
- * Trigger on read begin and read done
- - readnext(instrument)?
- * size_t current latest loaded sample
- * run in own thread? threads in drumgizmo??
- - Add soundfile-loader-class which run in its own thread
- * Add pre-loading constant
- * Pointer to pos in audio stream (maybe just last position read)
- * Strategy for how to handle pre-loading of remaining file
- - Is it acceptable only to handle sequential reading of data (no random access)?
-
- Thread A Thread B
-
- :preload constant (user defined)
- :speed modifier constant (in which time must
- sample n be loaded relative to trigger time)
- ---------- ------
- | Loader | <------- Trigger load of InstrumentSample n --------- | DG |
- ---------- ------
- Load (int- right most loaded sample --> If current sample pos loaded
- | --------- | |
- Wave Into --> | SndFile | <----- Read data (directly from array)
- ---------
-*/
-
-//#define LAZYLOAD
-
#define ALL_SAMPLES -1
class AudioFile {
public:
- AudioFile(std::string filename, int filechannel);
- ~AudioFile();
+ AudioFile(const std::string& filename, int filechannel);
+ ~AudioFile();
- void load(int num_samples = ALL_SAMPLES);
- void unload();
+ void load(int num_samples = ALL_SAMPLES);
+ void unload();
- bool isLoaded();
+ bool isLoaded();
- volatile size_t size;
- volatile sample_t *data;
+ volatile size_t size{0}; // Full size of the file
+ volatile size_t preloadedsize{0}; // Number of samples preloaded (in data)
+ sample_t *data{nullptr};
- std::string filename;
+ std::string filename;
-#ifdef LAZYLOAD
-// SF_INFO sf_info;
-// SNDFILE *fh;
-// bool completely_loaded;
- void init();
- void reset();
- void loadNext();
- sample_t* preloaded_data;
-#endif/*LAZYLOAD*/
+ bool isValid();
- bool isValid();
+ Mutex mutex;
- Mutex mutex;
+ int filechannel;
private:
- void *magic;
- volatile bool is_loaded;
- int filechannel;
+ void *magic;
+ volatile bool is_loaded;
};
-
-#endif/*__DRUMGIZMO_AUDIOFILE_H__*/
diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc
index 7ce05ef..a777125 100644
--- a/src/drumgizmo.cc
+++ b/src/drumgizmo.cc
@@ -46,718 +46,711 @@
#include "nolocale.h"
DrumGizmo::DrumGizmo(AudioOutputEngine *o, AudioInputEngine *i)
- : MessageReceiver(MSGRCV_ENGINE),
- loader(), oe(o), ie(i)
+ : MessageReceiver(MSGRCV_ENGINE)
+ , loader()
+ , oe(o)
+ , ie(i)
+ , framesize(0)
+ , freewheel(false)
{
- is_stopping = false;
+ is_stopping = false;
+ audioCache.init(1000); // start thread
}
DrumGizmo::~DrumGizmo()
{
+ audioCache.deinit(); // stop thread
}
bool DrumGizmo::loadkit(std::string file)
{
- if(file == "") return 1;
+ if(file == "")
+ {
+ return 1;
+ }
- DEBUG(drumgizmo, "loadkit(%s)\n", file.c_str());
+ DEBUG(drumgizmo, "loadkit(%s)\n", file.c_str());
- // Remove all queue AudioFiles from loader before we actually delete them.
- loader.skip();
+ // Remove all queue AudioFiles from loader before we actually delete them.
+ loader.skip();
- // Delete all Channels, Instruments, Samples and AudioFiles.
- kit.clear();
+ // Delete all Channels, Instruments, Samples and AudioFiles.
+ kit.clear();
- DrumKitParser parser(file, kit);
- if(parser.parse()) {
- ERR(drumgizmo, "Drumkit parser failed: %s\n", file.c_str());
- return false;
- }
+ DrumKitParser parser(file, kit);
+ if(parser.parse())
+ {
+ ERR(drumgizmo, "Drumkit parser failed: %s\n", file.c_str());
+ return false;
+ }
- loader.loadKit(&kit);
+ loader.loadKit(&kit);
#ifdef WITH_RESAMPLER
- for(int i = 0; i < MAX_NUM_CHANNELS; i++) {
- resampler[i].setup(kit.samplerate(), Conf::samplerate);
- }
+ for(int i = 0; i < MAX_NUM_CHANNELS; ++i)
+ {
+ resampler[i].setup(kit.samplerate(), Conf::samplerate);
+ }
#endif/*WITH_RESAMPLER*/
- DEBUG(loadkit, "loadkit: Success\n");
+ DEBUG(loadkit, "loadkit: Success\n");
- return true;
+ return true;
}
bool DrumGizmo::init()
{
- if(!ie->init(kit.instruments)) return false;
- if(!oe->init(kit.channels)) return false;
+ if(!ie->init(kit.instruments))
+ {
+ return false;
+ }
- return true;
+ if(!oe->init(kit.channels))
+ {
+ return false;
+ }
+
+ return true;
}
void DrumGizmo::handleMessage(Message *msg)
{
- DEBUG(msg, "got message.");
- switch(msg->type()) {
- case Message::LoadDrumKit:
- {
- DEBUG(msg, "got LoadDrumKitMessage message.");
- LoadDrumKitMessage *m = (LoadDrumKitMessage*)msg;
- loadkit(m->drumkitfile);
- //init(true);
- }
- break;
- case Message::LoadMidimap:
- DEBUG(msg, "got LoadMidimapMessage message.");
- if(!ie->isMidiEngine()) break;
- {
- AudioInputEngineMidi *aim = (AudioInputEngineMidi*)ie;
- LoadMidimapMessage *m = (LoadMidimapMessage*)msg;
- bool ret = aim->loadMidiMap(m->midimapfile, kit.instruments);
-
- LoadStatusMessageMidimap *ls = new LoadStatusMessageMidimap();
- ls->success = ret;
- msghandler.sendMessage(MSGRCV_UI, ls);
- }
- break;
- case Message::EngineSettingsMessage:
- {
- bool mmap_loaded = false;
- std::string mmapfile;
- if(ie->isMidiEngine()) {
- AudioInputEngineMidi *aim = (AudioInputEngineMidi*)ie;
- mmapfile = aim->midimapFile();
- mmap_loaded = aim->isValid();
- }
-
- EngineSettingsMessage *msg = new EngineSettingsMessage();
- msg->midimapfile = mmapfile;
- msg->midimap_loaded = mmap_loaded;
- msg->drumkitfile = kit.file();
- msg->drumkit_loaded = loader.isDone();
- msg->enable_velocity_modifier = Conf::enable_velocity_modifier;
- msg->velocity_modifier_falloff = Conf::velocity_modifier_falloff;
- msg->velocity_modifier_weight = Conf::velocity_modifier_weight;
- msg->enable_velocity_randomiser = Conf::enable_velocity_randomiser;
- msg->velocity_randomiser_weight = Conf::velocity_randomiser_weight;
- msghandler.sendMessage(MSGRCV_UI, msg);
- }
- break;
- case Message::ChangeSettingMessage:
- {
- ChangeSettingMessage *ch = (ChangeSettingMessage*)msg;
- switch(ch->name) {
- case ChangeSettingMessage::enable_velocity_modifier:
- Conf::enable_velocity_modifier = ch->value;
- break;
- case ChangeSettingMessage::velocity_modifier_weight:
- Conf::velocity_modifier_weight = ch->value;
- break;
- case ChangeSettingMessage::velocity_modifier_falloff:
- Conf::velocity_modifier_falloff = ch->value;
- break;
- }
- }
- break;
- default:
- break;
- }
+ DEBUG(msg, "got message.");
+ switch(msg->type()) {
+ case Message::LoadDrumKit:
+ {
+ DEBUG(msg, "got LoadDrumKitMessage message.");
+ LoadDrumKitMessage *m = (LoadDrumKitMessage*)msg;
+ loadkit(m->drumkitfile);
+ //init(true);
+ }
+ break;
+ case Message::LoadMidimap:
+ DEBUG(msg, "got LoadMidimapMessage message.");
+ if(!ie->isMidiEngine())
+ {
+ break;
+ }
+ {
+ AudioInputEngineMidi *aim = (AudioInputEngineMidi*)ie;
+ LoadMidimapMessage *m = (LoadMidimapMessage*)msg;
+ bool ret = aim->loadMidiMap(m->midimapfile, kit.instruments);
+
+ LoadStatusMessageMidimap *ls = new LoadStatusMessageMidimap();
+ ls->success = ret;
+ msghandler.sendMessage(MSGRCV_UI, ls);
+ }
+ break;
+ case Message::EngineSettingsMessage:
+ {
+ bool mmap_loaded = false;
+ std::string mmapfile;
+ if(ie->isMidiEngine())
+ {
+ AudioInputEngineMidi *aim = (AudioInputEngineMidi*)ie;
+ mmapfile = aim->midimapFile();
+ mmap_loaded = aim->isValid();
+ }
+
+ EngineSettingsMessage *msg = new EngineSettingsMessage();
+ msg->midimapfile = mmapfile;
+ msg->midimap_loaded = mmap_loaded;
+ msg->drumkitfile = kit.file();
+ msg->drumkit_loaded = loader.isDone();
+ msg->enable_velocity_modifier = Conf::enable_velocity_modifier;
+ msg->velocity_modifier_falloff = Conf::velocity_modifier_falloff;
+ msg->velocity_modifier_weight = Conf::velocity_modifier_weight;
+ msg->enable_velocity_randomiser = Conf::enable_velocity_randomiser;
+ msg->velocity_randomiser_weight = Conf::velocity_randomiser_weight;
+ msghandler.sendMessage(MSGRCV_UI, msg);
+ }
+ break;
+ case Message::ChangeSettingMessage:
+ {
+ ChangeSettingMessage *ch = (ChangeSettingMessage*)msg;
+ switch(ch->name) {
+ case ChangeSettingMessage::enable_velocity_modifier:
+ Conf::enable_velocity_modifier = ch->value;
+ break;
+ case ChangeSettingMessage::velocity_modifier_weight:
+ Conf::velocity_modifier_weight = ch->value;
+ break;
+ case ChangeSettingMessage::velocity_modifier_falloff:
+ Conf::velocity_modifier_falloff = ch->value;
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
}
-bool DrumGizmo::run(size_t pos, sample_t *samples, size_t nsamples)
+void DrumGizmo::setFrameSize(size_t framesize)
{
- // Handle engine messages, at most one in each iteration:
- handleMessages(1);
-
- ie->pre();
- oe->pre(nsamples);
-
- //
- // Read new events
- //
-
- //DEBUG(engine, "Number of active events: %d\n", activeevents[0].size());
-
- size_t nev;
- event_t *evs = ie->run(pos, nsamples, &nev);
-
- for(size_t e = 0; e < nev; e++) {
- if(evs[e].type == TYPE_ONSET) {
- Instrument *i = NULL;
- int d = evs[e].instrument;
- /*
- Instruments::iterator it = kit.instruments.begin();
- while(d-- && it != kit.instruments.end()) {
- i = &(it->second);
- it++;
- }
- */
-
- if(!kit.isValid()) continue;
-
- if(d < (int)kit.instruments.size()) {
- i = kit.instruments[d];
- }
-
- if(i == NULL || !i->isValid()) {
- ERR(drumgizmo, "Missing Instrument %d.\n", evs[e].instrument);
- continue;
- }
-
- if(i->group() != "") {
- // Add event to ramp down all existing events with the same groupname.
- Channels::iterator j = kit.channels.begin();
- while(j != kit.channels.end()) {
- Channel &ch = *j;
- std::list< Event* >::iterator evs = activeevents[ch.num].begin();
- while(evs != activeevents[ch.num].end()) {
- Event *ev = *evs;
- if(ev->type() == Event::sample) {
- EventSample *sev = (EventSample*)ev;
- if(sev->group == i->group() && sev->instrument != i) {
- sev->rampdown = 3000; // Ramp down 3000 samples
- // TODO: This must be configurable at some point...
- // ... perhaps even by instrument (ie. in the xml file)
- sev->ramp_start = sev->rampdown;
- }
- }
- evs++;
- }
- j++;
- }
- }
-
- Sample *s = i->sample(evs[e].velocity, evs[e].offset + pos);
-
- if(s == NULL) {
- ERR(drumgizmo, "Missing Sample.\n");
- continue;
- }
-
- Channels::iterator j = kit.channels.begin();
- while(j != kit.channels.end()) {
- Channel &ch = *j;
- AudioFile *af = s->getAudioFile(&ch);
- if(af) {
- // LAZYLOAD:
- // DEBUG(drumgizmo,"Requesting preparing of audio file\n");
- // loader.prepare(af);
- }
- if(af == NULL || !af->isValid()) {
- //DEBUG(drumgizmo,"Missing AudioFile.\n");
- } else {
- //DEBUG(drumgizmo, "Adding event %d.\n", evs[e].offset);
- Event *evt = new EventSample(ch.num, 1.0, af, i->group(), i);
- evt->offset = (evs[e].offset + pos) * resampler[0].ratio();
- activeevents[ch.num].push_back(evt);
- }
- j++;
- }
- }
-
- if(evs[e].type == TYPE_STOP) {
- is_stopping = true;
- }
-
- if(is_stopping) {
- // Count the number of active events.
- int num_active_events = 0;
- Channels::iterator j = kit.channels.begin();
- while(j != kit.channels.end()) {
- Channel &ch = *j;
- num_active_events += activeevents[ch.num].size();
- j++;
- }
-
- if(num_active_events == 0) {
- // No more active events - now we can stop the engine.
- return false;
- }
- }
-
- }
-
- free(evs);
-
- //
- // Write audio
- //
-#ifdef WITH_RESAMPLER
- if(Conf::enable_resampling == false ||
- resampler[0].ratio() == 1.0) { // No resampling needed
-#endif
- for(size_t c = 0; c < kit.channels.size(); c++) {
- sample_t *buf = samples;
- bool internal = false;
- if(oe->getBuffer(c)) {
- buf = oe->getBuffer(c);
- internal = true;
- }
- if(buf) {
- memset(buf, 0, nsamples * sizeof(sample_t));
-
- getSamples(c, pos, buf, nsamples);
-
- if(!internal) oe->run(c, samples, nsamples);
- }
- }
-#ifdef WITH_RESAMPLER
- } else {
- // Resampling needed
-
- //
- // NOTE: Channels must be processed one buffer at a time on all channels in
- // parallel - NOT all buffers on one channel and then all buffer on the next
- // one since this would mess up the event queue (it would jump back and forth
- // in time)
- //
-
- // Prepare output buffer
- for(size_t c = 0; c < kit.channels.size(); c++) {
- resampler[c].setOutputSamples(resampler_output_buffer[c], nsamples);
- }
-
- // Process channel data
- size_t kitpos = pos * resampler[0].ratio();
- size_t insize = sizeof(resampler_input_buffer[0]) / sizeof(sample_t);
-
- //printf("ratio: %f\n", resampler[c].ratio());
- while(resampler[0].getOutputSampleCount() > 0) {
- for(size_t c = 0; c < kit.channels.size(); c++) {
- if(resampler[c].getInputSampleCount() == 0) {
- sample_t *sin = resampler_input_buffer[c];
- memset(resampler_input_buffer[c], 0,
- sizeof(resampler_input_buffer[c]));
- getSamples(c, kitpos, sin, insize);
-
- resampler[c].setInputSamples(sin, insize);
- }
- resampler[c].process();
- }
- kitpos += insize;
- }
-
- // Write output data to output engine.
- for(size_t c = 0; c < kit.channels.size(); c++) {
- oe->run(c, resampler_output_buffer[c], nsamples);
- }
-
- }
-#endif/*WITH_RESAMPLER*/
-
- ie->post();
- oe->post(nsamples);
-
- pos += nsamples;
+ // If we are resampling override the frame size.
+ if(resampler[0].ratio() != 1)
+ {
+ framesize = RESAMPLER_INPUT_BUFFER;
+ }
+
+ if(this->framesize != framesize)
+ {
+ DEBUG(drumgizmo, "New framesize: %d\n", (int)framesize);
+
+ this->framesize = framesize;
+
+ // Update framesize in drumkitloader and cachemanager:
+ loader.setFrameSize(framesize);
+ audioCache.setFrameSize(framesize);
+ }
+}
- return true;
+void DrumGizmo::setFreeWheel(bool freewheel)
+{
+ // Freewheel = true means that we are bouncing and therefore running faster
+ // than realtime.
+ if(freewheel != this->freewheel)
+ {
+ this->freewheel = freewheel;
+ audioCache.setAsyncMode(!freewheel);
+ }
}
void DrumGizmo::run(int endpos)
{
- size_t pos = 0;
- size_t nsamples = oe->getBufferSize();
- sample_t *samples = (sample_t *)malloc(nsamples * sizeof(sample_t));
+ size_t pos = 0;
+ size_t nsamples = oe->getBufferSize();
+ sample_t *samples = (sample_t *)malloc(nsamples * sizeof(sample_t));
+
+ setFrameSize(oe->getBufferSize());
+
+ ie->start();
+ oe->start();
- ie->start();
- oe->start();
+ while(run(pos, samples, nsamples) == true)
+ {
+ pos += nsamples;
+ if((endpos != -1) && (pos >= (size_t)endpos))
+ {
+ break;
+ }
+ }
+
+ ie->stop();
+ oe->stop();
+
+ free(samples);
+}
+
+bool DrumGizmo::run(size_t pos, sample_t *samples, size_t nsamples)
+{
+ setFrameSize(nsamples);
+
+ // Handle engine messages, at most one in each iteration:
+ handleMessages(1);
+
+ ie->pre();
+ oe->pre(nsamples);
+
+ //
+ // Read new events
+ //
+
+ //DEBUG(engine, "Number of active events: %d\n", activeevents[0].size());
+
+ size_t nev;
+ event_t *evs = ie->run(pos, nsamples, &nev);
+
+ for(size_t e = 0; e < nev; ++e)
+ {
+ if(evs[e].type == TYPE_ONSET)
+ {
+ Instrument *i = nullptr;
+ int d = evs[e].instrument;
+ /*
+ Instruments::iterator it = kit.instruments.begin();
+ while(d-- && it != kit.instruments.end())
+ {
+ i = &(it->second);
+ ++it;
+ }
+ */
+
+ if(!kit.isValid())
+ {
+ continue;
+ }
+
+ if(d < (int)kit.instruments.size())
+ {
+ i = kit.instruments[d];
+ }
+
+ if(i == nullptr || !i->isValid())
+ {
+ ERR(drumgizmo, "Missing Instrument %d.\n", evs[e].instrument);
+ continue;
+ }
+
+ if(i->group() != "")
+ {
+ // Add event to ramp down all existing events with the same groupname.
+ Channels::iterator j = kit.channels.begin();
+ while(j != kit.channels.end())
+ {
+ Channel &ch = *j;
+ std::list< Event* >::iterator evs = activeevents[ch.num].begin();
+ while(evs != activeevents[ch.num].end())
+ {
+ Event *ev = *evs;
+ if(ev->type() == Event::sample)
+ {
+ EventSample *sev = (EventSample*)ev;
+ if(sev->group == i->group() && sev->instrument != i)
+ {
+ sev->rampdown = 3000; // Ramp down 3000 samples
+ // TODO: This must be configurable at some point...
+ // ... perhaps even by instrument (ie. in the xml file)
+ sev->ramp_start = sev->rampdown;
+ }
+ }
+ ++evs;
+ }
+ ++j;
+ }
+ }
+
+ Sample *s = i->sample(evs[e].velocity, evs[e].offset + pos);
+
+ if(s == nullptr)
+ {
+ ERR(drumgizmo, "Missing Sample.\n");
+ continue;
+ }
+
+ Channels::iterator j = kit.channels.begin();
+ while(j != kit.channels.end())
+ {
+ Channel &ch = *j;
+ AudioFile *af = s->getAudioFile(&ch);
+ if(af)
+ {
+ // LAZYLOAD:
+ // DEBUG(drumgizmo,"Requesting preparing of audio file\n");
+ // loader.prepare(af);
+ }
+ if(af == nullptr || !af->isValid())
+ {
+ //DEBUG(drumgizmo,"Missing AudioFile.\n");
+ }
+ else
+ {
+ //DEBUG(drumgizmo, "Adding event %d.\n", evs[e].offset);
+ Event *evt = new EventSample(ch.num, 1.0, af, i->group(), i);
+ evt->offset = (evs[e].offset + pos) * resampler[0].ratio();
+ activeevents[ch.num].push_back(evt);
+ }
+ ++j;
+ }
+ }
+
+ if(evs[e].type == TYPE_STOP)
+ {
+ is_stopping = true;
+ }
+
+ if(is_stopping)
+ {
+ // Count the number of active events.
+ int num_active_events = 0;
+ Channels::iterator j = kit.channels.begin();
+ while(j != kit.channels.end())
+ {
+ Channel &ch = *j;
+ num_active_events += activeevents[ch.num].size();
+ ++j;
+ }
+
+ if(num_active_events == 0)
+ {
+ // No more active events - now we can stop the engine.
+ return false;
+ }
+ }
+
+ }
+
+ free(evs);
+
+ //
+ // Write audio
+ //
+#ifdef WITH_RESAMPLER
+ if((Conf::enable_resampling == false) ||
+ (resampler[0].ratio() == 1.0)) // No resampling needed
+ {
+#endif
+ for(size_t c = 0; c < kit.channels.size(); ++c)
+ {
+ sample_t *buf = samples;
+ bool internal = false;
+ if(oe->getBuffer(c))
+ {
+ buf = oe->getBuffer(c);
+ internal = true;
+ }
+
+ if(buf)
+ {
+ memset(buf, 0, nsamples * sizeof(sample_t));
+
+ getSamples(c, pos, buf, nsamples);
+
+ if(!internal)
+ {
+ oe->run(c, samples, nsamples);
+ }
+ }
+ }
+#ifdef WITH_RESAMPLER
+ }
+ else
+ {
+ // Resampling needed
+
+ //
+ // NOTE: Channels must be processed one buffer at a time on all channels in
+ // parallel - NOT all buffers on one channel and then all buffer on the next
+ // one since this would mess up the event queue (it would jump back and
+ // forth in time)
+ //
+
+ // Prepare output buffer
+ for(size_t c = 0; c < kit.channels.size(); ++c)
+ {
+ resampler[c].setOutputSamples(resampler_output_buffer[c], nsamples);
+ }
+
+ // Process channel data
+ size_t kitpos = pos * resampler[0].ratio();
+ size_t insize = sizeof(resampler_input_buffer[0]) / sizeof(sample_t);
+
+ while(resampler[0].getOutputSampleCount() > 0)
+ {
+ for(size_t c = 0; c < kit.channels.size(); ++c)
+ {
+ if(resampler[c].getInputSampleCount() == 0)
+ {
+ sample_t *sin = resampler_input_buffer[c];
+ memset(resampler_input_buffer[c], 0,
+ sizeof(resampler_input_buffer[c]));
+ getSamples(c, kitpos, sin, insize);
+
+ resampler[c].setInputSamples(sin, insize);
+ }
+ resampler[c].process();
+ }
+ kitpos += insize;
+ }
+
+ // Write output data to output engine.
+ for(size_t c = 0; c < kit.channels.size(); ++c)
+ {
+ oe->run(c, resampler_output_buffer[c], nsamples);
+ }
+
+ }
+#endif/*WITH_RESAMPLER*/
- while(run(pos, samples, nsamples) == true) {
- pos += nsamples;
- if(endpos != -1 && pos >= (size_t)endpos) break;
- }
+ ie->post();
+ oe->post(nsamples);
- ie->stop();
- oe->stop();
+ pos += nsamples;
- free(samples);
+ return true;
}
+#undef SSE // SSE broken for now ... so disable it.
#ifdef SSE
#define N 8
-typedef float vNsf __attribute__ ((vector_size(sizeof(float)*N)));
+typedef float vNsf __attribute__ ((vector_size(sizeof(sample_t)*N)));
#endif/*SSE*/
void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz)
{
- std::list< Event* >::iterator i = activeevents[ch].begin();
- while(i != activeevents[ch].end()) {
- bool removeevent = false;
-
- Event *event = *i;
-
- Event::type_t type = event->type();
- switch(type) {
- case Event::sample:
- {
- EventSample *evt = (EventSample *)event;
- AudioFile *af = evt->file;
-
- if(!af->isLoaded() || !af->isValid() || s == NULL) {
- removeevent = true;
- break;
- }
-
- {
- MutexAutolock l(af->mutex);
-
- size_t n = 0;
- if(evt->offset > (size_t)pos) n = evt->offset - pos;
- size_t end = sz;
- if((evt->t + end - n) > af->size) end = af->size - evt->t + n;
- if(end > sz) end = sz;
-
- if(evt->rampdown == NO_RAMPDOWN) {
+ std::list< Event* >::iterator i = activeevents[ch].begin();
+ for(; i != activeevents[ch].end(); ++i)
+ {
+ bool removeevent = false;
+
+ Event *event = *i;
+
+ Event::type_t type = event->type();
+ switch(type) {
+ case Event::sample:
+ {
+ EventSample *evt = (EventSample *)event;
+ AudioFile *af = evt->file;
+
+ if(!af->isLoaded() || !af->isValid() || (s == nullptr))
+ {
+ removeevent = true;
+ break;
+ }
+
+ // Don't handle event now is is scheduled for a future iteration?
+ if(evt->offset > (pos + sz))
+ {
+ continue;
+ }
+
+ if(evt->cache_id == CACHE_NOID)
+ {
+ size_t initial_chunksize = (pos + sz) - evt->offset;
+ evt->buffer = audioCache.open(af, initial_chunksize,
+ af->filechannel, evt->cache_id);
+ evt->buffer_size = initial_chunksize;
+ }
+
+ {
+ MutexAutolock l(af->mutex);
+
+ size_t n = 0; // default start point is 0.
+
+ // If we are not at offset 0 in current buffer:
+ if(evt->offset > (size_t)pos)
+ {
+ n = evt->offset - pos;
+ }
+
+ size_t end = sz; // default end point is the end of the buffer.
+
+ // Find the end point intra-buffer
+ if((evt->t + end - n) > af->size)
+ {
+ end = af->size - evt->t + n;
+ }
+
+ // This should not be necessary but make absolutely sure that we do
+ // not write over the end of the buffer.
+ if(end > sz)
+ {
+ end = sz;
+ }
+
+ size_t t = 0; // Internal buffer counter
+ if(evt->rampdown == NO_RAMPDOWN)
+ {
+
#ifdef SSE
-// DEBUG(drumgizmo,"%d\n", evt->t); fflush(stdout);
- size_t optend = ((end - n) / N) * N + n;
- for(; n < optend; n += N) {
- *(vNsf*)&(s[n]) += *(vNsf*)&(af->data[evt->t]);
- evt->t += N;
- }
+ size_t optend = ((end - n) / N) * N + n;
+
+ // Force source addr to be 16 byte aligned...
+ // (might skip 1 or 2 samples)
+ while((size_t)&evt->buffer[t] % 16)
+ {
+ ++t;
+ }
+
+ for(; (n < optend) && (t < evt->buffer_size); n += N)
+ {
+ *(vNsf*)&(s[n]) += *(vNsf*)&(evt->buffer[t]);
+ t += N;
+ }
#endif
- for(; n < end; n++) {
- s[n] += af->data[evt->t];
- evt->t++;
- }
- } else { // Ramp down in progress.
- for(; n < end && evt->rampdown; n++) {
- float scale = (float)evt->rampdown/(float)evt->ramp_start;
- s[n] += af->data[evt->t] * scale;
- evt->t++;
- evt->rampdown--;
- }
-
- if(evt->rampdown == 0) {
- removeevent = true; // Down ramp done. Remove event.
- }
- }
-
- if(evt->t >= af->size) {
- removeevent = true;
- }
-
- }
- }
- break;
- }
-
- if(removeevent) {
- delete event;
- i = activeevents[ch].erase(i);
- continue;
- }
- i++;
- }
+ for(; (n < end) && (t < evt->buffer_size); ++n)
+ {
+ s[n] += evt->buffer[t];
+ ++t;
+ }
+ }
+ else
+ { // Ramp down in progress.
+ for(; (n < end) && (t < evt->buffer_size) && evt->rampdown; ++n)
+ {
+ float scale = (float)evt->rampdown/(float)evt->ramp_start;
+ s[n] += evt->buffer[t] * scale;
+ ++t;
+ evt->rampdown--;
+ }
+ }
+
+ // Add internal buffer counter to "global" event counter.
+ evt->t += evt->buffer_size;
+
+ if((evt->t < af->size) && (evt->rampdown != 0))
+ {
+ evt->buffer = audioCache.next(evt->cache_id, evt->buffer_size);
+ }
+ else
+ {
+ removeevent = true;
+ }
+
+ if(removeevent)
+ {
+ audioCache.close(evt->cache_id);
+ }
+ }
+ }
+ break;
+ }
+
+ if(removeevent)
+ {
+ delete event;
+ i = activeevents[ch].erase(i);
+ continue;
+ }
+ }
}
void DrumGizmo::stop()
{
- // engine.stop();
+ // engine.stop();
}
int DrumGizmo::samplerate()
{
- return Conf::samplerate;
+ return Conf::samplerate;
}
void DrumGizmo::setSamplerate(int samplerate)
{
- Conf::samplerate = samplerate;
+ DEBUG(dgeditor, "%s samplerate: %d\n", __PRETTY_FUNCTION__, samplerate);
+ Conf::samplerate = samplerate;
#ifdef WITH_RESAMPLER
- for(int i = 0; i < MAX_NUM_CHANNELS; i++) {
- resampler[i].setup(kit.samplerate(), Conf::samplerate);
- }
+ for(int i = 0; i < MAX_NUM_CHANNELS; ++i)
+ {
+ resampler[i].setup(kit.samplerate(), Conf::samplerate);
+ }
+ if(resampler[0].ratio() != 1)
+ {
+ setFrameSize(RESAMPLER_INPUT_BUFFER);
+ }
#endif/*WITH_RESAMPLER*/
-
}
std::string float2str(float a)
{
- char buf[256];
- snprintf_nol(buf, sizeof(buf) - 1, "%f", a);
- return buf;
+ char buf[256];
+ snprintf_nol(buf, sizeof(buf) - 1, "%f", a);
+ return buf;
}
std::string bool2str(bool a)
{
- return a?"true":"false";
+ return a?"true":"false";
}
float str2float(std::string a)
{
- if(a == "") return 0.0;
- return atof_nol(a.c_str());
+ if(a == "")
+ {
+ return 0.0;
+ }
+
+ return atof_nol(a.c_str());
}
std::string DrumGizmo::configString()
{
- std::string mmapfile;
- if(ie->isMidiEngine()) {
- AudioInputEngineMidi *aim = (AudioInputEngineMidi*)ie;
- mmapfile = aim->midimapFile();
- }
-
- return
- "<config>\n"
- " <value name=\"drumkitfile\">" + kit.file() + "</value>\n"
- " <value name=\"midimapfile\">" + mmapfile + "</value>\n"
- " <value name=\"enable_velocity_modifier\">" +
- bool2str(Conf::enable_velocity_modifier) + "</value>\n"
- " <value name=\"velocity_modifier_falloff\">" +
- float2str(Conf::velocity_modifier_falloff) + "</value>\n"
- " <value name=\"velocity_modifier_weight\">" +
- float2str(Conf::velocity_modifier_weight) + "</value>\n"
- " <value name=\"enable_velocity_randomiser\">" +
- bool2str(Conf::enable_velocity_randomiser) + "</value>\n"
- " <value name=\"velocity_randomiser_weight\">" +
- float2str(Conf::velocity_randomiser_weight) + "</value>\n"
- "</config>";
+ std::string mmapfile;
+ if(ie->isMidiEngine())
+ {
+ AudioInputEngineMidi *aim = (AudioInputEngineMidi*)ie;
+ mmapfile = aim->midimapFile();
+ }
+
+ return
+ "<config>\n"
+ " <value name=\"drumkitfile\">" + kit.file() + "</value>\n"
+ " <value name=\"midimapfile\">" + mmapfile + "</value>\n"
+ " <value name=\"enable_velocity_modifier\">" +
+ bool2str(Conf::enable_velocity_modifier) + "</value>\n"
+ " <value name=\"velocity_modifier_falloff\">" +
+ float2str(Conf::velocity_modifier_falloff) + "</value>\n"
+ " <value name=\"velocity_modifier_weight\">" +
+ float2str(Conf::velocity_modifier_weight) + "</value>\n"
+ " <value name=\"enable_velocity_randomiser\">" +
+ bool2str(Conf::enable_velocity_randomiser) + "</value>\n"
+ " <value name=\"velocity_randomiser_weight\">" +
+ float2str(Conf::velocity_randomiser_weight) + "</value>\n"
+ "</config>";
}
-
bool DrumGizmo::setConfigString(std::string cfg)
{
- DEBUG(config, "Load config: %s\n", cfg.c_str());
-
- std::string dkf;
- ConfigParser p;
- if(p.parse(cfg)) {
- ERR(drumgizmo, "Config parse error.\n");
- return false;
- }
-
- if(p.value("enable_velocity_modifier") != "") {
- Conf::enable_velocity_modifier =
- p.value("enable_velocity_modifier") == "true";
- }
-
- if(p.value("velocity_modifier_falloff") != "") {
- Conf::velocity_modifier_falloff =
- str2float(p.value("velocity_modifier_falloff"));
- }
-
- if(p.value("velocity_modifier_weight") != "") {
- Conf::velocity_modifier_weight =
- str2float(p.value("velocity_modifier_weight"));
- }
-
- if(p.value("enable_velocity_randomiser") != "") {
- Conf::enable_velocity_randomiser =
- p.value("enable_velocity_randomiser") == "true";
- }
-
- if(p.value("velocity_randomiser_weight") != "") {
- Conf::velocity_randomiser_weight =
- str2float(p.value("velocity_randomiser_weight"));
- }
-
- if(p.value("enable_resampling") != "") {
- Conf::enable_resampling =
- p.value("enable_resampling") == "true";
- }
-
- std::string newkit = p.value("drumkitfile");
- if(newkit != "" && kit.file() != newkit) {
- /*
- if(!loadkit(p.values["drumkitfile"])) return false;
- init(true);
- */
- LoadDrumKitMessage *msg = new LoadDrumKitMessage();
- msg->drumkitfile = newkit;
- msghandler.sendMessage(MSGRCV_ENGINE, msg);
- }
-
- std::string newmidimap = p.value("midimapfile");
- if(newmidimap != "") {
- //midimapfile = newmidimap;
- LoadMidimapMessage *msg = new LoadMidimapMessage();
- msg->midimapfile = newmidimap;
- msghandler.sendMessage(MSGRCV_ENGINE, msg);
- }
-
- return true;
-}
-
-#ifdef TEST_DRUMGIZMO
-//deps: instrument.cc sample.cc channel.cc audiofile.cc drumkit.cc drumkitparser.cc configuration.cc saxparser.cc instrumentparser.cc path.cc
-//cflags: $(SNDFILE_CFLAGS) $(EXPAT_CFLAGS) -I../include -DSSE -msse -msse2 -msse3
-//libs: $(SNDFILE_LIBS) $(EXPAT_LIBS)
-#include "test.h"
-
-static float f(size_t x)
-{
- return x + 1.0;
+ DEBUG(config, "Load config: %s\n", cfg.c_str());
+
+ std::string dkf;
+ ConfigParser p;
+ if(p.parse(cfg))
+ {
+ ERR(drumgizmo, "Config parse error.\n");
+ return false;
+ }
+
+ if(p.value("enable_velocity_modifier") != "")
+ {
+ Conf::enable_velocity_modifier =
+ p.value("enable_velocity_modifier") == "true";
+ }
+
+ if(p.value("velocity_modifier_falloff") != "")
+ {
+ Conf::velocity_modifier_falloff =
+ str2float(p.value("velocity_modifier_falloff"));
+ }
+
+ if(p.value("velocity_modifier_weight") != "")
+ {
+ Conf::velocity_modifier_weight =
+ str2float(p.value("velocity_modifier_weight"));
+ }
+
+ if(p.value("enable_velocity_randomiser") != "")
+ {
+ Conf::enable_velocity_randomiser =
+ p.value("enable_velocity_randomiser") == "true";
+ }
+
+ if(p.value("velocity_randomiser_weight") != "")
+ {
+ Conf::velocity_randomiser_weight =
+ str2float(p.value("velocity_randomiser_weight"));
+ }
+
+ if(p.value("enable_resampling") != "")
+ {
+ Conf::enable_resampling =
+ p.value("enable_resampling") == "true";
+ }
+
+ std::string newkit = p.value("drumkitfile");
+ if(newkit != "" && kit.file() != newkit)
+ {
+ /*
+ if(!loadkit(p.values["drumkitfile"]))
+ {
+ return false;
+ }
+ init(true);
+ */
+ LoadDrumKitMessage *msg = new LoadDrumKitMessage();
+ msg->drumkitfile = newkit;
+ msghandler.sendMessage(MSGRCV_ENGINE, msg);
+ }
+
+ std::string newmidimap = p.value("midimapfile");
+ if(newmidimap != "")
+ {
+ //midimapfile = newmidimap;
+ LoadMidimapMessage *msg = new LoadMidimapMessage();
+ msg->midimapfile = newmidimap;
+ msghandler.sendMessage(MSGRCV_ENGINE, msg);
+ }
+
+ return true;
}
-
-class AITest : public AudioInputEngine {
-public:
- bool init(Instruments &instruments) { return true; }
- void setParm(std::string parm, std::string value) {}
- bool start() { return true; }
- void stop() {}
- void pre() {}
- event_t *run(size_t pos, size_t len, size_t *nevents)
- {
- event_t *e = NULL;
- *nevents = 0;
-
- if(pos <= offset && offset < pos + len) {
- e = new event_t;
-
- e->type = TYPE_ONSET;
- e->instrument = 0;
- e->velocity = 1.0;
- e->offset = offset - pos;
-
- *nevents = 1;
- }
- return e;
- }
- void post() {}
- size_t offset;
-};
-
-class AOTest : public AudioOutputEngine {
-public:
- bool init(Channels channels) { return true; }
- void setParm(std::string parm, std::string value) {}
- bool start() { return true; }
- void stop() {}
- void pre(size_t nsamples) {}
- void run(int ch, sample_t *samples, size_t nsamples)
- {
- }
- void post(size_t nsamples) {}
-};
-
-const char xml_kit[] =
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- "<drumkit name=\"test\" description=\"\">\n"
- " <channels>\n"
- " <channel name=\"ch1\"/>\n"
- " </channels>\n"
- " <instruments>\n"
- " <instrument name=\"instr1\" file=\"instr1.xml\">\n"
- " <channelmap in=\"ch1\" out=\"ch1\"/>\n"
- " </instrument>\n"
- " </instruments>\n"
- "</drumkit>";
-
-const char xml_instr[] =
- "<?xml version='1.0' encoding='UTF-8'?>\n"
- "<instrument name=\"instr1\">\n"
- " <samples>\n"
- " <sample name=\"sample1\">\n"
- " <audiofile channel=\"ch1\" file=\"instr1.wav\"/>\n"
- " </sample>\n"
- " </samples>\n"
- " <velocities>\n"
- " <velocity lower=\"0\" upper=\"1.0\">\n"
- " <sampleref name=\"sample1\"/>\n"
- " </velocity>\n"
- " </velocities>\n"
- "</instrument>";
-
-#define PCM_SIZE 100
-
-void createTestKit()
-{
- FILE *fp;
- fp = fopen("/tmp/kit.xml", "w");
- fwrite(xml_kit, strlen(xml_kit), 1, fp);
- fclose(fp);
-
- fp = fopen("/tmp/instr1.xml", "w");
- fwrite(xml_instr, strlen(xml_instr), 1, fp);
- fclose(fp);
-
- SF_INFO sf_info;
- sf_info.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT;
- sf_info.samplerate = 44100;
- sf_info.channels = 1;
-
- SNDFILE *fh = sf_open("/tmp/instr1.wav", SFM_WRITE, &sf_info);
- if(!fh) {
- printf("Error: %s\n", sf_strerror(fh));
- }
-
- size_t size = PCM_SIZE;
- sample_t samples[size];
-
- for(size_t i = 0; i < size; i++) {
- samples[i] = f(i);//(float)i / (float)size;
- }
-
- sf_write_float(fh, samples, size);
- sf_close(fh);
-}
-
-void deleteTestKit()
-{
- unlink("/tmp/kit.xml");
- unlink("/tmp/instr1.xml");
- unlink("/tmp/instr1.wav");
-}
-
-TEST_BEGIN;
-
-createTestKit();
-
-size_t size = PCM_SIZE;
-//for(size_t chunksz = 1; chunksz < size + 1; chunksz++) {
-size_t chunksz = 16; {
- sample_t samples[chunksz];
-
- for(size_t offset = 0; offset < chunksz + size + 1; offset++) {
- //size_t offset = 5; {
- for(size_t padding = 0; padding < chunksz + size + offset + 1; padding++) {
- //size_t padding = 2; {
- TEST_MSG("Values (offset %d, padding %d, chunksz %d)",
- offset, padding, chunksz);
-
- AOTest ao;
- AITest ai; ai.offset = offset;
- DrumGizmo dg(&ao, &ai);
- dg.loadkit("/tmp/kit.xml");
-
- size_t pos = 0;
- // sample_t samples[chunksz];
- while(pos < offset + size + padding) {
- dg.run(pos, samples, chunksz);
-
- float err = 0;
- size_t errcnt = 0;
- for(size_t i = 0; i < chunksz && pos < offset + size + padding; i++) {
- float val = 0.0;
- if(pos >= offset && pos < (offset + size)) val = f(pos - offset);
- float diff = samples[i] - val;
- /*
- if(diff != 0.0) {
- TEST_EQUAL_FLOAT(samples[i], val,
- "samples[%d] ?= val, pos %d", i, pos);
- }
- */
- if(diff != 0.0) errcnt++;
-
- err += fabs(diff);
- pos++;
- }
-
- TEST_EQUAL_FLOAT(err, 0.0,
- "Compare error (offset %d, padding %d, chunksz %d)",
- offset, padding, chunksz);
- TEST_EQUAL_INT(errcnt, 0,
- "Compare count (offset %d, padding %d, chunksz %d)",
- offset, padding, chunksz);
- }
-
- }
- }
-}
-
-deleteTestKit();
-
-TEST_END;
-
-#endif/*TEST_DRUMGIZMO*/
diff --git a/src/drumgizmo.h b/src/drumgizmo.h
index 5e58ba5..2778092 100644
--- a/src/drumgizmo.h
+++ b/src/drumgizmo.h
@@ -24,8 +24,7 @@
* along with DrumGizmo; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-#ifndef __DRUMGIZMO_DRUMGIZMO_H__
-#define __DRUMGIZMO_DRUMGIZMO_H__
+#pragma once
#include <string>
#include <list>
@@ -38,6 +37,7 @@
#include "drumkit.h"
#include "drumkitloader.h"
+#include "audiocache.h"
#include "mutex.h"
@@ -51,52 +51,57 @@
#define MAX_NUM_CHANNELS 64
#define REFSFILE "refs.conf"
+#define RESAMPLER_INPUT_BUFFER 64
-class DrumGizmo : public MessageReceiver {
+class DrumGizmo
+ : public MessageReceiver
+{
public:
- DrumGizmo(AudioOutputEngine *outputengine, AudioInputEngine *inputengine);
- virtual ~DrumGizmo();
+ DrumGizmo(AudioOutputEngine *outputengine, AudioInputEngine *inputengine);
+ virtual ~DrumGizmo();
- bool loadkit(std::string kitfile);
+ bool loadkit(std::string kitfile);
- bool init();
+ bool init();
- /**
- * @param endpos number of samples to process, -1 := never stop.
- */
- void run(int endpos);
- bool run(size_t pos, sample_t *samples, size_t nsamples);
- void stop();
+ void run(int endpos);
+ bool run(size_t pos, sample_t *samples, size_t nsamples);
+ void stop();
- void getSamples(int ch, int pos, sample_t *s, size_t sz);
+ void getSamples(int ch, int pos, sample_t *s, size_t sz);
- std::string configString();
- bool setConfigString(std::string cfg);
+ std::string configString();
+ bool setConfigString(std::string cfg);
- void handleMessage(Message *msg);
+ void handleMessage(Message *msg);
- int samplerate();
- void setSamplerate(int samplerate);
+ int samplerate();
+ void setSamplerate(int samplerate);
-private:
- DrumKitLoader loader;
+ void setFrameSize(size_t framesize);
- Mutex mutex;
- bool is_stopping; ///< Is set to true when a TYPE_STOP event has been seen.
+ void setFreeWheel(bool freewheel);
- AudioOutputEngine *oe;
- AudioInputEngine *ie;
+protected:
+ DrumKitLoader loader;
- std::list< Event* > activeevents[MAX_NUM_CHANNELS];
+ Mutex mutex;
+ bool is_stopping; ///< Is set to true when a TYPE_STOP event has been seen.
- CHResampler resampler[MAX_NUM_CHANNELS];
- sample_t resampler_output_buffer[MAX_NUM_CHANNELS][4096];
- sample_t resampler_input_buffer[MAX_NUM_CHANNELS][64];
+ AudioOutputEngine *oe;
+ AudioInputEngine *ie;
- std::map<std::string, AudioFile *> audiofiles;
+ std::list< Event* > activeevents[MAX_NUM_CHANNELS];
- DrumKit kit;
-};
+ CHResampler resampler[MAX_NUM_CHANNELS];
+ sample_t resampler_output_buffer[MAX_NUM_CHANNELS][4096];
+ sample_t resampler_input_buffer[MAX_NUM_CHANNELS][RESAMPLER_INPUT_BUFFER];
+
+ std::map<std::string, AudioFile *> audiofiles;
+ AudioCache audioCache;
+ DrumKit kit;
-#endif/*__DRUMGIZMO_DRUMGIZMO_H__*/
+ size_t framesize;
+ bool freewheel;
+};
diff --git a/src/drumkitloader.cc b/src/drumkitloader.cc
index bf01db6..64d6710 100644
--- a/src/drumkitloader.cc
+++ b/src/drumkitloader.cc
@@ -32,123 +32,165 @@
#include "drumgizmo.h"
DrumKitLoader::DrumKitLoader()
- : semaphore("drumkitloader")
+ : semaphore("drumkitloader")
+ , framesize(0)
{
- run();
- run_semaphore.wait(); // Wait for the thread to actually start.
+ run();
+ run_semaphore.wait(); // Wait for the thread to actually start.
}
DrumKitLoader::~DrumKitLoader()
{
- if(running) {
- stop();
- }
+ DEBUG(loader, "~DrumKitLoader() pre\n");
+
+ if(running)
+ {
+ framesize_semaphore.post();
+ stop();
+ }
+
+ DEBUG(loader, "~DrumKitLoader() post\n");
}
void DrumKitLoader::stop()
{
- {
- MutexAutolock l(mutex);
- load_queue.clear();
- }
-
- running = false;
- semaphore.post();
- wait_stop();
+ {
+ MutexAutolock l(mutex);
+ load_queue.clear();
+ }
+
+ running = false;
+ semaphore.post();
+ wait_stop();
}
void DrumKitLoader::skip()
{
- MutexAutolock l(mutex);
- load_queue.clear();
+ MutexAutolock l(mutex);
+ load_queue.clear();
+}
+
+void DrumKitLoader::setFrameSize(size_t framesize)
+{
+ DEBUG(loader, "%s pre\n", __PRETTY_FUNCTION__);
+
+ {
+ MutexAutolock l(mutex);
+ this->framesize = framesize;
+ framesize_semaphore.post(); // Signal that the framesize has been set.
+ }
+
+ DEBUG(loader, "%s post\n", __PRETTY_FUNCTION__);
}
bool DrumKitLoader::isDone()
{
- MutexAutolock l(mutex);
- return load_queue.size() == 0;
+ MutexAutolock l(mutex);
+ return load_queue.size() == 0;
}
void DrumKitLoader::loadKit(DrumKit *kit)
{
- MutexAutolock l(mutex);
-
- DEBUG(loader, "Create AudioFile queue from DrumKit\n");
-
- total_num_audiofiles = 0;// For UI Progress Messages
-
- { // Count total number of files that need loading:
- Instruments::iterator i = kit->instruments.begin();
- while(i != kit->instruments.end()) {
- Instrument *instr = *i;
- total_num_audiofiles += instr->audiofiles.size();
- i++;
- }
- }
-
- fraction = total_num_audiofiles / 200;
- if(fraction == 0) fraction = 1;
-
- { // Now actually queue them for loading:
- Instruments::iterator i = kit->instruments.begin();
- while(i != kit->instruments.end()) {
- Instrument *instr = *i;
-
- std::vector<AudioFile*>::iterator af = instr->audiofiles.begin();
- while(af != instr->audiofiles.end()) {
- AudioFile *audiofile = *af;
- load_queue.push_back(audiofile);
- af++;
- }
-
- i++;
- }
- }
-
- loaded = 0; // For UI Progress Messages
-
- DEBUG(loader, "Queued %d (size: %d) AudioFiles for loading.\n",
- (int)total_num_audiofiles, (int)load_queue.size());
-
- semaphore.post(); // Start loader loop.
+ MutexAutolock l(mutex);
+
+ DEBUG(loader, "Create AudioFile queue from DrumKit\n");
+
+ total_num_audiofiles = 0;// For UI Progress Messages
+
+ { // Count total number of files that need loading:
+ Instruments::iterator i = kit->instruments.begin();
+ while(i != kit->instruments.end())
+ {
+ Instrument *instr = *i;
+ total_num_audiofiles += instr->audiofiles.size();
+ ++i;
+ }
+ }
+
+ fraction = total_num_audiofiles / 200;
+ if(fraction == 0)
+ {
+ fraction = 1;
+ }
+
+ { // Now actually queue them for loading:
+ Instruments::iterator i = kit->instruments.begin();
+ while(i != kit->instruments.end())
+ {
+ Instrument *instr = *i;
+
+ std::vector<AudioFile*>::iterator af = instr->audiofiles.begin();
+ while(af != instr->audiofiles.end())
+ {
+ AudioFile *audiofile = *af;
+ load_queue.push_back(audiofile);
+ af++;
+ }
+
+ ++i;
+ }
+ }
+
+ loaded = 0; // For UI Progress Messages
+
+ DEBUG(loader, "Queued %d (size: %d) AudioFiles for loading.\n",
+ (int)total_num_audiofiles, (int)load_queue.size());
+
+ semaphore.post(); // Start loader loop.
}
void DrumKitLoader::thread_main()
{
- running = true;
-
- run_semaphore.post(); // Signal that the thread has been started.
-
- while(running) {
- size_t size;
- {
- MutexAutolock l(mutex);
- size = load_queue.size();
- }
-
- // Only sleep if queue is empty.
- if(size == 0) semaphore.wait();
-
- std::string filename;
- {
- MutexAutolock l(mutex);
- if(load_queue.size() == 0) continue;
- AudioFile *audiofile = load_queue.front();
- load_queue.pop_front();
- filename = audiofile->filename;
- audiofile->load();
- }
-
- loaded++;
-
- if(loaded % fraction == 0 || loaded == total_num_audiofiles) {
- LoadStatusMessage *ls = new LoadStatusMessage();
- ls->number_of_files = total_num_audiofiles;
- ls->numer_of_files_loaded = loaded;
- ls->current_file = filename;
- msghandler.sendMessage(MSGRCV_UI, ls);
- }
- }
-
- DEBUG(loader, "Loader thread finished.");
+ running = true;
+
+ run_semaphore.post(); // Signal that the thread has been started.
+
+ framesize_semaphore.wait(); // Wait until the framesize has been set.
+
+ while(running)
+ {
+ size_t size;
+ {
+ MutexAutolock l(mutex);
+ size = load_queue.size();
+ }
+
+ // Only sleep if queue is empty.
+ if(size == 0)
+ {
+ semaphore.wait();
+ }
+
+ std::string filename;
+ {
+ MutexAutolock l(mutex);
+ if(load_queue.size() == 0)
+ {
+ continue;
+ }
+ AudioFile *audiofile = load_queue.front();
+ load_queue.pop_front();
+ filename = audiofile->filename;
+ size_t preload_size = framesize * CHUNK_MULTIPLIER + framesize;
+ if(preload_size < 1024)
+ {
+ preload_size = 1024;
+ }
+ (void)preload_size;
+ audiofile->load(ALL_SAMPLES); // Note: Change this to enable diskstreaming
+ }
+
+ loaded++;
+
+ if(loaded % fraction == 0 || loaded == total_num_audiofiles)
+ {
+ LoadStatusMessage *ls = new LoadStatusMessage();
+ ls->number_of_files = total_num_audiofiles;
+ ls->numer_of_files_loaded = loaded;
+ ls->current_file = filename;
+ msghandler.sendMessage(MSGRCV_UI, ls);
+ }
+ }
+
+ DEBUG(loader, "Loader thread finished.");
}
diff --git a/src/drumkitloader.h b/src/drumkitloader.h
index b4a0a69..0532691 100644
--- a/src/drumkitloader.h
+++ b/src/drumkitloader.h
@@ -24,8 +24,7 @@
* along with DrumGizmo; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-#ifndef __DRUMGIZMO_DRUMKITLOADER_H__
-#define __DRUMGIZMO_DRUMKITLOADER_H__
+#pragma once
#include <string>
#include <list>
@@ -36,64 +35,51 @@
#include "drumkit.h"
-/**
- * This class is responsible for loading the drumkits in its own thread.
- * All interaction calls are simply modifying queues and not doing any
- * work in-sync with the caller.
- * This means that if loadKit(...) is called, one cannot assume that the
- * drumkit has actually been loaded when the call returns.
- */
-class DrumKitLoader : public Thread {
+//! This class is responsible for loading the drumkits in its own thread.
+//! All interaction calls are simply modifying queues and not doing any
+//! work in-sync with the caller.
+//! This means that if loadKit(...) is called, one cannot assume that the
+//! drumkit has actually been loaded when the call returns.
+class DrumKitLoader
+ : public Thread
+{
public:
- /**
- * The constrcutor starts the loader thread.
- */
- DrumKitLoader();
+ //! The constrcutor starts the loader thread.
+ DrumKitLoader();
+
+ //! The destructor signals the thread to stop and waits to merge before
+ //! returning (ie. deleting the object will garantuee that the thread has
+ //! been stopped).
+ ~DrumKitLoader();
- /**
- * The destructor signals the thread to stop and waits to merge before
- * returning (ie. deleting the object will garantuee that the thread has
- * been stopped).
- */
- ~DrumKitLoader();
+ //! Signal the loader to start loading all audio files contained in kit.
+ //! All other AudioFiles in queue will be removed before the new ones are
+ //! scheduled.
+ void loadKit(DrumKit *kit);
- /**
- * Signal the loader to start loading all audio files contained in kit.
- * All other AudioFiles in queue will be removed before the new ones are
- * scheduled.
- */
- void loadKit(DrumKit *kit);
-
- // I have no idea what this does..
- //void reset(AudioFile* af);
+ void thread_main();
- void thread_main();
+ //! Simply reports if the load queue is empty (i.e. all AudioFiles has been
+ //! loaded).
+ bool isDone();
- /**
- * Simply reports if the load queue is empty (i.e. all AudioFiles has been
- * loaded).
- */
- bool isDone();
+ //! Signal the loader to stop and wait until it has.
+ void stop();
- /**
- * Signal the loader to stop and wait until it has.
- */
- void stop();
+ //! Skip all queued AudioFiles.
+ void skip();
- /**
- * Skip all queued AudioFiles.
- */
- void skip();
+ void setFrameSize(size_t framesize);
-private:
- Semaphore run_semaphore;
- Semaphore semaphore;
- Mutex mutex;
+protected:
+ Semaphore run_semaphore;
+ Semaphore semaphore;
+ Semaphore framesize_semaphore;
+ Mutex mutex;
volatile bool running{false};
- std::list<AudioFile*> load_queue;
+ std::list<AudioFile*> load_queue;
size_t total_num_audiofiles{0};
size_t fraction{1};
size_t loaded{0};
+ size_t framesize{0};
};
-
-#endif/*__DRUMGIZMO_DRUMKITLOADER_H__*/
diff --git a/src/events.h b/src/events.h
index fa0147b..26eaf3f 100644
--- a/src/events.h
+++ b/src/events.h
@@ -35,6 +35,7 @@
#include "audiofile.h"
#include "audio.h"
#include "mutex.h"
+#include "audiocache.h"
typedef unsigned int timepos_t;
@@ -58,6 +59,7 @@ public:
EventSample(channel_t c, float g, AudioFile *af, std::string grp,
void *instr)
{
+ cache_id = CACHE_NOID;
channel = c;
gain = g;
t = 0;
@@ -70,6 +72,10 @@ public:
Event::type_t type() { return Event::sample; }
+ cacheid_t cache_id;
+ sample_t *buffer;
+ size_t buffer_size;
+
float gain;
unsigned int t;
AudioFile *file;
diff --git a/src/mutex.cc b/src/mutex.cc
index 22d59a6..dfdab33 100644
--- a/src/mutex.cc
+++ b/src/mutex.cc
@@ -27,129 +27,93 @@
*/
#include "mutex.h"
+#include <hugin.hpp>
+
#ifdef WIN32
#include <windows.h>
#else
#include <pthread.h>
+#include <errno.h>
#endif
struct mutex_private_t {
#ifdef WIN32
- HANDLE mutex;
+ HANDLE mutex;
#else
- pthread_mutex_t mutex;
+ pthread_mutex_t mutex;
#endif
};
Mutex::Mutex()
{
- prv = new struct mutex_private_t();
+ prv = new struct mutex_private_t();
#ifdef WIN32
- prv->mutex = CreateMutex(NULL, // default security attributes
- FALSE, // initially not owned
- NULL); // unnamed mutex
+ prv->mutex = CreateMutex(nullptr, // default security attributes
+ FALSE, // initially not owned
+ nullptr); // unnamed mutex
#else
- pthread_mutex_init (&prv->mutex, NULL);
+ pthread_mutex_init (&prv->mutex, nullptr);
#endif
}
Mutex::~Mutex()
{
#ifdef WIN32
- CloseHandle(prv->mutex);
+ CloseHandle(prv->mutex);
#else
- pthread_mutex_destroy(&prv->mutex);
+ pthread_mutex_destroy(&prv->mutex);
#endif
- if(prv) delete prv;
+ if(prv)
+ {
+ delete prv;
+ }
+}
+
+//! \return true if the function succeeds in locking the mutex for the thread.
+//! false otherwise.
+bool Mutex::try_lock()
+{
+#ifdef WIN32
+ DEBUG(mutex, "%s\n", __PRETTY_FUNCTION__);
+
+ DWORD result = WaitForSingleObject(prv->mutex, 0);
+
+ DEBUG(mutex, "WAIT_OBJECT_0: %lu, WAIT_TIMEOUT: %lu, result: %lu\n",
+ WAIT_OBJECT_0, WAIT_TIMEOUT, result);
+
+ return result != WAIT_TIMEOUT;
+#else
+ return pthread_mutex_trylock(&prv->mutex) != EBUSY;
+#endif
}
void Mutex::lock()
{
#ifdef WIN32
- WaitForSingleObject(prv->mutex, // handle to mutex
- INFINITE); // no time-out interval
+ WaitForSingleObject(prv->mutex, // handle to mutex
+ INFINITE); // no time-out interval
#else
- pthread_mutex_lock(&prv->mutex);
+ pthread_mutex_lock(&prv->mutex);
#endif
}
void Mutex::unlock()
{
#ifdef WIN32
- ReleaseMutex(prv->mutex);
+ ReleaseMutex(prv->mutex);
#else
- pthread_mutex_unlock(&prv->mutex);
+ pthread_mutex_unlock(&prv->mutex);
#endif
}
MutexAutolock::MutexAutolock(Mutex &m)
- : mutex(m)
+ : mutex(m)
{
- mutex.lock();
+ mutex.lock();
}
MutexAutolock::~MutexAutolock()
{
- mutex.unlock();
-}
-
-#ifdef TEST_MUTEX
-//deps:
-//cflags: $(PTHREAD_CFLAGS)
-//libs: $(PTHREAD_LIBS)
-#include <test.h>
-
-#include <unistd.h>
-
-volatile int cnt = 0;
-
-static void* thread_run(void *data)
-{
- Mutex *mutex = (Mutex*)data;
- mutex->lock();
- cnt++;
- mutex->unlock();
- return NULL;
+ mutex.unlock();
}
-
-TEST_BEGIN;
-
-Mutex mutex;
-
-mutex.lock();
-TEST_FALSE(mutex.trylock(), "Testing if trylock works negative.");
-mutex.unlock();
-TEST_TRUE(mutex.trylock(), "Testing if trylock works positive.");
-mutex.unlock();
-
-mutex.lock();
-
-pthread_attr_t attr;
-pthread_t tid;
-pthread_attr_init(&attr);
-pthread_create(&tid, &attr, thread_run, &mutex);
-
-sleep(1);
-TEST_EQUAL_INT(cnt, 0, "Testing if lock prevent cnt from increasing.");
-mutex.unlock();
-
-sleep(1);
-TEST_EQUAL_INT(cnt, 1, "Testing if unlock makes cnt increase.");
-
-pthread_join(tid, NULL);
-pthread_attr_destroy(&attr);
-
-{
- TEST_TRUE(mutex.trylock(), "Testing if autolock has not yet locked the mutex.");
- mutex.unlock();
- MutexAutolock mlock(mutex);
- TEST_FALSE(mutex.trylock(), "Testing if autolock worked.");
-}
-
-TEST_TRUE(mutex.trylock(), "Testing if autolock has released the lock on the mutex.");
-mutex.unlock();
-
-TEST_END;
-
-#endif/*TEST_MUTEX*/
diff --git a/src/mutex.h b/src/mutex.h
index 11704d4..4659a07 100644
--- a/src/mutex.h
+++ b/src/mutex.h
@@ -25,31 +25,35 @@
* along with Pracro; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-#ifndef __PRACRO_MUTEX_H__
-#define __PRACRO_MUTEX_H__
+#pragma once
struct mutex_private_t;
class Mutex {
public:
- Mutex();
- ~Mutex();
+ Mutex();
+ ~Mutex();
- bool trylock();
- void lock();
- void unlock();
+ bool try_lock();
+ void lock();
+ void unlock();
private:
- struct mutex_private_t* prv;
+ struct mutex_private_t* prv;
};
+#ifdef WIN32
+// Hack: mingw doesn't have std::mutex
+namespace std {
+ class mutex : public Mutex {};
+}
+#endif
+
class MutexAutolock {
public:
- MutexAutolock(Mutex &mutex);
- ~MutexAutolock();
+ MutexAutolock(Mutex &mutex);
+ ~MutexAutolock();
private:
- Mutex &mutex;
+ Mutex &mutex;
};
-
-#endif/*__PRACRO_MUTEX_H__*/
diff --git a/src/semaphore.cc b/src/semaphore.cc
index 3f5781f..2bd244c 100644
--- a/src/semaphore.cc
+++ b/src/semaphore.cc
@@ -48,7 +48,7 @@ struct semaphore_private_t {
Semaphore::Semaphore(const char *name)
{
this->name = name;
- DEBUG(semaphore, "Create [%s]\n", name);
+ // DEBUG(semaphore, "Create [%s]\n", name);
prv = new struct semaphore_private_t();
@@ -64,7 +64,7 @@ Semaphore::Semaphore(const char *name)
Semaphore::~Semaphore()
{
- DEBUG(semaphore, "Delete [%s]\n", name);
+ // DEBUG(semaphore, "Delete [%s]\n", name);
#ifdef WIN32
CloseHandle(prv->semaphore);
@@ -77,7 +77,7 @@ Semaphore::~Semaphore()
void Semaphore::post()
{
- DEBUG(semaphore, "Post [%s]\n", name);
+ // DEBUG(semaphore, "Post [%s]\n", name);
#ifdef WIN32
ReleaseSemaphore(prv->semaphore, 1, NULL);
@@ -88,7 +88,7 @@ void Semaphore::post()
void Semaphore::wait()
{
- DEBUG(semaphore, "Wait [%s]\n", name);
+ // DEBUG(semaphore, "Wait [%s]\n", name);
#ifdef WIN32
WaitForSingleObject(prv->semaphore, INFINITE);
diff --git a/test/Makefile.am b/test/Makefile.am
index 29ab4b1..8a746a6 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1,7 +1,8 @@
# Rules for the test code (use `make check` to execute)
include $(top_srcdir)/src/Makefile.am.drumgizmo
-TESTS = resource engine gui resampler lv2 configfile
+TESTS = resource engine gui resampler lv2 configfile audiocache \
+ audiocachefile audiocacheidmanager audiocacheeventhandler
check_PROGRAMS = $(TESTS)
@@ -15,9 +16,65 @@ resource_SOURCES = \
test.cc \
resource_test.cc
+audiocache_CXXFLAGS = -DOUTPUT=\"audiocache\" $(CPPUNIT_CFLAGS) \
+ -I$(top_srcdir)/src -I$(top_srcdir)/include \
+ -I$(top_srcdir)/hugin -DDISABLE_HUGIN $(PTHREAD_CFLAGS) $(SNDFILE_CFLAGS)
+audiocache_LDFLAGS = $(PTHREAD_LIBS) $(CPPUNIT_LIBS) $(SNDFILE_LIBS)
+audiocache_SOURCES = \
+ $(top_srcdir)/src/audiocache.cc \
+ $(top_srcdir)/src/audiocacheeventhandler.cc \
+ $(top_srcdir)/src/audiocachefile.cc \
+ $(top_srcdir)/src/audiocacheidmanager.cc \
+ $(top_srcdir)/src/thread.cc \
+ $(top_srcdir)/src/mutex.cc \
+ $(top_srcdir)/src/semaphore.cc \
+ $(top_srcdir)/src/configuration.cc \
+ $(top_srcdir)/src/audiofile.cc \
+ test.cc \
+ audiocachetest.cc
+
+audiocachefile_CXXFLAGS = -DOUTPUT=\"audiocachefile\" $(CPPUNIT_CFLAGS) \
+ -I$(top_srcdir)/src -I$(top_srcdir)/include \
+ -I$(top_srcdir)/hugin -DDISABLE_HUGIN $(PTHREAD_CFLAGS) $(SNDFILE_CFLAGS)
+audiocachefile_LDFLAGS = $(PTHREAD_LIBS) $(CPPUNIT_LIBS) $(SNDFILE_LIBS)
+audiocachefile_SOURCES = \
+ $(top_srcdir)/src/audiocachefile.cc \
+ $(top_srcdir)/src/thread.cc \
+ $(top_srcdir)/src/mutex.cc \
+ $(top_srcdir)/src/semaphore.cc \
+ $(top_srcdir)/src/configuration.cc \
+ $(top_srcdir)/src/audiofile.cc \
+ test.cc \
+ audiocachefiletest.cc
+
+audiocacheidmanager_CXXFLAGS = -DOUTPUT=\"audiocacheidmanager\" \
+ $(CPPUNIT_CFLAGS) \
+ -I$(top_srcdir)/src -I$(top_srcdir)/include \
+ -I$(top_srcdir)/hugin -DDISABLE_HUGIN $(SNDFILE_CFLAGS)
+audiocacheidmanager_LDFLAGS = $(CPPUNIT_LIBS) $(SNDFILE_LIBS)
+audiocacheidmanager_SOURCES = \
+ $(top_srcdir)/src/audiocacheidmanager.cc \
+ test.cc \
+ audiocacheidmanagertest.cc
+
+audiocacheeventhandler_CXXFLAGS = -DOUTPUT=\"audiocacheeventhandler\" \
+ $(CPPUNIT_CFLAGS) \
+ -I$(top_srcdir)/src -I$(top_srcdir)/include \
+ -I$(top_srcdir)/hugin -DDISABLE_HUGIN $(PTHREAD_CFLAGS) $(SNDFILE_CFLAGS)
+audiocacheeventhandler_LDFLAGS = $(PTHREAD_LIBS) $(CPPUNIT_LIBS) $(SNDFILE_LIBS)
+audiocacheeventhandler_SOURCES = \
+ $(top_srcdir)/src/audiocacheeventhandler.cc \
+ $(top_srcdir)/src/audiocacheidmanager.cc \
+ $(top_srcdir)/src/audiocachefile.cc \
+ $(top_srcdir)/src/mutex.cc \
+ $(top_srcdir)/src/thread.cc \
+ $(top_srcdir)/src/semaphore.cc \
+ test.cc \
+ audiocacheeventhandlertest.cc
+
engine_CXXFLAGS = -DOUTPUT=\"engine\" $(CPPUNIT_CFLAGS) \
-I$(top_srcdir)/src -I$(top_srcdir)/include \
- -I$(top_srcdir)/hugin -DDISABLE_HUGIN
+ -I$(top_srcdir)/hugin -DDISABLE_HUGIN $(PTHREAD_CFLAGS)
engine_CFLAGS = -DDISABLE_HUGIN
engine_LDFLAGS = $(CPPUNIT_LIBS) $(DRUMGIZMO_LIBS) $(PTHREAD_LIBS)
engine_SOURCES = \
diff --git a/test/audiocacheeventhandlertest.cc b/test/audiocacheeventhandlertest.cc
new file mode 100644
index 0000000..5f30d2e
--- /dev/null
+++ b/test/audiocacheeventhandlertest.cc
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * audiocacheeventhandlertest.cc
+ *
+ * Thu Jan 7 15:44:14 CET 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 General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 <cppunit/extensions/HelperMacros.h>
+
+#include <audiocacheeventhandler.h>
+
+class AudioCacheEventHandlerTest
+ : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(AudioCacheEventHandlerTest);
+ CPPUNIT_TEST(threadedTest);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void setUp() {}
+ void tearDown() {}
+
+ void threadedTest()
+ {
+ AudioCacheIDManager id_manager;
+ id_manager.init(10);
+
+ AudioCacheEventHandler event_handler(id_manager);
+ }
+};
+
+// Registers the fixture into the 'registry'
+CPPUNIT_TEST_SUITE_REGISTRATION(AudioCacheEventHandlerTest);
diff --git a/test/audiocachefiletest.cc b/test/audiocachefiletest.cc
new file mode 100644
index 0000000..98b7ab5
--- /dev/null
+++ b/test/audiocachefiletest.cc
@@ -0,0 +1,240 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * audiocachefiletest.cc
+ *
+ * Thu Jan 7 15:43:12 CET 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 General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 <cppunit/extensions/HelperMacros.h>
+
+#include <cstring>
+
+#include <audiocachefile.h>
+#include <audiofile.h>
+
+class TestableAudioCacheFiles
+ : public AudioCacheFiles
+{
+public:
+ //CacheAudioFile& getAudioFile(const std::string& filename);
+ //void release(const std::string& filename);
+ int getRef(const std::string& filename)
+ {
+ if(audiofiles.find(filename) == audiofiles.end())
+ {
+ return -1;
+ }
+
+ return audiofiles[filename]->ref;
+ }
+};
+
+class AudioCacheFileTest
+ : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(AudioCacheFileTest);
+ CPPUNIT_TEST(refTest);
+ CPPUNIT_TEST(readTest);
+ CPPUNIT_TEST(noFileTest);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void setUp() {}
+ void tearDown() {}
+
+ void refTest()
+ {
+ TestableAudioCacheFiles audiofiles;
+ std::string filename = "kit/ride-single-channel.wav";
+ CPPUNIT_ASSERT_EQUAL(-1, audiofiles.getRef(filename));
+
+ audiofiles.getFile(filename);
+ CPPUNIT_ASSERT_EQUAL(1, audiofiles.getRef(filename));
+
+ audiofiles.getFile(filename);
+ CPPUNIT_ASSERT_EQUAL(2, audiofiles.getRef(filename));
+
+ audiofiles.releaseFile(filename);
+ CPPUNIT_ASSERT_EQUAL(1, audiofiles.getRef(filename));
+
+ audiofiles.releaseFile(filename);
+ CPPUNIT_ASSERT_EQUAL(-1, audiofiles.getRef(filename));
+ }
+
+ void readTestHelper(size_t buffer_size)
+ {
+ printf("Test buffer size: %d samples\n", (int)buffer_size);
+
+ std::string filename = "kit/ride-multi-channel.wav";
+ AudioFile* ref_file[13];
+ for(size_t c = 0; c < 13; ++c)
+ {
+ ref_file[c] = new AudioFile(filename, c);
+ ref_file[c]->load();
+ }
+
+ AudioCacheFile file(filename);
+ CPPUNIT_ASSERT_EQUAL(filename, file.getFilename());
+ CPPUNIT_ASSERT_EQUAL(13, (int)file.getChannelCount()); // Sanity check
+
+ CacheChannels channels;
+
+ sample_t samples[13][buffer_size];
+ volatile bool ready[13];
+ for(size_t c = 0; c < 13; ++c)
+ {
+ for(size_t i = 0; i < buffer_size; ++i)
+ {
+ samples[c][i] = 42;
+ }
+
+ channels.push_back(
+ {
+ c, // channel
+ samples[c], // samples
+ buffer_size, // max_num_samples
+ &ready[c] // ready
+ }
+ );
+ }
+
+ for(size_t offset = 0; offset < file.getSize(); offset += buffer_size)
+ {
+ for(size_t c = 0; c < 13; ++c)
+ {
+ ready[c] = false;
+ }
+
+ size_t read_size = file.getSize() - offset;
+ if(read_size > buffer_size)
+ {
+ read_size = buffer_size;
+ }
+ else
+ {
+ printf("Last read: %d samples\n", (int)read_size);
+ }
+
+ file.readChunk(channels, offset, read_size);
+
+ for(size_t c = 0; c < 13; ++c)
+ {
+ CPPUNIT_ASSERT_EQUAL(true, ready[c]?true:false);
+ }
+
+ sample_t diff[13] = {0.0};
+ for(size_t c = 0; c < 13; ++c)
+ {
+ for(size_t i = 0; i < read_size; ++i)
+ {
+ diff[c] += abs(ref_file[c]->data[i + offset] - samples[c][i]);
+ }
+ }
+
+ for(int c = 0; c < 13; ++c)
+ {
+ CPPUNIT_ASSERT_EQUAL((sample_t)0.0, diff[c]);
+ }
+ }
+
+ for(size_t c = 0; c < 13; ++c)
+ {
+ delete ref_file[c];
+ }
+ }
+
+ void readTest()
+ {
+ // Exhaustive test for 1...64
+ for(size_t buffer_size = 1; buffer_size < 64; ++buffer_size)
+ {
+ readTestHelper(buffer_size);
+ }
+
+ // Binary test for 64 .. 4096
+ for(size_t buffer_size = 64; buffer_size < 4096; buffer_size *= 2)
+ {
+ readTestHelper(buffer_size);
+ }
+
+ // And some sporadic tests for some "wierd" sizes.
+ for(size_t buffer_size = 65; buffer_size < 4096; buffer_size *= 1.1)
+ {
+ readTestHelper(buffer_size);
+ }
+ }
+
+ void noFileTest()
+ {
+ size_t buffer_size = 64;
+ std::string filename = "kits/no-such-file.wav";
+
+ AudioCacheFile file(filename);
+ CPPUNIT_ASSERT_EQUAL(filename, file.getFilename());
+ CPPUNIT_ASSERT_EQUAL(0u, (unsigned int)file.getSize());
+ CPPUNIT_ASSERT_EQUAL(0u, (unsigned int)file.getChannelCount());
+
+ CacheChannels channels;
+
+ sample_t samples[13][buffer_size];
+ volatile bool ready[13];
+ for(size_t c = 0; c < 13; ++c)
+ {
+ for(size_t i = 0; i < buffer_size; ++i)
+ {
+ samples[c][i] = 42.0f;
+ }
+
+ channels.push_back(
+ {
+ c, // channel
+ samples[c], // samples
+ buffer_size, // max_num_samples
+ &ready[c] // ready
+ }
+ );
+ }
+
+ for(size_t c = 0; c < 13; ++c)
+ {
+ ready[c] = false;
+ }
+
+ file.readChunk(channels, 0, buffer_size);
+
+ for(size_t c = 0; c < 13; ++c)
+ {
+ CPPUNIT_ASSERT_EQUAL(false, ready[c]?true:false);
+ }
+
+ for(size_t c = 0; c < 13; ++c)
+ {
+ for(size_t i = 0; i < buffer_size; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(42.0f, samples[c][i]);
+ }
+ }
+ }
+};
+
+// Registers the fixture into the 'registry'
+CPPUNIT_TEST_SUITE_REGISTRATION(AudioCacheFileTest);
diff --git a/test/audiocacheidmanagertest.cc b/test/audiocacheidmanagertest.cc
new file mode 100644
index 0000000..a9cc878
--- /dev/null
+++ b/test/audiocacheidmanagertest.cc
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * audiocacheidmanagertest.cc
+ *
+ * Thu Jan 7 15:42:31 CET 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 General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 <cppunit/extensions/HelperMacros.h>
+
+#include <audiocacheidmanager.h>
+
+class TestableAudioCacheIDManager
+ : public AudioCacheIDManager
+{
+public:
+ int getAvailableIDs()
+ {
+ return availableids.size();
+ }
+};
+
+class AudioCacheIDManagerTest
+ : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(AudioCacheIDManagerTest);
+ CPPUNIT_TEST(registerReleaseTest);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void setUp() {}
+ void tearDown() {}
+
+ void registerReleaseTest()
+ {
+ TestableAudioCacheIDManager manager;
+ manager.init(2);
+
+ cache_t c1; c1.afile = (AudioCacheFile*)1;
+ auto id1 = manager.registerID(c1);
+ CPPUNIT_ASSERT(id1 != CACHE_DUMMYID);
+ CPPUNIT_ASSERT(id1 != CACHE_NOID);
+ CPPUNIT_ASSERT_EQUAL(1, manager.getAvailableIDs());
+
+ cache_t c2; c2.afile = (AudioCacheFile*)2;
+ auto id2 = manager.registerID(c2);
+ CPPUNIT_ASSERT(id2 != CACHE_DUMMYID);
+ CPPUNIT_ASSERT(id2 != CACHE_NOID);
+ CPPUNIT_ASSERT_EQUAL(0, manager.getAvailableIDs());
+
+ cache_t c3; c3.afile = (AudioCacheFile*)3;
+ auto id3 = manager.registerID(c3);
+ CPPUNIT_ASSERT(id3 == CACHE_DUMMYID);
+ CPPUNIT_ASSERT_EQUAL(0, manager.getAvailableIDs());
+
+ cache_t& tc1 = manager.getCache(id1);
+ CPPUNIT_ASSERT_EQUAL(c1.afile, tc1.afile);
+
+ cache_t& tc2 = manager.getCache(id2);
+ CPPUNIT_ASSERT_EQUAL(c2.afile, tc2.afile);
+
+ manager.releaseID(id1);
+ CPPUNIT_ASSERT_EQUAL(1, manager.getAvailableIDs());
+
+ cache_t c4; c4.afile = (AudioCacheFile*)4;
+ auto id4 = manager.registerID(c4);
+ CPPUNIT_ASSERT(id4 != CACHE_DUMMYID);
+ CPPUNIT_ASSERT(id4 != CACHE_NOID);
+ CPPUNIT_ASSERT_EQUAL(0, manager.getAvailableIDs());
+
+ cache_t& tc4 = manager.getCache(id4);
+ CPPUNIT_ASSERT_EQUAL(c4.afile, tc4.afile);
+
+ manager.releaseID(id2);
+ CPPUNIT_ASSERT_EQUAL(1, manager.getAvailableIDs());
+
+ manager.releaseID(id4);
+ CPPUNIT_ASSERT_EQUAL(2, manager.getAvailableIDs());
+ }
+};
+
+// Registers the fixture into the 'registry'
+CPPUNIT_TEST_SUITE_REGISTRATION(AudioCacheIDManagerTest);
diff --git a/test/audiocachetest.cc b/test/audiocachetest.cc
new file mode 100644
index 0000000..5db5940
--- /dev/null
+++ b/test/audiocachetest.cc
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * cachemanagertest.cc
+ *
+ * Sun Apr 19 10:15:59 CEST 2015
+ * Copyright 2015 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 General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 <cppunit/extensions/HelperMacros.h>
+
+#include <audiofile.h>
+#include <audiocache.h>
+#include <unistd.h>
+
+#define FRAMESIZE 64
+
+class AudioCacheTest
+ : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(AudioCacheTest);
+ CPPUNIT_TEST(singleChannelNonThreaded);
+ CPPUNIT_TEST(singleChannelThreaded);
+ CPPUNIT_TEST(multiChannelNonThreaded);
+ CPPUNIT_TEST(multiChannelThreaded);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void setUp() {}
+ void tearDown() {}
+
+ //! Test runner.
+ //! \param filename The name of the file to read.
+ //! \param channel The channel number to do comparison on.
+ //! \param thread Control if this test is running in threaded mode or not.
+ //! \param framesize The initial framesize to use.
+ void testHelper(const char* filename, int channel, bool threaded,
+ int framesize)
+ {
+ // Reference file:
+ AudioFile audio_file_ref(filename, channel);
+ printf("audio_file_ref.load\n");
+ audio_file_ref.load(ALL_SAMPLES);
+
+ // Input file:
+ AudioFile audio_file(filename, channel);
+ printf("audio_file.load\n");
+ audio_file.load(4096);
+
+ AudioCache audio_cache;
+ printf("audio_cache.init\n");
+ audio_cache.init(100);
+ audio_cache.setAsyncMode(threaded);
+
+ // Set initial (upper limit) framesize
+ audio_cache.setFrameSize(framesize);
+
+ cacheid_t id;
+
+ for(size_t initial_samples_needed = 0;
+ initial_samples_needed < (size_t)(framesize + 1);
+ ++initial_samples_needed)
+ {
+
+ printf("open: initial_samples_needed: %d\n", (int)initial_samples_needed);
+ sample_t *samples =
+ audio_cache.open(&audio_file, initial_samples_needed, channel, id);
+ size_t size = initial_samples_needed;
+ size_t offset = 0;
+
+ // Test pre cache:
+ for(size_t i = 0; i < size; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(audio_file_ref.data[offset], samples[i]);
+ ++offset;
+ }
+
+ // Test the rest
+ while(offset < audio_file_ref.size)
+ {
+ if(threaded)
+ {
+ // Wait until we are finished reading
+ int timeout = 1000;
+ while(!audio_cache.isReady(id))
+ {
+ usleep(1000);
+ if(--timeout == 0)
+ {
+ CPPUNIT_ASSERT(false); // timeout
+ }
+ }
+ }
+
+ samples = audio_cache.next(id, size);
+
+ CPPUNIT_ASSERT_EQUAL(0, (int)audio_cache.getNumberOfUnderruns());
+
+ for(size_t i = 0; (i < size) && (offset < audio_file_ref.size); ++i)
+ {
+ if(audio_file_ref.data[offset] != samples[i])
+ {
+ printf("-----> offset: %d, size: %d, diff: %d,"
+ " i: %d, size: %d, block-diff: %d\n",
+ (int)offset, (int)audio_file_ref.size,
+ (int)(audio_file_ref.size - offset),
+ (int)i, (int)size, (int)(size - i));
+ }
+ CPPUNIT_ASSERT_EQUAL(audio_file_ref.data[offset], samples[i]);
+ ++offset;
+ }
+ }
+
+ audio_cache.close(id);
+ }
+
+ printf("done\n");
+ }
+
+ void singleChannelNonThreaded()
+ {
+ printf("\nsinglechannel_nonthreaded()\n");
+ const char filename[] = "kit/ride-single-channel.wav";
+ int channel = 0;
+ bool threaded = false;
+ testHelper(filename, channel, threaded, FRAMESIZE);
+ }
+
+ void singleChannelThreaded()
+ {
+ printf("\nsinglechannel_threaded()\n");
+ const char filename[] = "kit/ride-single-channel.wav";
+ int channel = 0;
+ bool threaded = true;
+ testHelper(filename, channel, threaded, FRAMESIZE);
+ }
+
+ void multiChannelNonThreaded()
+ {
+ printf("\nmultichannel_nonthreaded()\n");
+ const char filename[] = "kit/ride-multi-channel.wav";
+ int channel = 0;
+ bool threaded = false;
+ testHelper(filename, channel, threaded, FRAMESIZE);
+ ++channel;
+ testHelper(filename, channel, threaded, FRAMESIZE);
+ }
+
+ void multiChannelThreaded()
+ {
+ printf("\nmultichannel_threaded()\n");
+ const char filename[] = "kit/ride-multi-channel.wav";
+ int channel = 0;
+ bool threaded = true;
+ testHelper(filename, channel, threaded, FRAMESIZE);
+ ++channel;
+ testHelper(filename, channel, threaded, FRAMESIZE);
+ }
+
+};
+
+// Registers the fixture into the 'registry'
+CPPUNIT_TEST_SUITE_REGISTRATION(AudioCacheTest);
diff --git a/test/engine.cc b/test/engine.cc
index 69d2a37..4c49a6c 100644
--- a/test/engine.cc
+++ b/test/engine.cc
@@ -43,6 +43,7 @@ public:
AudioOutputEngine *oe = NULL;
AudioInputEngine *ie = NULL;
DrumGizmo dg(oe, ie);
+ dg.setFrameSize(100);
// Switch kits emmidiately with giving the loader time to work:
for(int i = 0; i < 100; i++) {
diff --git a/test/kit/ride-multi-channel.wav b/test/kit/ride-multi-channel.wav
new file mode 100644
index 0000000..3dec8a9
--- /dev/null
+++ b/test/kit/ride-multi-channel.wav
Binary files differ
diff --git a/test/kit/ride-single-channel.wav b/test/kit/ride-single-channel.wav
new file mode 100644
index 0000000..1760697
--- /dev/null
+++ b/test/kit/ride-single-channel.wav
Binary files differ
diff --git a/test/lv2.cc b/test/lv2.cc
index 32d896c..0ecf178 100644
--- a/test/lv2.cc
+++ b/test/lv2.cc
@@ -35,6 +35,12 @@
#define DG_URI "http://drumgizmo.org/lv2"
+enum class Ports {
+ FreeWheel = 0,
+ MidiPort,
+ AudioPortOffset,
+};
+
/**
* Tests that should be performed:
* -------------------------------
@@ -86,7 +92,7 @@ public:
res = h.verify();
CPPUNIT_ASSERT_EQUAL(0, res);
- res = h.createInstance();
+ res = h.createInstance(44100);
CPPUNIT_ASSERT_EQUAL(0, res);
const char config_fmt[] =
@@ -127,7 +133,7 @@ public:
// run for 1 samples to trigger kit loading
res = h.run(1);
CPPUNIT_ASSERT_EQUAL(0, res);
- sleep(1); // wait for kit to get loaded (async),
+ usleep(1000); // wait for kit to get loaded (async),
res = h.run(100);
CPPUNIT_ASSERT_EQUAL(0, res);
@@ -151,7 +157,7 @@ public:
res = h.verify();
CPPUNIT_ASSERT_EQUAL(0, res);
- res = h.createInstance();
+ res = h.createInstance(44100);
CPPUNIT_ASSERT_EQUAL(0, res);
const char config_fmt[] =
@@ -191,15 +197,19 @@ public:
// Port buffers:
char sequence_buffer[4096];
+ bool freeWheel = false;
+
+ // Free wheel port
+ res = h.connectPort((int)Ports::FreeWheel, (void*)&freeWheel);
LV2TestHost::Sequence seq(sequence_buffer, sizeof(sequence_buffer));
- res = h.connectPort(0, seq.data());
+ res = h.connectPort((int)Ports::MidiPort, seq.data());
CPPUNIT_ASSERT_EQUAL(0, res);
// run for 1 samples to trigger kit loading
res = h.run(1);
CPPUNIT_ASSERT_EQUAL(0, res);
- sleep(1); // wait for kit to get loaded (async),
+ usleep(1000); // wait for kit to get loaded (async),
seq.addMidiNote(5, 1, 127);
res = h.run(100);
@@ -224,7 +234,7 @@ public:
res = h.verify();
CPPUNIT_ASSERT_EQUAL(0, res);
- res = h.createInstance();
+ res = h.createInstance(44100);
CPPUNIT_ASSERT_EQUAL(0, res);
const char config_fmt[] =
@@ -265,14 +275,22 @@ public:
// Port buffers:
char sequence_buffer[4096];
float pcm_buffer[16][10];
+ bool freeWheel = true;
+
+ // Free wheel port
+ res = h.connectPort((int)Ports::FreeWheel, (void*)&freeWheel);
LV2TestHost::Sequence seq(sequence_buffer, sizeof(sequence_buffer));
- res = h.connectPort(0, seq.data());
+ res = h.connectPort((int)Ports::MidiPort, seq.data());
CPPUNIT_ASSERT_EQUAL(0, res);
- for(int i = 1; i <= 16; i++) {
- memset(pcm_buffer, 1, sizeof(pcm_buffer));
- res += h.connectPort(i, pcm_buffer[i-1]);
+ for(int i = 0; i < 16; ++i)
+ {
+ for(int j = 0; j < 10; ++j)
+ {
+ pcm_buffer[i][j] = 0.42;
+ }
+ res += h.connectPort((int)Ports::AudioPortOffset + i, pcm_buffer[i]);
}
CPPUNIT_ASSERT_EQUAL(0, res);
@@ -282,19 +300,19 @@ public:
sleep(1); // wait for kit to get loaded (async),
seq.addMidiNote(5, 1, 127);
- for(int i = 0; i < 10; i++) {
+ for(int i = 0; i < 10; i++)
+ {
res = h.run(10);
+ usleep(1000);
CPPUNIT_ASSERT_EQUAL(0, res);
- /*
- printf("Iteration:\n");
- for(int k = 0; k < 4; k++) {
- printf("#%d ", k);
- for(int j = 0; j < 10; j++) printf("[%f]", pcm_buffer[k][j]);
- printf("\n");
- }
- printf("\n");
- */
+ //printf("Iteration:\n");
+ //for(int k = 0; k < 16; k++) {
+ // printf("#%d ", k);
+ // for(int j = 0; j < 10; j++) printf("[%f]", pcm_buffer[k][j]);
+ // printf("\n");
+ //}
+ //printf("\n");
seq.clear();
}
@@ -302,6 +320,7 @@ public:
seq.addMidiNote(5, 1, 127);
res = h.run(10);
+ usleep(1000);
CPPUNIT_ASSERT_EQUAL(0, res);
/*
@@ -321,9 +340,11 @@ public:
comp_val.u = 1040744448; // floating point value 0.133301....
- for(int k = 0; k < 4; k++) {
- for(int j = 0; j < 10; j++) {
- CPPUNIT_ASSERT_EQUAL(((j==0)?comp_val.f:0), pcm_buffer[k][j]);
+ for(int k = 0; k < 4; k++)
+ {
+ for(int j = 0; j < 10; j++)
+ {
+ CPPUNIT_ASSERT_EQUAL(((j==5)?comp_val.f:0), pcm_buffer[k][j]);
}
}
seq.clear();
diff --git a/test/lv2_test_host.cc b/test/lv2_test_host.cc
index 375ae40..9b4fc7a 100644
--- a/test/lv2_test_host.cc
+++ b/test/lv2_test_host.cc
@@ -42,6 +42,7 @@
#include <openssl/err.h>
#include <openssl/evp.h>
#include <string>
+
class Base64 {
public:
Base64()
@@ -64,36 +65,44 @@ public:
std::string write(const char *in, size_t size)
{
std::string out;
-
+
BIO_write((BIO*)bio, in, size);
size_t osize = BIO_ctrl_pending((BIO*)mbio);
char *outbuf = (char*)malloc(osize);
int len = BIO_read((BIO*)mbio, outbuf, osize);
- if(len < 1) return "";
+ if(len < 1)
+ {
+ return "";
+ }
+
out.append(outbuf, len);
free(outbuf);
-
+
return out;
}
-
+
std::string flush()
{
std::string out;
-
+
(void)BIO_flush((BIO*)bio);
size_t size = BIO_ctrl_pending((BIO*)mbio);
char *outbuf = (char*)malloc(size);
int len = BIO_read((BIO*)mbio, outbuf, size);
- if(len < 1) return "";
+ if(len < 1)
+ {
+ return "";
+ }
+
out.append(outbuf, len);
free(outbuf);
-
+
return out;
}
@@ -107,13 +116,15 @@ private:
// TODO: Use map<int, std::string> instead
-static char** uris = NULL;
+static char** uris = nullptr;
static size_t n_uris = 0;
static LV2_URID map_uri(LV2_URID_Map_Handle handle, const char* uri)
{
- for(size_t i = 0; i < n_uris; ++i) {
- if(!strcmp(uris[i], uri)) {
+ for(size_t i = 0; i < n_uris; ++i)
+ {
+ if(!strcmp(uris[i], uri))
+ {
return i + 1;
}
}
@@ -126,22 +137,23 @@ static LV2_URID map_uri(LV2_URID_Map_Handle handle, const char* uri)
static const char* unmap_uri(LV2_URID_Map_Handle handle, LV2_URID urid)
{
- if(urid > 0 && urid <= n_uris) {
+ if((urid > 0) && (urid <= n_uris))
+ {
return uris[urid - 1];
}
- return NULL;
+ return nullptr;
}
-LV2_URID_Map map = { NULL, map_uri };
+LV2_URID_Map map = { nullptr, map_uri };
LV2_Feature map_feature = { LV2_URID_MAP_URI, &map };
-LV2_URID_Unmap unmap = { NULL, unmap_uri };
+LV2_URID_Unmap unmap = { nullptr, unmap_uri };
LV2_Feature unmap_feature = { LV2_URID_UNMAP_URI, &unmap };
-const LV2_Feature* features[] = { &map_feature, &unmap_feature, NULL };
+const LV2_Feature* features[] = { &map_feature, &unmap_feature, nullptr };
LV2TestHost::Sequence::Sequence(void *buffer, size_t buffer_size)
{
- this->buffer = buffer;
- this->buffer_size = buffer_size;
+ this->buffer = buffer;
+ this->buffer_size = buffer_size;
seq = (LV2_Atom_Sequence *)buffer;
@@ -152,15 +164,14 @@ LV2TestHost::Sequence::Sequence(void *buffer, size_t buffer_size)
}
// Keep this to support atom extension from lv2 < 1.10
-static inline void
-_lv2_atom_sequence_clear(LV2_Atom_Sequence* seq)
+static inline void _lv2_atom_sequence_clear(LV2_Atom_Sequence* seq)
{
- seq->atom.size = sizeof(LV2_Atom_Sequence_Body);
+ seq->atom.size = sizeof(LV2_Atom_Sequence_Body);
}
void LV2TestHost::Sequence::clear()
{
- _lv2_atom_sequence_clear(seq);
+ _lv2_atom_sequence_clear(seq);
}
// Keep this to support atom extension from lv2 < 1.10
@@ -169,18 +180,19 @@ _lv2_atom_sequence_append_event(LV2_Atom_Sequence* seq,
uint32_t capacity,
const LV2_Atom_Event* event)
{
- const uint32_t total_size = (uint32_t)sizeof(*event) + event->body.size;
-
- if (capacity - seq->atom.size < total_size) {
- return NULL;
- }
-
- LV2_Atom_Event* e = lv2_atom_sequence_end(&seq->body, seq->atom.size);
- memcpy(e, event, total_size);
-
- seq->atom.size += lv2_atom_pad_size(total_size);
-
- return e;
+ const uint32_t total_size = (uint32_t)sizeof(*event) + event->body.size;
+
+ if(capacity - seq->atom.size < total_size)
+ {
+ return nullptr;
+ }
+
+ LV2_Atom_Event* e = lv2_atom_sequence_end(&seq->body, seq->atom.size);
+ memcpy(e, event, total_size);
+
+ seq->atom.size += lv2_atom_pad_size(total_size);
+
+ return e;
}
void LV2TestHost::Sequence::addMidiNote(uint64_t pos,
@@ -197,66 +209,88 @@ void LV2TestHost::Sequence::addMidiNote(uint64_t pos,
ev.event.time.frames = pos;// sample position
ev.event.body.type = map.map(map.handle, LV2_MIDI__MidiEvent);
ev.event.body.size = sizeof(ev.msg);
-
+
ev.msg[0] = note_on;
ev.msg[1] = key;
ev.msg[2] = velocity;
LV2_Atom_Event *e =
_lv2_atom_sequence_append_event(seq, this->buffer_size, &ev.event);
- (void)e;
+ (void)e;
}
void *LV2TestHost::Sequence::data()
{
- return buffer;
+ return buffer;
}
LV2TestHost::LV2TestHost(const char *lv2_path)
{
- if(lv2_path) {
- setenv("LV2_PATH", lv2_path, 1);
- }
+ if(lv2_path)
+ {
+ setenv("LV2_PATH", lv2_path, 1);
+ }
world = lilv_world_new();
- if(world == NULL) return;
+ if(world == nullptr)
+ {
+ return;
+ }
lilv_world_load_all(world);
}
LV2TestHost::~LV2TestHost()
{
- if(world) lilv_world_free(world);
+ if(world)
+ {
+ lilv_world_free(world);
+ }
}
int LV2TestHost::open(const char *plugin_uri)
{
- if(world == NULL) return 1;
+ if(world == nullptr)
+ {
+ return 1;
+ }
plugins = lilv_world_get_all_plugins(world);
- if(plugins == NULL) return 2;
+ if(plugins == nullptr)
+ {
+ return 2;
+ }
uri = lilv_new_uri(world, plugin_uri);
- if(uri == NULL) return 3;
+ if(uri == nullptr)
+ {
+ return 3;
+ }
plugin = lilv_plugins_get_by_uri(plugins, uri);
- if(plugin == NULL) return 4;
-
+ if(plugin == nullptr)
+ {
+ return 4;
+ }
- return 0;
+ return 0;
}
int LV2TestHost::verify()
{
bool verify = lilv_plugin_verify(plugin);
- if(!verify) return 1;
- return 0;
+ if(!verify)
+ {
+ return 1;
+ }
+
+ return 0;
}
int LV2TestHost::close()
{
- // plugin is a const pointer; nothing to close here.
- return 0;
+ // plugin is a const pointer; nothing to close here.
+ return 0;
}
/* // Get metadata
@@ -330,29 +364,37 @@ int LV2TestHost::getPorts()
}
}
*/
-int LV2TestHost::createInstance()
+int LV2TestHost::createInstance(size_t samplerate)
{
- instance = lilv_plugin_instantiate(plugin, 48000, features);
- if(instance == NULL) return 1;
- return 0;
+ instance = lilv_plugin_instantiate(plugin, samplerate, features);
+ if(instance == nullptr)
+ {
+ return 1;
+ }
+
+ return 0;
}
int LV2TestHost::destroyInstance()
{
- if(instance) lilv_instance_free(instance);
- return 0;
+ if(instance)
+ {
+ lilv_instance_free(instance);
+ }
+
+ return 0;
}
int LV2TestHost::activate()
{
lilv_instance_activate(instance);
- return 0;
+ return 0;
}
int LV2TestHost::deactivate()
{
lilv_instance_deactivate(instance);
- return 0;
+ return 0;
}
int LV2TestHost::loadConfig(const char *config, size_t size)
@@ -379,26 +421,26 @@ int LV2TestHost::loadConfig(const char *config, size_t size)
{
LilvState* restore_state =
lilv_state_new_from_string(world, &map, ttl_config);
-
- lilv_state_restore(restore_state, instance, NULL, NULL,
- LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE,
- features);
+
+ lilv_state_restore(restore_state, instance, nullptr, nullptr,
+ LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE,
+ features);
}
- return 0;
+ return 0;
}
int LV2TestHost::connectPort(int port, void *portdata)
{
- // if(lilv_port_is_a(p, port, lv2_ControlPort)) ...
+ // if(lilv_port_is_a(p, port, lv2_ControlPort)) ...
lilv_instance_connect_port(instance, port, portdata);
- return 0;
+ return 0;
}
int LV2TestHost::run(int num_samples)
{
- lilv_instance_run(instance, num_samples);
- return 0;
+ lilv_instance_run(instance, num_samples);
+ return 0;
}
diff --git a/test/lv2_test_host.h b/test/lv2_test_host.h
index f0677c7..81aa413 100644
--- a/test/lv2_test_host.h
+++ b/test/lv2_test_host.h
@@ -24,56 +24,53 @@
* along with DrumGizmo; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-#ifndef __DRUMGIZMO_LV2_TEST_HOST_H__
-#define __DRUMGIZMO_LV2_TEST_HOST_H__
+#pragma once
#include <lilv/lilv.h>
#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
class LV2TestHost {
public:
- class Sequence {
- public:
- Sequence(void *buffer, size_t buffer_size);
- void clear();
- void addMidiNote(uint64_t pos, uint8_t key, int8_t velocity);
- void *data();
+ class Sequence {
+ public:
+ Sequence(void *buffer, size_t buffer_size);
+ void clear();
+ void addMidiNote(uint64_t pos, uint8_t key, int8_t velocity);
+ void *data();
- private:
- void *buffer;
- size_t buffer_size;
- LV2_Atom_Sequence *seq;
- };
+ private:
+ void *buffer;
+ size_t buffer_size;
+ LV2_Atom_Sequence *seq;
+ };
- LV2TestHost(const char *lv2_path);
- ~LV2TestHost();
+ LV2TestHost(const char *lv2_path);
+ ~LV2TestHost();
- int open(const char *plugin_uri);
- int close();
+ int open(const char *plugin_uri);
+ int close();
- int verify();
+ int verify();
- //void getMetadata();
- //int getPorts();
+ //void getMetadata();
+ //int getPorts();
- int createInstance();
- int destroyInstance();
+ int createInstance(size_t samplerate);
+ int destroyInstance();
- int connectPort(int port, void *portdata);
+ int connectPort(int port, void *portdata);
- int activate();
- int deactivate();
+ int activate();
+ int deactivate();
- int loadConfig(const char *config, size_t size);
- int run(int num_samples);
+ int loadConfig(const char *config, size_t size);
+ int run(int num_samples);
private:
- LilvWorld* world;
- const LilvPlugins* plugins;
+ LilvWorld* world;
+ const LilvPlugins* plugins;
LilvNode* uri;
const LilvPlugin* plugin;
- LilvInstance* instance;
+ LilvInstance* instance;
};
-
-#endif/*__DRUMGIZMO_LV2_TEST_HOST_H__*/
diff --git a/vst/Makefile.mingw32.in b/vst/Makefile.mingw32.in
index 089e869..1caf926 100644
--- a/vst/Makefile.mingw32.in
+++ b/vst/Makefile.mingw32.in
@@ -7,6 +7,10 @@ VST_SRC = \
VST_CFLAGS=-I$(VST_BASE)
DG_SRC = \
+ @top_srcdir@/src/audiocachefile.cc \
+ @top_srcdir@/src/audiocache.cc \
+ @top_srcdir@/src/audiocacheeventhandler.cc \
+ @top_srcdir@/src/audiocacheidmanager.cc \
@top_srcdir@/src/audioinputenginemidi.cc \
@top_srcdir@/src/audiofile.cc \
@top_srcdir@/src/channel.cc \
@@ -35,7 +39,8 @@ DG_SRC = \
@top_srcdir@/src/thread.cc \
@top_srcdir@/src/velocity.cc \
@top_srcdir@/src/versionstr.cc
-DG_CFLAGS = -I.. -I../include -I../src -DSSE -msse -msse2 -DDISABLE_HUGIN
+DG_CFLAGS = -I.. -I../include -I../src -DSSE -msse -msse2
+# -DDISABLE_HUGIN
GUI_SRC = \
@top_srcdir@/plugingui/dgwindow.cc \
@@ -79,7 +84,8 @@ DBG_SRC = \
@top_srcdir@/hugin/hugin.c \
@top_srcdir@/hugin/hugin_syslog.c
-DBG_CFLAGS=-I../hugin -DWITH_HUG_SYSLOG -DWITH_HUG_MUTEX -DDISABLE_HUGIN
+DBG_CFLAGS=-I../hugin -DWITH_HUG_SYSLOG -DWITH_HUG_MUTEX
+# -DDISABLE_HUGIN
#
# http://old.nabble.com/using-VC%2B%2B-.lib-with-mingw-td23151303.html
@@ -137,7 +143,7 @@ SRC = \
all:
gcc $(DBG_CFLAGS) @top_srcdir@/hugin/hugin.c -c
gcc $(DBG_CFLAGS) @top_srcdir@/hugin/hugin_syslog.c -c
- g++ -std=c++11 -static -static-libgcc -O2 -g -Wall $(DBG_CFLAGS) $(DG_CFLAGS) $(DG_LIBS) $(VST_CFLAGS) hugin.o hugin_syslog.o $(DG_SRC) $(VST_SRC) ${SRC} ${GUI_SRC} ${GUI_CFLAGS} $(GUI_LIBS) $(EXPAT_CFLAGS) $(SRC_CFLAGS) $(ZITA_CXXFLAGS) $(EXPAT_LIBS) $(SNDFILE_CFLAGS) $(SNDFILE_LIBS) $(SRC_LIBS) $(ZITA_LIBS) -shared -o drumgizmo_vst.dll -Wl,--out-implib,libdrumgizmo_vst.a
+ g++ $(CXXFLAGS) -std=c++11 -static -static-libgcc -O2 -g -Wall $(DBG_CFLAGS) $(DG_CFLAGS) $(DG_LIBS) $(VST_CFLAGS) hugin.o hugin_syslog.o $(DG_SRC) $(VST_SRC) ${SRC} ${GUI_SRC} ${GUI_CFLAGS} $(GUI_LIBS) $(EXPAT_CFLAGS) $(SRC_CFLAGS) $(ZITA_CXXFLAGS) $(EXPAT_LIBS) $(SNDFILE_CFLAGS) $(SNDFILE_LIBS) $(SRC_LIBS) $(ZITA_LIBS) -shared -o drumgizmo_vst.dll -Wl,--out-implib,libdrumgizmo_vst.a
clean:
del -f drumgizmo_vst.dll libdrumgizmo_vst.a
diff --git a/vst/drumgizmo_vst.cc b/vst/drumgizmo_vst.cc
index d9cb975..c974a2a 100644
--- a/vst/drumgizmo_vst.cc
+++ b/vst/drumgizmo_vst.cc
@@ -32,266 +32,341 @@
#include <drumgizmo.h>
#include <hugin.hpp>
+#include <stdlib.h>
+#include <string>
#define NUM_PROGRAMS 0
#define NUM_PARAMS 0
-DGEditor::DGEditor(AudioEffect* effect)
+DGEditor::DGEditor(AudioEffect* effect)
{
- DEBUG(dgeditor, "Create DGEditor\n");
- dgeff = (DrumGizmoVst*)effect;
- plugingui = NULL;
- drumgizmo = dgeff->drumgizmo;
+ DEBUG(dgeditor, "%s\n", __PRETTY_FUNCTION__);
+ dgeff = (DrumGizmoVst*)effect;
+ plugingui = nullptr;
+ drumgizmo = dgeff->drumgizmo;
}
bool DGEditor::open(void* ptr)
{
- DEBUG(dgeditor, "open GUI (new PluginGUI)\n");
- if(plugingui) delete plugingui;
+ DEBUG(dgeditor, "%s\n", __PRETTY_FUNCTION__);
+ if(plugingui)
+ {
+ delete plugingui;
+ }
- plugingui = new GUI::PluginGUI();
- // plugingui->setChangeMidimapCallback(midimapHandler, dgeff);
-
- // plugingui->show();
- return true;
+ plugingui = new GUI::PluginGUI();
+ plugingui->show();
+ return true;
}
void DGEditor::close()
{
- DEBUG(dgeditor, "close GUI (delete PluginGUI)\n");
- // plugingui->hide();
- if(plugingui) delete plugingui;
- plugingui = NULL;
+ DEBUG(dgeditor, "%s\n", __PRETTY_FUNCTION__);
+
+ if(plugingui)
+ {
+ delete plugingui;
+ }
+
+ plugingui = nullptr;
}
bool DGEditor::isOpen()
{
- DEBUG(vst, "isOpen\n");
- return plugingui != NULL;
+ DEBUG(dgeditor, "%s\n", __PRETTY_FUNCTION__);
+ return plugingui != nullptr;
}
void DGEditor::idle()
{
- DEBUG(vst, "idle\n");
- // if(plugingui) plugingui->processEvents();
+ DEBUG(dgeditor, "%s\n", __PRETTY_FUNCTION__);
+ // if(plugingui) plugingui->processEvents();
}
AudioEffect* createEffectInstance(audioMasterCallback audioMaster)
{
- DEBUG(vst, "createEffectInstance\n");
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
return new DrumGizmoVst(audioMaster);
}
DrumGizmoVst::DrumGizmoVst(audioMasterCallback audioMaster)
- : AudioEffectX(audioMaster, NUM_PROGRAMS, NUM_PARAMS)
+ : AudioEffectX(audioMaster, NUM_PROGRAMS, NUM_PARAMS)
{
- hug_status_t status = hug_init(HUG_FLAG_OUTPUT_TO_SYSLOG | HUG_FLAG_USE_MUTEX,
- HUG_OPTION_SYSLOG_HOST, "192.168.0.10",
- HUG_OPTION_SYSLOG_PORT, 514,
- HUG_OPTION_END);
+ hug_status_t status = HUG_STATUS_OK;
+
+ int hugin_flags = HUG_FLAG_USE_MUTEX;
+
+ const char* syslog_host_env = getenv("DG_SYSLOG_HOST");
+
+ if(syslog_host_env)
+ {
+ std::string syslog_host = syslog_host_env;
+ int syslog_port = 514;
+ const char* syslog_port_env = getenv("DG_SYSLOG_PORT");
+ if(syslog_port_env)
+ {
+ syslog_port = atoi(syslog_port_env);
+ }
+
+ status = hug_init(hugin_flags | HUG_FLAG_OUTPUT_TO_SYSLOG,
+ HUG_OPTION_SYSLOG_HOST, syslog_host.c_str(),
+ HUG_OPTION_SYSLOG_PORT, syslog_port,
+ HUG_OPTION_END);
+ }
+ else
+ {
+ status = hug_init(hugin_flags);
+ }
+
+ if(status != HUG_STATUS_OK)
+ {
+ printf("Error: %d\n", status);
+ }
- if(status != HUG_STATUS_OK) {
- printf("Error: %d\n", status);
- }
+ INFO(vst, "We are up and running");
- INFO(example, "We are up and running");
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
- DEBUG(vst, "DrumGizmoVst()\n");
+ pos = 0;
+ buffer = nullptr;
+ buffer_size = 0;
- pos = 0;
- buffer = NULL;
- buffer_size = 0;
+ output = nullptr;
+ input = nullptr;
+ drumgizmo = nullptr;
- output = NULL;
- input = NULL;
- drumgizmo = NULL;
-
- output = new OutputVST();
- input = new InputVST();
- drumgizmo = new DrumGizmo(output, input);
+ output = new OutputVST();
+ input = new InputVST();
+ drumgizmo = new DrumGizmo(output, input);
// initialize programs
- //programs = new DrumGizmoVstProgram[kNumPrograms];
- //for(VstInt32 i = 0; i < 16; i++) channelPrograms[i] = i;
+ // programs = new DrumGizmoVstProgram[kNumPrograms];
+ // for(VstInt32 i = 0; i < 16; i++) channelPrograms[i] = i;
+
+ // if(programs) setProgram(0);
- //if(programs) setProgram(0);
-
- if(audioMaster) {
+ if(audioMaster)
+ {
setNumInputs(0); // no audio inputs
setNumOutputs(NUM_OUTPUTS);
canProcessReplacing();
isSynth();
- union {
- char cid[4];
- unsigned int iid;
- } id;
+ union
+ {
+ char cid[4];
+ unsigned int iid;
+ } id;
- memcpy(id.cid, "DGV5", 4); // Four bytes typecasted into an unsigned integer
+ memcpy(id.cid, "DGV5", 4); // Four bytes typecasted into an unsigned integer
setUniqueID(id.iid);
- // setUniqueID((unsigned int)time(NULL));
-
+ // setUniqueID((unsigned int)time(nullptr));
}
initProcess();
suspend();
- editor = new DGEditor(this);
- setEditor(editor);
+ editor = new DGEditor(this);
+ setEditor(editor);
- programsAreChunks(true);
+ programsAreChunks(true);
- // getChunk
- // file:///home/deva/docs/c/drumgizmo/vst/vstsdk2.4/doc/html/class_audio_effect.html#42883c327783d7d31ed513b10c9204fc
+ // getChunk
+ // file:///home/deva/docs/c/drumgizmo/vst/vstsdk2.4/doc/html/class_audio_effect.html#42883c327783d7d31ed513b10c9204fc
- // setChunk
- // file:///home/deva/docs/c/drumgizmo/vst/vstsdk2.4/doc/html/class_audio_effect.html#b6e4c31c1acf8d1fc4046521912787b1
+ // setChunk
+ // file:///home/deva/docs/c/drumgizmo/vst/vstsdk2.4/doc/html/class_audio_effect.html#b6e4c31c1acf8d1fc4046521912787b1
}
DrumGizmoVst::~DrumGizmoVst()
{
- DEBUG(vst, "~DrumGizmoVst(1)\n");
- if(drumgizmo) delete drumgizmo;
- DEBUG(vst, "~DrumGizmoVst(2)\n");
- if(input) delete input;
- DEBUG(vst, "~DrumGizmoVst(3)\n");
- if(output) delete output;
- DEBUG(vst, "~DrumGizmoVst(4)\n");
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
+
+ DEBUG(vst, "~DrumGizmoVst(1)\n");
+ if(drumgizmo)
+ {
+ delete drumgizmo;
+ }
+
+ DEBUG(vst, "~DrumGizmoVst(2)\n");
+ if(input)
+ {
+ delete input;
+ }
+
+ DEBUG(vst, "~DrumGizmoVst(3)\n");
+ if(output)
+ {
+ delete output;
+ }
- hug_close();
+ DEBUG(vst, "~DrumGizmoVst(4)\n");
+
+ hug_close();
}
-VstInt32 DrumGizmoVst::getChunk(void **data, bool isPreset)
+VstInt32 DrumGizmoVst::getChunk(void** data, bool isPreset)
{
- DEBUG(vst, "getChunk(data: %p isPreset: %d)\n", *data, isPreset?1:0);
- std::string cfg = drumgizmo->configString();
- DEBUG(vst, "drumgizmo->config := %s\n", cfg.c_str());
- char *config = strdup(cfg.c_str());
- *data = config;
- return cfg.length();
+ DEBUG(vst, "%s - data: %p isPreset: %d\n",
+ __PRETTY_FUNCTION__, *data, isPreset ? 1 : 0);
+ std::string cfg = drumgizmo->configString();
+ DEBUG(vst, "drumgizmo->config := %s\n", cfg.c_str());
+ char* config = strdup(cfg.c_str());
+ *data = config;
+ return cfg.length();
}
-VstInt32 DrumGizmoVst::setChunk(void *data, VstInt32 byteSize, bool isPreset)
+VstInt32 DrumGizmoVst::setChunk(void* data, VstInt32 byteSize, bool isPreset)
{
- std::string config;
- config.append((const char*)data, (size_t)byteSize);
- DEBUG(vst, "setChunk(isPreset: %d): [%d] %s\n",
- isPreset?1:0, byteSize, config.c_str());
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
- if(!drumgizmo->setConfigString(config)) {
- ERR(vst, "setConfigString failed...\n");
- return 1;
- }
+ std::string config;
+ config.append((const char*)data, (size_t)byteSize);
+ DEBUG(vst, "setChunk(isPreset: %d): [%d] %s\n", isPreset ? 1 : 0, byteSize,
+ config.c_str());
- return 0;
+ if(!drumgizmo->setConfigString(config))
+ {
+ ERR(vst, "setConfigString failed...\n");
+ return 1;
+ }
+
+ return 0;
}
-void DrumGizmoVst::setProgram(VstInt32 program) {}
-void DrumGizmoVst::setProgramName(char* name) {}
-void DrumGizmoVst::getProgramName(char* name) { name[0] = '\0'; }
+void DrumGizmoVst::setProgram(VstInt32 program)
+{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
+}
+void DrumGizmoVst::setProgramName(char* name)
+{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
+}
+void DrumGizmoVst::getProgramName(char* name)
+{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
+ name[0] = '\0';
+}
void DrumGizmoVst::getParameterLabel(VstInt32 index, char* label)
{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
label[0] = '\0';
/*
switch(index)
{
- case kWaveform1:
- case kWaveform2:
- vst_strncpy(label, "Shape", kVstMaxParamStrLen);
- break;
-
- case kFreq1:
- case kFreq2:
- vst_strncpy(label, "Hz", kVstMaxParamStrLen);
- break;
-
- case kVolume1:
- case kVolume2:
- case kVolume:
- vst_strncpy(label, "dB", kVstMaxParamStrLen);
- break;
+ case kWaveform1:
+ case kWaveform2:
+ vst_strncpy(label, "Shape", kVstMaxParamStrLen);
+ break;
+
+ case kFreq1:
+ case kFreq2:
+ vst_strncpy(label, "Hz", kVstMaxParamStrLen);
+ break;
+
+ case kVolume1:
+ case kVolume2:
+ case kVolume:
+ vst_strncpy(label, "dB", kVstMaxParamStrLen);
+ break;
}
*/
}
void DrumGizmoVst::getParameterDisplay(VstInt32 index, char* text)
{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
text[0] = 0;
/*
switch(index)
{
- case kWaveform1:
- if(fWaveform1 < .5)
- vst_strncpy(text, "Sawtooth", kVstMaxParamStrLen);
- else
- vst_strncpy(text, "Pulse", kVstMaxParamStrLen);
- break;
-
- case kFreq1: float2string(fFreq1, text, kVstMaxParamStrLen); break;
- case kVolume1: dB2string(fVolume1, text, kVstMaxParamStrLen); break;
-
- case kWaveform2:
- if(fWaveform2 < .5)
- vst_strncpy(text, "Sawtooth", kVstMaxParamStrLen);
- else
- vst_strncpy(text, "Pulse", kVstMaxParamStrLen);
- break;
-
- case kFreq2: float2string(fFreq2, text, kVstMaxParamStrLen); break;
- case kVolume2: dB2string(fVolume2, text, kVstMaxParamStrLen); break;
- case kVolume: dB2string(fVolume, text, kVstMaxParamStrLen); break;
+ case kWaveform1:
+ if(fWaveform1 < .5)
+ vst_strncpy(text, "Sawtooth", kVstMaxParamStrLen);
+ else
+ vst_strncpy(text, "Pulse", kVstMaxParamStrLen);
+ break;
+
+ case kFreq1: float2string(fFreq1, text, kVstMaxParamStrLen);
+ break;
+ case kVolume1: dB2string(fVolume1, text, kVstMaxParamStrLen);
+ break;
+
+ case kWaveform2:
+ if(fWaveform2 < .5)
+ vst_strncpy(text, "Sawtooth", kVstMaxParamStrLen);
+ else
+ vst_strncpy(text, "Pulse", kVstMaxParamStrLen);
+ break;
+
+ case kFreq2: float2string(fFreq2, text, kVstMaxParamStrLen);
+ break;
+ case kVolume2: dB2string(fVolume2, text, kVstMaxParamStrLen);
+ break;
+ case kVolume: dB2string(fVolume, text, kVstMaxParamStrLen);
+ break;
}
*/
}
void DrumGizmoVst::getParameterName(VstInt32 index, char* label)
{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
/*
switch(index)
{
- case kWaveform1: vst_strncpy(label, "Wave 1", kVstMaxParamStrLen); break;
- case kFreq1: vst_strncpy(label, "Freq 1", kVstMaxParamStrLen); break;
- case kVolume1: vst_strncpy(label, "Levl 1", kVstMaxParamStrLen); break;
- case kWaveform2: vst_strncpy(label, "Wave 2", kVstMaxParamStrLen); break;
- case kFreq2: vst_strncpy(label, "Freq 2", kVstMaxParamStrLen); break;
- case kVolume2: vst_strncpy(label, "Levl 2", kVstMaxParamStrLen); break;
- case kVolume: vst_strncpy(label, "Volume", kVstMaxParamStrLen); break;
+ case kWaveform1: vst_strncpy(label, "Wave 1", kVstMaxParamStrLen);
+ break;
+ case kFreq1: vst_strncpy(label, "Freq 1", kVstMaxParamStrLen);
+ break;
+ case kVolume1: vst_strncpy(label, "Levl 1", kVstMaxParamStrLen);
+ break;
+ case kWaveform2: vst_strncpy(label, "Wave 2", kVstMaxParamStrLen);
+ break;
+ case kFreq2: vst_strncpy(label, "Freq 2", kVstMaxParamStrLen);
+ break;
+ case kVolume2: vst_strncpy(label, "Levl 2", kVstMaxParamStrLen);
+ break;
+ case kVolume: vst_strncpy(label, "Volume", kVstMaxParamStrLen);
+ break;
}
*/
}
void DrumGizmoVst::setParameter(VstInt32 index, float value)
{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
/*
DrumGizmoVstProgram *ap = &programs[curProgram];
switch(index)
{
- case kWaveform1: fWaveform1 = ap->fWaveform1 = value; break;
- case kFreq1: fFreq1 = ap->fFreq1 = value; break;
- case kVolume1: fVolume1 = ap->fVolume1 = value; break;
- case kWaveform2: fWaveform2 = ap->fWaveform2 = value; break;
- case kFreq2: fFreq2 = ap->fFreq2 = value; break;
- case kVolume2: fVolume2 = ap->fVolume2 = value; break;
- case kVolume: fVolume = ap->fVolume = value; break;
+ case kWaveform1: fWaveform1 = ap->fWaveform1 = value; break;
+ case kFreq1: fFreq1 = ap->fFreq1 = value; break;
+ case kVolume1: fVolume1 = ap->fVolume1 = value; break;
+ case kWaveform2: fWaveform2 = ap->fWaveform2 = value; break;
+ case kFreq2: fFreq2 = ap->fFreq2 = value; break;
+ case kVolume2: fVolume2 = ap->fVolume2 = value; break;
+ case kVolume: fVolume = ap->fVolume = value; break;
}
*/
}
float DrumGizmoVst::getParameter(VstInt32 index)
{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
float value = 0;
/*
switch(index)
{
- case kWaveform1: value = fWaveform1; break;
- case kFreq1: value = fFreq1; break;
- case kVolume1: value = fVolume1; break;
- case kWaveform2: value = fWaveform2; break;
- case kFreq2: value = fFreq2; break;
- case kVolume2: value = fVolume2; break;
- case kVolume: value = fVolume; break;
+ case kWaveform1: value = fWaveform1; break;
+ case kFreq1: value = fFreq1; break;
+ case kVolume1: value = fVolume1; break;
+ case kWaveform2: value = fWaveform2; break;
+ case kFreq2: value = fFreq2; break;
+ case kVolume2: value = fVolume2; break;
+ case kVolume: value = fVolume; break;
}
*/
return value;
@@ -300,6 +375,7 @@ float DrumGizmoVst::getParameter(VstInt32 index)
bool DrumGizmoVst::getOutputProperties(VstInt32 index,
VstPinProperties* properties)
{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
if(index < NUM_OUTPUTS)
{
vst_strncpy(properties->label, "Channel ", 63);
@@ -317,75 +393,105 @@ bool DrumGizmoVst::getOutputProperties(VstInt32 index,
bool DrumGizmoVst::getProgramNameIndexed(VstInt32 category, VstInt32 index,
char* text)
{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
return false;
}
bool DrumGizmoVst::getEffectName(char* name)
{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
vst_strncpy(name, "DrumGizmo4", kVstMaxEffectNameLen);
return true;
}
bool DrumGizmoVst::getVendorString(char* text)
{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
vst_strncpy(text, "Aasimon.org", kVstMaxVendorStrLen);
return true;
}
bool DrumGizmoVst::getProductString(char* text)
{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
vst_strncpy(text, "Vst Synth", kVstMaxProductStrLen);
return true;
}
VstInt32 DrumGizmoVst::getVendorVersion()
-{
- return 1000;
+{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
+ return 1000;
}
VstInt32 DrumGizmoVst::canDo(char* text)
{
- if(!strcmp(text, "receiveVstEvents")) return 1;
- if(!strcmp(text, "receiveVstMidiEvent")) return 1;
- //if(!strcmp(text, "midiProgramNames")) return 1;
- return -1; // explicitly can't do; 0 => don't know
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
+ if(!strcmp(text, "receiveVstEvents"))
+ {
+ return 1;
+ }
+
+ if(!strcmp(text, "receiveVstMidiEvent"))
+ {
+ return 1;
+ }
+
+ // if(!strcmp(text, "midiProgramNames")) return 1;
+ return -1; // explicitly can't do; 0 => don't know
}
VstInt32 DrumGizmoVst::getNumMidiInputChannels()
{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
return 1; // we are monophonic
}
VstInt32 DrumGizmoVst::getNumMidiOutputChannels()
{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
return 0; // no MIDI output back to Host app
}
VstInt32 DrumGizmoVst::getMidiProgramName(VstInt32 channel,
MidiProgramName* mpn)
{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
VstInt32 prg = mpn->thisProgramIndex;
- if(prg < 0 || prg >= 128)
+ if((prg < 0) || (prg >= 128))
+ {
return 0;
+ }
+
fillProgram(channel, prg, mpn);
if(channel == 9)
+ {
return 1;
+ }
+
return 128L;
}
VstInt32 DrumGizmoVst::getCurrentMidiProgram(VstInt32 channel,
MidiProgramName* mpn)
{
- if(channel < 0 || channel >= 16 || !mpn) return -1;
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
+ if((channel < 0) || (channel >= 16) || !mpn)
+ {
+ return -1;
+ }
+
VstInt32 prg = 0;
mpn->thisProgramIndex = prg;
fillProgram(channel, prg, mpn);
+
return prg;
}
void DrumGizmoVst::fillProgram(VstInt32 channel, VstInt32 prg,
MidiProgramName* mpn)
{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
mpn->midiBankMsb = mpn->midiBankLsb = -1;
mpn->reserved = 0;
mpn->flags = 0;
@@ -398,16 +504,17 @@ void DrumGizmoVst::fillProgram(VstInt32 channel, VstInt32 prg,
VstInt32 DrumGizmoVst::getMidiProgramCategory(VstInt32 channel,
MidiProgramCategory* cat)
{
- cat->parentCategoryIndex = -1; // -1:no parent category
- cat->flags = 0; // reserved, none defined yet, zero.
- // VstInt32 category = cat->thisCategoryIndex;
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
+ cat->parentCategoryIndex = -1; // -1:no parent category
+ cat->flags = 0; // reserved, none defined yet, zero.
+ // VstInt32 category = cat->thisCategoryIndex;
vst_strncpy(cat->name, "Drums", 63);
return 1;
}
bool DrumGizmoVst::hasMidiProgramsChanged(VstInt32 channel)
{
- return false; // updateDisplay()
+ return false; // updateDisplay()
}
bool DrumGizmoVst::getMidiKeyName(VstInt32 channel, MidiKeyName* key)
@@ -416,49 +523,68 @@ bool DrumGizmoVst::getMidiKeyName(VstInt32 channel, MidiKeyName* key)
// if keyName is "" the standard name of the key will be displayed.
// if false is returned, no MidiKeyNames defined for 'thisProgramIndex'.
{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
// key->thisProgramIndex; // >= 0. fill struct for this program index.
// key->thisKeyNumber; // 0 - 127. fill struct for this key number.
key->keyName[0] = 0;
- key->reserved = 0; // zero
- key->flags = 0; // reserved, none defined yet, zero.
+ key->reserved = 0; // zero
+ key->flags = 0; // reserved, none defined yet, zero.
return false;
}
void DrumGizmoVst::setSampleRate(float sampleRate)
{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
AudioEffectX::setSampleRate(sampleRate);
- drumgizmo->setSamplerate(sampleRate);
+ drumgizmo->setSamplerate(sampleRate);
}
void DrumGizmoVst::setBlockSize(VstInt32 blockSize)
{
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
AudioEffectX::setBlockSize(blockSize);
}
void DrumGizmoVst::initProcess()
{
- // drumgizmo->loadkit(getenv("DRUMGIZMO_DRUMKIT"));
- drumgizmo->init();
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
+ // drumgizmo->loadkit(getenv("DRUMGIZMO_DRUMKIT"));
+ drumgizmo->init();
}
void DrumGizmoVst::processReplacing(float** inputs, float** outputs,
VstInt32 sampleFrames)
{
- output->setOutputs(outputs);
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
+ long lvl = getCurrentProcessLevel();
+ // 0 = realtime/normal
+ // 1 = non-realtime/rendering
+ // 2 = offline processing
+ drumgizmo->setFreeWheel(lvl != 0);
- if(buffer_size != (size_t)sampleFrames) {
- if(buffer) free(buffer);
- buffer_size = sampleFrames;
- buffer = (sample_t*)malloc(sizeof(sample_t) * buffer_size);
- }
+ output->setOutputs(outputs);
+
+ if(buffer_size != (size_t)sampleFrames)
+ {
+ if(buffer)
+ {
+ free(buffer);
+ }
+
+ buffer_size = sampleFrames;
+ buffer = (sample_t*)malloc(sizeof(sample_t) * buffer_size);
+
+ drumgizmo->setFrameSize(buffer_size);
+ }
- drumgizmo->run(pos, buffer, buffer_size);
+ drumgizmo->run(pos, buffer, buffer_size);
- pos += sampleFrames;
+ pos += sampleFrames;
}
VstInt32 DrumGizmoVst::processEvents(VstEvents* ev)
{
- input->processEvents(ev);
+ DEBUG(vst, "%s\n", __PRETTY_FUNCTION__);
+ input->processEvents(ev);
return 1;
}