diff options
author | Bent Bisballe Nyeng <deva@aasimon.org> | 2015-12-24 17:49:09 +0100 |
---|---|---|
committer | Bent Bisballe Nyeng <deva@aasimon.org> | 2016-01-20 13:32:03 +0100 |
commit | 84e2cacec69c73712100de31585da3fe96c94704 (patch) | |
tree | 2e947348e26dabb487d05d6e1437635a18e5036c /src | |
parent | c3841a8cec9d239bca27a113c013a032dfc658bd (diff) |
Split internal CacheManager AFile class out into it's own file/class CacheAudioFile and improve interface.
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am.drumgizmo | 2 | ||||
-rw-r--r-- | src/cacheaudiofile.cc | 156 | ||||
-rw-r--r-- | src/cacheaudiofile.h | 63 | ||||
-rw-r--r-- | src/cachemanager.cc | 660 | ||||
-rw-r--r-- | src/cachemanager.h | 262 |
5 files changed, 657 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; +}; |