diff options
39 files changed, 3538 insertions, 1498 deletions
| @@ -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);      } @@ -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.wavBinary files differ new file mode 100644 index 0000000..3dec8a9 --- /dev/null +++ b/test/kit/ride-multi-channel.wav diff --git a/test/kit/ride-single-channel.wav b/test/kit/ride-single-channel.wavBinary files differ new file mode 100644 index 0000000..1760697 --- /dev/null +++ b/test/kit/ride-single-channel.wav 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;  } | 
