summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2015-12-24 17:49:09 +0100
committerBent Bisballe Nyeng <deva@aasimon.org>2016-01-20 13:32:03 +0100
commit84e2cacec69c73712100de31585da3fe96c94704 (patch)
tree2e947348e26dabb487d05d6e1437635a18e5036c
parentc3841a8cec9d239bca27a113c013a032dfc658bd (diff)
Split internal CacheManager AFile class out into it's own file/class CacheAudioFile and improve interface.
-rw-r--r--src/Makefile.am.drumgizmo2
-rw-r--r--src/cacheaudiofile.cc156
-rw-r--r--src/cacheaudiofile.h63
-rw-r--r--src/cachemanager.cc660
-rw-r--r--src/cachemanager.h262
-rw-r--r--vst/Makefile.mingw32.in2
6 files changed, 659 insertions, 486 deletions
diff --git a/src/Makefile.am.drumgizmo b/src/Makefile.am.drumgizmo
index ee09445..be46a3a 100644
--- a/src/Makefile.am.drumgizmo
+++ b/src/Makefile.am.drumgizmo
@@ -4,6 +4,8 @@ DRUMGIZMO_SOURCES = \
$(top_srcdir)/src/channel.cc \
$(top_srcdir)/src/channelmixer.cc \
$(top_srcdir)/src/chresampler.cc \
+ $(top_srcdir)/src/cacheaudiofile.cc \
+ $(top_srcdir)/src/cachemanager.cc \
$(top_srcdir)/src/configfile.cc \
$(top_srcdir)/src/cachemanager.cc \
$(top_srcdir)/src/configuration.cc \
diff --git a/src/cacheaudiofile.cc b/src/cacheaudiofile.cc
new file mode 100644
index 0000000..0d4aec9
--- /dev/null
+++ b/src/cacheaudiofile.cc
@@ -0,0 +1,156 @@
+/* -*- 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 "cacheaudiofile.h"
+
+#include <assert.h>
+
+#include <hugin.hpp>
+
+#include <audiotypes.h>
+
+#include "cachemanager.h"
+
+CacheAudioFile::CacheAudioFile(const std::string& filename)
+ : filename(filename)
+{
+ 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));
+ }
+}
+
+CacheAudioFile::~CacheAudioFile()
+{
+ if(fh)
+ {
+ sf_close(fh);
+ fh = nullptr;
+ }
+}
+
+size_t CacheAudioFile::getSize() const
+{
+ return sf_info.frames;
+}
+
+const std::string& CacheAudioFile::getFilename() const
+{
+ return filename;
+}
+
+void CacheAudioFile::readChunk(const CacheChannels& channels,
+ size_t pos, size_t num_samples)
+{
+ if(fh == nullptr)
+ {
+ printf("File handle is null.\n");
+ return;
+ }
+
+ if(pos > sf_info.frames)
+ {
+ printf("pos > sf_info.frames\n");
+ 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;
+ }
+}
+
+CacheAudioFile& CacheAudioFiles::getAudioFile(const std::string filename)
+{
+ auto it = audiofiles.find(filename);
+ if(it == audiofiles.end())
+ {
+ it = audiofiles.insert(audiofiles.begin(), std::make_pair(filename, new CacheAudioFile(filename)));
+ //it = audiofiles.find(filename);
+ }
+
+ auto afile = it->second;
+
+ // Increase ref count.
+ ++afile->ref;
+
+ return *afile;
+}
+
+void CacheAudioFiles::release(const std::string filename)
+{
+ auto it = audiofiles.find(filename);
+ if(it == audiofiles.end())
+ {
+ 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/cacheaudiofile.h b/src/cacheaudiofile.h
new file mode 100644
index 0000000..4f78247
--- /dev/null
+++ b/src/cacheaudiofile.h
@@ -0,0 +1,63 @@
+/* -*- 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 <sndfile.h>
+
+class CacheChannels;
+
+class CacheAudioFile {
+ friend class CacheAudioFiles;
+public:
+ CacheAudioFile(const std::string& filename);
+ ~CacheAudioFile();
+
+ size_t getSize() const;
+ const std::string& getFilename() const;
+
+ 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 CacheAudioFiles {
+public:
+ CacheAudioFile& getAudioFile(const std::string filename);
+ void release(const std::string filename);
+
+private:
+ std::map<std::string, CacheAudioFile*> audiofiles;
+};
diff --git a/src/cachemanager.cc b/src/cachemanager.cc
index 4cc1fa5..b2e10ac 100644
--- a/src/cachemanager.cc
+++ b/src/cachemanager.cc
@@ -3,7 +3,7 @@
* cachemanager.cc
*
* Fri Apr 10 10:39:24 CEST 2015
- * Copyright 2015 Jonas Suhr Christensen
+ * Copyright 2015 Jonas Suhr Christensen
* jsc@umbraculum.org
****************************************************************************/
@@ -30,436 +30,382 @@
#include <stdio.h>
#include <assert.h>
-#include <sndfile.h>
-
#include <hugin.hpp>
+#include "cacheaudiofile.h"
+
#define CHUNKSIZE(x) (x * CHUNK_MULTIPLIER)
-class AFile {
-public:
- AFile(std::string filename)
- : ref(0)
- , filename(filename)
- {
- 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 0;
- }
- }
-
- ~AFile()
- {
- sf_close(fh);
- fh = NULL;
- }
-
- int ref;
- SNDFILE* fh;
- SF_INFO sf_info;
- std::string filename;
-};
-
-static void readChunk(AFile* file,
- std::list<CacheManager::Channel>& channels,
- size_t pos, size_t num_samples)
-{
- SNDFILE* fh = file->fh;
- SF_INFO& sf_info = file->sf_info;
-
- if(fh == NULL) {
- printf("File handle is null.\n");
- return;
- }
-
- if(pos > sf_info.frames) {
- printf("pos > sf_info.frames\n");
- 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 = NULL;
- 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;
- }
-}
CacheManager::CacheManager()
- : framesize(0)
- , nodata(NULL)
+ : framesize(0)
+ , nodata(nullptr)
{
}
CacheManager::~CacheManager()
{
- deinit();
- delete[] nodata;
+ deinit();
+ delete[] nodata;
}
void CacheManager::init(size_t poolsize, bool threaded)
{
- this->threaded = threaded;
-
- id2cache.resize(poolsize);
- for(size_t i = 0; i < poolsize; i++) {
- availableids.push_back(i);
- }
-
- running = true;
- if(threaded) {
- run();
- sem_run.wait();
- }
+ this->threaded = threaded;
+
+ id2cache.resize(poolsize);
+ for(size_t i = 0; i < poolsize; ++i)
+ {
+ availableids.push_back(i);
+ }
+
+ running = true;
+ if(threaded)
+ {
+ run();
+ sem_run.wait();
+ }
}
void CacheManager::deinit()
{
- if(!running) return;
- running = false;
- if(threaded) {
- sem.post();
- wait_stop();
- }
+ if(!running) return;
+ running = false;
+ if(threaded)
+ {
+ sem.post();
+ wait_stop();
+ }
}
-// Invariant: initial_samples_needed < preloaded audio data
+// Invariant: initial_samples_needed < preloaded audio data
sample_t *CacheManager::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;
- }
-
- {
- MutexAutolock l(m_ids);
- if(availableids.empty()) {
- id = CACHE_DUMMYID;
- } else {
- id = availableids.front();
- availableids.pop_front();
- }
- }
-
- if(id == CACHE_DUMMYID) {
- assert(nodata);
- return nodata;
- }
-
- AFile *afile = NULL;
- if(files.find(file->filename) == files.end()) {
- afile = new AFile(file->filename);
- files[file->filename] = afile;
- } else {
- afile = files[file->filename];
- }
-
- // Increase ref count.
- afile->ref++;
-
- cache_t c;
- c.afile = afile;
- c.channel = channel;
-
- // next call to 'next' will read from this point.
- c.localpos = initial_samples_needed;
-
- c.front = NULL; // This is allocated when needed.
- c.back = NULL; // 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;
-
- {
- MutexAutolock l(m_ids);
- id2cache[id] = c;
- }
- // NOTE: Below this point we can no longer write to 'c'.
-
- // Only load next buffer if there are more data in the file to be loaded...
- if(c.pos < file->size) {
- cache_t& c = id2cache[id]; // Create new writeable 'c'.
-
- if(c.back == NULL) {
- c.back = new sample_t[CHUNKSIZE(framesize)];
- }
-
- cevent_t e =
- createLoadNextEvent(c.afile, c.channel, c.pos, c.back, &c.ready);
- pushEvent(e);
- }
-
- return c.preloaded_samples; // return preloaded data
+ if(!file->isValid())
+ {
+ // File preload not yet ready - skip this sample.
+ id = CACHE_DUMMYID;
+ assert(nodata);
+ return nodata;
+ }
+
+ {
+ MutexAutolock l(m_ids);
+ if(availableids.empty())
+ {
+ id = CACHE_DUMMYID;
+ }
+ else
+ {
+ id = availableids.front();
+ availableids.pop_front();
+ }
+ }
+
+ if(id == CACHE_DUMMYID)
+ {
+ assert(nodata);
+ return nodata;
+ }
+
+ CacheAudioFile& afile = files.getAudioFile(file->filename);
+
+ cache_t c;
+ c.afile = &afile;
+ 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;
+
+ {
+ MutexAutolock l(m_ids);
+ id2cache[id] = c;
+ }
+ // NOTE: Below this point we can no longer write to 'c'.
+
+ // Only load next buffer if there are more data in the file to be loaded...
+ if(c.pos < file->size)
+ {
+ cache_t& c = id2cache[id]; // Create new writeable 'c'.
+
+ if(c.back == nullptr)
+ {
+ c.back = new sample_t[CHUNKSIZE(framesize)];
+ }
+
+ cevent_t e =
+ createLoadNextEvent(c.afile, c.channel, c.pos, c.back, &c.ready);
+ pushEvent(e);
+ }
+
+ return c.preloaded_samples; // return preloaded data
}
-sample_t *CacheManager::next(cacheid_t id, size_t &size)
+sample_t *CacheManager::next(cacheid_t id, size_t &size)
{
- size = framesize;
-
- if(id == CACHE_DUMMYID) {
- assert(nodata);
- return nodata;
- }
-
- cache_t& c = id2cache[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 = NULL; // 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;
- }
- }
-
- if(!c.ready) {
- printf("#%d: NOT READY!\n", id); // TODO: Count and show in UI?
- 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);
-
- if(c.pos < c.afile->sf_info.frames) {
- if(c.back == NULL) {
- c.back = new sample_t[CHUNKSIZE(framesize)];
- }
-
- cevent_t e = createLoadNextEvent(c.afile, c.channel, c.pos, c.back, &c.ready);
- pushEvent(e);
- }
-
- if(!c.front) {
- printf("We shouldn't get here... ever!\n");
- assert(false);
- return nodata;
- }
-
- return c.front;
+ size = framesize;
+
+ if(id == CACHE_DUMMYID)
+ {
+ assert(nodata);
+ return nodata;
+ }
+
+ cache_t& c = id2cache[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;
+ }
+ }
+
+ if(!c.ready)
+ {
+ //printf("#%d: NOT READY!\n", id); // TODO: Count and show in UI?
+ 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);
+
+ if(c.pos < c.afile->getSize())
+ {
+ if(c.back == nullptr)
+ {
+ c.back = new sample_t[CHUNKSIZE(framesize)];
+ }
+
+ cevent_t e = createLoadNextEvent(c.afile, c.channel, c.pos, c.back, &c.ready);
+ pushEvent(e);
+ }
+
+ if(!c.front)
+ {
+ printf("We shouldn't get here... ever!\n");
+ assert(false);
+ return nodata;
+ }
+
+ return c.front;
}
void CacheManager::close(cacheid_t id)
{
- if(id == CACHE_DUMMYID) {
- return;
- }
+ if(id == CACHE_DUMMYID)
+ {
+ return;
+ }
- cevent_t e = createCloseEvent(id);
- pushEvent(e);
+ cevent_t e = createCloseEvent(id);
+ pushEvent(e);
}
void CacheManager::setFrameSize(size_t framesize)
{
- if(framesize > this->framesize) {
- delete[] nodata;
- nodata = new sample_t[framesize];
-
- for(size_t i = 0; i < framesize; i++) {
- nodata[i] = 0;
- }
- }
-
- this->framesize = framesize;
+ if(framesize > this->framesize)
+ {
+ delete[] nodata;
+ nodata = new sample_t[framesize];
+
+ for(size_t i = 0; i < framesize; ++i)
+ {
+ nodata[i] = 0;
+ }
+ }
+
+ this->framesize = framesize;
}
void CacheManager::setAsyncMode(bool async)
{
- // TODO: Clean out read queue.
- // TODO: Block until reader thread is idle, otherwise we might screw up the
- // buffers...?
- threaded = async;
+ // TODO: Clean out read queue.
+ // TODO: Block until reader thread is idle, otherwise we might screw up the
+ // buffers...?
+ threaded = async;
}
-void CacheManager::handleLoadNextEvent(cevent_t &e)
+void CacheManager::handleLoadNextEvent(cevent_t &event)
{
- assert(files.find(e.afile->filename) != files.end());
- readChunk(files[e.afile->filename], e.channels, e.pos, CHUNKSIZE(framesize));
+ event.afile->readChunk(event.channels, event.pos, CHUNKSIZE(framesize));
}
-void CacheManager::handleCloseEvent(cevent_t &e)
+void CacheManager::handleCloseEvent(cevent_t &e)
{
- cache_t& c = id2cache[e.id];
-
- {
- MutexAutolock l(m_events);
-
- auto f = files.find(c.afile->filename);
-
- if(f != files.end()) {
- // Decrease ref count and close file if needed (in AFile destructor).
- files[c.afile->filename]->ref--;
- if(files[c.afile->filename]->ref == 0) {
- delete f->second;
- files.erase(f);
- }
- }
- }
-
- delete[] c.front;
- delete[] c.back;
-
- {
- MutexAutolock l(m_ids);
- availableids.push_back(e.id);
- }
+ handleCloseCache(e.id);
+}
+
+void CacheManager::handleCloseCache(cacheid_t& cacheid)
+{
+ cache_t& cache = id2cache[cacheid];
+ {
+ MutexAutolock l(m_events);
+
+ files.release(cache.afile->getFilename());
+ }
+
+ delete[] cache.front;
+ delete[] cache.back;
+
+ {
+ MutexAutolock l(m_ids);
+ availableids.push_back(cacheid);
+ }
}
void CacheManager::handleEvent(cevent_t &e)
{
- switch(e.cmd) {
- case LOADNEXT:
- handleLoadNextEvent(e);
- break;
- case CLOSE:
- handleCloseEvent(e);
- break;
- }
+ switch(e.cmd)
+ {
+ case LOADNEXT:
+ handleLoadNextEvent(e);
+ break;
+ case CLOSE:
+ handleCloseEvent(e);
+ break;
+ }
}
void CacheManager::thread_main()
{
- sem_run.post(); // Signal that the thread has been started
+ sem_run.post(); // Signal that the thread has been started
- while(running) {
- sem.wait();
+ while(running)
+ {
+ sem.wait();
- m_events.lock();
- if(eventqueue.empty()) {
- m_events.unlock();
- continue;
- }
+ m_events.lock();
+ if(eventqueue.empty())
+ {
+ m_events.unlock();
+ continue;
+ }
- cevent_t e = eventqueue.front();
- eventqueue.pop_front();
- m_events.unlock();
+ cevent_t e = eventqueue.front();
+ eventqueue.pop_front();
+ m_events.unlock();
- // TODO: Skip event if e.pos < cache.pos
- // if(!e.active) continue;
+ // TODO: Skip event if e.pos < cache.pos
+ // if(!e.active) continue;
- handleEvent(e);
- }
+ handleEvent(e);
+ }
}
void CacheManager::pushEvent(cevent_t& e)
{
- if(!threaded) {
- handleEvent(e);
- return;
- }
-
- {
- MutexAutolock l(m_events);
-
- bool found = false;
-
- if(e.cmd == LOADNEXT) {
- for(auto it = eventqueue.begin(); it != eventqueue.end(); ++it) {
- auto& event = *it;
- if((event.cmd == LOADNEXT) &&
- (e.afile->filename == event.afile->filename) &&
- (e.pos == event.pos)) {
- // Append channel and buffer to the existing event.
- event.channels.insert(event.channels.end(), e.channels.begin(), e.channels.end());
- found = true;
- break;
- }
- }
- }
-
- if(!found) {
- // The event was not already on the list, create a new one.
- eventqueue.push_back(e);
- }
- }
-
- sem.post();
+ if(!threaded)
+ {
+ handleEvent(e);
+ return;
+ }
+
+ {
+ MutexAutolock l(m_events);
+
+ bool found = false;
+
+ if(e.cmd == LOADNEXT)
+ {
+ for(auto it = eventqueue.begin(); it != eventqueue.end(); ++it)
+ {
+ auto& event = *it;
+ if((event.cmd == LOADNEXT) &&
+ (e.afile->getFilename() == event.afile->getFilename()) &&
+ (e.pos == event.pos))
+ {
+ // Append channel and buffer to the existing event.
+ event.channels.insert(event.channels.end(), e.channels.begin(),
+ e.channels.end());
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if(!found)
+ {
+ // The event was not already on the list, create a new one.
+ eventqueue.push_back(e);
+ }
+ }
+
+ sem.post();
}
CacheManager::cevent_t
-CacheManager::createLoadNextEvent(AFile *afile, size_t channel, size_t pos,
- sample_t* buffer, volatile bool* ready)
+CacheManager::createLoadNextEvent(CacheAudioFile *afile, size_t channel,
+ size_t pos, sample_t* buffer,
+ volatile bool* ready)
{
- cevent_t e;
- e.cmd = LOADNEXT;
- e.pos = pos;
- e.afile = afile;
+ cevent_t e;
+ e.cmd = LOADNEXT;
+ e.pos = pos;
+ e.afile = afile;
- CacheManager::Channel c;
- c.channel = channel;
- c.samples = buffer;
+ CacheChannel c;
+ c.channel = channel;
+ c.samples = buffer;
- *ready = false;
- c.ready = ready;
+ *ready = false;
+ c.ready = ready;
- e.channels.insert(e.channels.end(), c);
+ e.channels.insert(e.channels.end(), c);
- return e;
+ return e;
}
CacheManager::cevent_t
CacheManager::createCloseEvent(cacheid_t id)
{
- cevent_t e;
- e.cmd = CLOSE;
- e.id = id;
- return e;
+ cevent_t e;
+ e.cmd = CLOSE;
+ e.id = id;
+ return e;
}
diff --git a/src/cachemanager.h b/src/cachemanager.h
index 1660ecf..a43d19a 100644
--- a/src/cachemanager.h
+++ b/src/cachemanager.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_CACHEMANAGER_H__
-#define __DRUMGIZMO_CACHEMANAGER_H__
+#pragma once
#include <string>
#include <list>
@@ -37,6 +36,7 @@
#include "audiotypes.h"
#include "audiofile.h"
+#include "cacheaudiofile.h"
#define CACHE_DUMMYID -2
#define CACHE_NOID -1
@@ -44,9 +44,20 @@
#define CHUNK_MULTIPLIER 16
class AudioFile;
+class CacheAudioFile;
+class CacheAudioFiles;
+
typedef int cacheid_t;
-class AFile;
+class CacheChannel {
+public:
+ size_t channel;
+ sample_t* samples;
+ size_t num_samples;
+ volatile bool* ready;
+};
+
+class CacheChannels : public std::list<CacheChannel> {};
//TODO:
// 1: Move nodata initialisation to init method.
@@ -72,146 +83,139 @@ class AFile;
class CacheManager : public Thread {
public:
- /**
- * Empty constructor...
- */
- CacheManager();
-
- /**
- * Destroy object and stop thread if needed.
- */
- ~CacheManager();
-
- /**
- * 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, bool threaded);
-
- /**
- * 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);
-
- /**
- * Unregister cache entry.
- * Close associated file handles and free associated buffers.
- * @param id The cache id to close.
- */
- void close(cacheid_t id);
-
- /**
- * Set internal framesize used when iterating through cache buffers.
- */
- void setFrameSize(size_t framesize);
-
- /**
- * Control reader thread.
- * Set to true to make reading happen threaded, false to do all reading sync.
- */
- void setAsyncMode(bool async);
-
- ///! Internal thread main method - needs to be public.
- void thread_main();
-
- class Channel {
- public:
- size_t channel;
- sample_t* samples;
- size_t num_samples;
- volatile bool* ready;
- };
+ /**
+ * Empty constructor...
+ */
+ CacheManager();
+
+ /**
+ * Destroy object and stop thread if needed.
+ */
+ ~CacheManager();
+
+ /**
+ * 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, bool threaded);
+
+ /**
+ * 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);
+
+ /**
+ * Unregister cache entry.
+ * Close associated file handles and free associated buffers.
+ * @param id The cache id to close.
+ */
+ void close(cacheid_t id);
+
+ /**
+ * Set internal framesize used when iterating through cache buffers.
+ */
+ void setFrameSize(size_t framesize);
+
+ /**
+ * Control reader thread.
+ * Set to true to make reading happen threaded, false to do all reading sync.
+ */
+ void setAsyncMode(bool async);
private:
- size_t framesize;
- sample_t *nodata;
+ ///! Internal thread main method
+ void thread_main();
- typedef struct {
- AFile *afile;
- size_t channel;
- size_t pos; //< File possition
- volatile bool ready;
- sample_t *front;
- sample_t *back;
- size_t localpos; //< Intra buffer (front) position.
+ size_t framesize;
+ sample_t *nodata;
- sample_t* preloaded_samples; // NULL means not active.
- size_t preloaded_samples_size;
+ typedef struct {
+ CacheAudioFile* afile;
+ size_t channel;
+ size_t pos; //< File possition
+ volatile bool ready;
+ sample_t *front;
+ sample_t *back;
+ size_t localpos; //< Intra buffer (front) position.
- } cache_t;
+ sample_t* preloaded_samples; // NULL means not active.
+ size_t preloaded_samples_size;
- typedef enum {
- LOADNEXT = 0,
- CLOSE = 1
- } cmd_t;
+ } cache_t;
- typedef struct {
- cmd_t cmd;
+ typedef enum {
+ LOADNEXT = 0,
+ CLOSE = 1
+ } cmd_t;
- // For close event:
- cacheid_t id;
+ typedef struct {
+ cmd_t cmd;
- // For load next event:
- size_t pos;
- AFile *afile;
- std::list<CacheManager::Channel> channels;
- } cevent_t;
+ // For close event:
+ cacheid_t id;
- cevent_t createLoadNextEvent(AFile *afile, size_t channel, size_t pos,
- sample_t* buffer, volatile bool* ready);
- cevent_t createCloseEvent(cacheid_t id);
+ // For load next event:
+ size_t pos;
+ CacheAudioFile* afile;
+ CacheChannels channels;
+ } cevent_t;
- void handleLoadNextEvent(cevent_t& e);
- void handleCloseEvent(cevent_t& e);
+ cevent_t createLoadNextEvent(CacheAudioFile* afile, size_t channel,
+ size_t pos, sample_t* buffer,
+ volatile bool* ready);
+ cevent_t createCloseEvent(cacheid_t id);
- void handleEvent(cevent_t& e);
- void pushEvent(cevent_t& e);
+ void handleLoadNextEvent(cevent_t& e);
+ void handleCloseEvent(cevent_t& e);
+ void handleCloseCache(cacheid_t& cacheid);
- std::vector<cache_t> id2cache;
+ void handleEvent(cevent_t& e);
+ void pushEvent(cevent_t& e);
- // Protected by mutex:
- std::list<cevent_t> eventqueue;
- std::list<cacheid_t> availableids;
-
- Mutex m_events;
- Mutex m_ids;
+ std::vector<cache_t> id2cache;
- bool threaded; // Indicates if we are running in thread mode or offline mode.
- Semaphore sem;
- Semaphore sem_run;
- bool running;
+ // Protected by mutex:
+ std::list<cevent_t> eventqueue;
+ std::list<cacheid_t> availableids;
- std::map<std::string, AFile*> files;
-};
+ Mutex m_events;
+ Mutex m_ids;
+
+ bool threaded; // Indicates if we are running in thread mode or offline mode.
+ Semaphore sem;
+ Semaphore sem_run;
+ bool running;
-#endif/*__DRUMGIZMO_CACHEMANAGER_H__*/
+ CacheAudioFiles files;
+ //std::map<std::string, CacheAudioFile*> files;
+};
diff --git a/vst/Makefile.mingw32.in b/vst/Makefile.mingw32.in
index 089e869..5a087b7 100644
--- a/vst/Makefile.mingw32.in
+++ b/vst/Makefile.mingw32.in
@@ -54,6 +54,8 @@ GUI_SRC = \
@top_srcdir@/plugingui/lineedit.cc \
@top_srcdir@/plugingui/led.cc \
@top_srcdir@/plugingui/layout.cc \
+ @top_srcdir@/plugingui/cacheaudiofile.cc \
+ @top_srcdir@/plugingui/cachemanager.cc \
@top_srcdir@/plugingui/checkbox.cc \
@top_srcdir@/plugingui/slider.cc \
@top_srcdir@/plugingui/scrollbar.cc \