summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am4
-rw-r--r--src/Makefile.am.drumgizmo6
-rw-r--r--src/audiocache.cc276
-rw-r--r--src/audiocache.h113
-rw-r--r--src/audiocacheeventhandler.cc326
-rw-r--r--src/audiocacheeventhandler.h114
-rw-r--r--src/audiocachefile.cc180
-rw-r--r--src/audiocachefile.h98
-rw-r--r--src/audiocacheidmanager.cc124
-rw-r--r--src/audiocacheidmanager.h93
-rw-r--r--src/audiofile.cc277
-rw-r--r--src/audiofile.h72
-rw-r--r--src/drumgizmo.cc1253
-rw-r--r--src/drumgizmo.h71
-rw-r--r--src/drumkitloader.cc230
-rw-r--r--src/drumkitloader.h86
-rw-r--r--src/events.h6
-rw-r--r--src/mutex.cc120
-rw-r--r--src/mutex.h30
-rw-r--r--src/semaphore.cc8
20 files changed, 2334 insertions, 1153 deletions
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);