diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am.drumgizmo | 6 | ||||
| -rw-r--r-- | src/audiocache.cc | 274 | ||||
| -rw-r--r-- | src/audiocache.h | 136 | ||||
| -rw-r--r-- | src/audiocacheeventhandler.cc | 315 | ||||
| -rw-r--r-- | src/audiocacheeventhandler.h | 109 | ||||
| -rw-r--r-- | src/audiocachefile.cc (renamed from src/cacheaudiofile.cc) | 58 | ||||
| -rw-r--r-- | src/audiocachefile.h | 95 | ||||
| -rw-r--r-- | src/audiocacheidmanager.cc | 128 | ||||
| -rw-r--r-- | src/audiocacheidmanager.h | 88 | ||||
| -rw-r--r-- | src/cacheaudiofile.h | 63 | ||||
| -rw-r--r-- | src/cachemanager.cc | 438 | ||||
| -rw-r--r-- | src/cachemanager.h | 221 | ||||
| -rw-r--r-- | src/drumgizmo.cc | 16 | ||||
| -rw-r--r-- | src/drumgizmo.h | 4 | ||||
| -rw-r--r-- | src/drumkitloader.cc | 1 | ||||
| -rw-r--r-- | src/events.h | 2 | 
16 files changed, 1194 insertions, 760 deletions
| diff --git a/src/Makefile.am.drumgizmo b/src/Makefile.am.drumgizmo index 54f830f..57b6362 100644 --- a/src/Makefile.am.drumgizmo +++ b/src/Makefile.am.drumgizmo @@ -1,11 +1,13 @@  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 \  	$(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/configuration.cc \  	$(top_srcdir)/src/configparser.cc \ diff --git a/src/audiocache.cc b/src/audiocache.cc new file mode 100644 index 0000000..1a2225e --- /dev/null +++ b/src/audiocache.cc @@ -0,0 +1,274 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + *            cachemanager.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() +{ + +	// TODO: Run through all active cacheids and release them/close their files. + +	deinit(); +	delete[] nodata; +} + +void AudioCache::init(size_t poolsize) +{ +	setAsyncMode(true); + +	idManager.init(poolsize); +	eventHandler.start(); +} + +void AudioCache::deinit() +{ +	eventHandler.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 = idManager.registerID({}); + +	// If we are out of availabel 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 = idManager.getCache(id); + +	c.afile = &eventHandler.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)]; +		} + +		eventHandler.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 = idManager.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. +		++numberOfUnderruns; +		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)]; +		} + +		eventHandler.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 = idManager.getCache(id); +	return cache.ready; +} + +void AudioCache::close(cacheid_t id) +{ +	if(id == CACHE_DUMMYID) +	{ +		return; +	} + +	eventHandler.pushCloseEvent(id); +} + +void AudioCache::setFrameSize(size_t framesize) +{ +	// Make sure the event handler thread is stalled while we set the framesize +	// state. +	std::lock_guard<AudioCacheEventHandler> eventHandlerLock(eventHandler); + +	// NOTE: Not threaded... +	//std::lock_guard<AudioCacheIDManager> idManagerLock(idManager); + +	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; + +	eventHandler.setChunkSize(CHUNKSIZE(framesize)); +} + +size_t AudioCache::frameSize() const +{ +	return framesize; +} + +void AudioCache::setAsyncMode(bool async) +{ +	// TODO: Clean out read queue. +	// TODO: Block until reader thread is idle, otherwise we might screw up the +	// buffers...? +	eventHandler.setThreaded(async); +} + +bool AudioCache::asyncMode() const +{ +	return eventHandler.getThreaded(); +} + +size_t AudioCache::getNumberOfUnderruns() const +{ +	return numberOfUnderruns; +} + +void AudioCache::resetNumberOfUnderruns() +{ +	numberOfUnderruns = 0; +} diff --git a/src/audiocache.h b/src/audiocache.h new file mode 100644 index 0000000..824db3a --- /dev/null +++ b/src/audiocache.h @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + *            cachemanager.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 + + +//TODO: +// 1: Move nodata initialisation to init method. +// 2: Make semaphore in thread to block init call until thread has been started. + +//// next +// Pre: preloaded contains 2 x framesize. chunk size is framesize. +// allocate 2 chunks and copy initial_samples_needed to first buffer from +// preloaded data and enough to fill up the second buffer from preloaded +// returns the first buffer and its size in &size. +// get id from "free stack" and store pointers to buffers in id vector. +// event: open sndfile handle (if not already open) and increase refcount + +//// next +// Return which ever buffer is the front, swap them and add event to load the +// next chunk. + +//// close +// decrement file handle refcounter and close file if it is 0. +// free the 2 buffers +// (do not erase from the id vector), push index to +// "free stack" for reuse. + +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 form 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 numberOfUnderruns{0}; + +	AudioCacheIDManager idManager; +	AudioCacheEventHandler eventHandler{idManager}; +}; diff --git a/src/audiocacheeventhandler.cc b/src/audiocacheeventhandler.cc new file mode 100644 index 0000000..598cc15 --- /dev/null +++ b/src/audiocacheeventhandler.cc @@ -0,0 +1,315 @@ +/* -*- 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 "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& idManager) +	// Hack to be able to forward declare CacheEvent: +	: eventqueue(new std::list<CacheEvent>()) +	, idManager(idManager) +{ +} + +AudioCacheEventHandler::~AudioCacheEventHandler() +{ +	// Close all ids already enqueued to be closed. +	clearEvents(); + +	auto activeIDs = idManager.getActiveIDs(); +	for(auto id : activeIDs) +	{ +		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; +	if(threaded) +	{ +		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 e; +	e.eventType = EventType::LoadNext; +	e.pos = pos; +	e.afile = afile; + +	CacheChannel c; +	c.channel = channel; +	c.samples = buffer; + +	*ready = false; +	c.ready = ready; + +	e.channels.insert(e.channels.end(), c); + +	pushEvent(e); +} + +void AudioCacheEventHandler::pushCloseEvent(cacheid_t id) +{ +	CacheEvent e; +	e.eventType = EventType::Close; +	e.id = id; + +	pushEvent(e); +} + +void AudioCacheEventHandler::setChunkSize(size_t chunksize) +{ +	if(this->chunksize == chunksize) +	{ +		return; +	} + +	// Remove all events from event queue. +	clearEvents(); + +	// Skip all active cacheids and make their buffers point at nodata. +	idManager.disableActive(); + +	this->chunksize = chunksize; +} + +size_t AudioCacheEventHandler::chunkSize() +{ +	return chunksize; +} + +AudioCacheFile& AudioCacheEventHandler::openFile(const std::string& filename) +{ +	std::lock_guard<std::mutex> l(mutex); +	return files.getFile(filename); +} + +void AudioCacheEventHandler::clearEvents() +{ +	std::lock_guard<std::mutex> l(mutex); + +	// Iterate all events ignoring load events and handling close events. +	for(auto& event : *eventqueue) +	{ +		if(event.eventType == EventType::Close) +		{ +			handleCloseEvent(event); +		} +	} + +	eventqueue->clear(); +} + +void AudioCacheEventHandler::handleLoadNextEvent(CacheEvent& event) +{ +	event.afile->readChunk(event.channels, event.pos, chunksize); +} + +void AudioCacheEventHandler::handleCloseEvent(CacheEvent& e) +{ +	handleCloseCache(e.id); +} + +void AudioCacheEventHandler::handleCloseCache(cacheid_t cacheid) +{ +	auto& cache = idManager.getCache(cacheid); +	{ +		std::lock_guard<std::mutex> l(mutex); + +		files.releaseFile(cache.afile->getFilename()); +	} + +	delete[] cache.front; +	delete[] cache.back; + +	idManager.releaseID(cacheid); +} + +void AudioCacheEventHandler::handleEvent(CacheEvent& e) +{ +	switch(e.eventType) +	{ +	case EventType::LoadNext: +		handleLoadNextEvent(e); +		break; +	case EventType::Close: +		handleCloseEvent(e); +		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 e = eventqueue->front(); +		eventqueue->pop_front(); +		mutex.unlock(); + +		// TODO: Skip event if e.pos < cache.pos +		//if(!e.active) +		//{ +		//	continue; +		//} + +		handleEvent(e); +	} +} + +void AudioCacheEventHandler::pushEvent(CacheEvent& event) +{ +	if(!threaded) +	{ +		handleEvent(event); +		return; +	} + +	{ +		std::lock_guard<std::mutex> l(mutex); + +		bool found = false; + +		if(event.eventType == EventType::LoadNext) +		{ +			for(auto& queuedEvent : *eventqueue) +			{ +				if((queuedEvent.eventType == EventType::LoadNext) && +				   (event.afile->getFilename() == queuedEvent.afile->getFilename()) && +				   (event.pos == queuedEvent.pos)) +				{ +					// Append channel and buffer to the existing event. +					queuedEvent.channels.insert(queuedEvent.channels.end(), +					                            event.channels.begin(), +					                            event.channels.end()); +					found = true; +					break; +				} +			} +		} + +		if(!found) +		{ +			// The event was not already on the list, create a new one. +			eventqueue->push_back(event); +		} +	} + +	sem.post(); +} diff --git a/src/audiocacheeventhandler.h b/src/audiocacheeventhandler.h new file mode 100644 index 0000000..014cc00 --- /dev/null +++ b/src/audiocacheeventhandler.h @@ -0,0 +1,109 @@ +/* -*- 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 "audiocachefile.h" +#include "audiocacheidmanager.h" + +class CacheEvent; + +class AudioCacheEventHandler +	: protected Thread +{ +public: +	AudioCacheEventHandler(AudioCacheIDManager& idManager); +	~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: +	AudioCacheFiles files; + +	std::mutex mutex; + +	void clearEvents(); + +	void handleLoadNextEvent(CacheEvent& e); +	void handleCloseEvent(CacheEvent& e); +	void handleCloseCache(cacheid_t cacheid); + +	void handleEvent(CacheEvent& e); + +	// From Thread +	void thread_main() override; + +	void pushEvent(CacheEvent& e); + +	std::list<CacheEvent>* eventqueue; + +	bool threaded{false}; +	Semaphore sem; +	Semaphore sem_run; +	bool running{false}; + +	AudioCacheIDManager& idManager; + +	size_t chunksize{1024}; +}; diff --git a/src/cacheaudiofile.cc b/src/audiocachefile.cc index 0d4aec9..c1fdd7b 100644 --- a/src/cacheaudiofile.cc +++ b/src/audiocachefile.cc @@ -24,17 +24,15 @@   *  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 "audiocachefile.h"  #include <assert.h>  #include <hugin.hpp> -#include <audiotypes.h> +#include "audiocache.h" -#include "cachemanager.h" - -CacheAudioFile::CacheAudioFile(const std::string& filename) +AudioCacheFile::AudioCacheFile(const std::string& filename)  	: filename(filename)  {  	fh = sf_open(filename.c_str(), SFM_READ, &sf_info); @@ -43,9 +41,14 @@ CacheAudioFile::CacheAudioFile(const std::string& filename)  		ERR(audiofile,"SNDFILE Error (%s): %s\n",  		    filename.c_str(), sf_strerror(fh));  	} + +	if(sf_info.frames == 0) +	{ +		printf("sf_info.frames == 0\n"); +	}  } -CacheAudioFile::~CacheAudioFile() +AudioCacheFile::~AudioCacheFile()  {  	if(fh)  	{ @@ -54,33 +57,33 @@ CacheAudioFile::~CacheAudioFile()  	}  } -size_t CacheAudioFile::getSize() const +size_t AudioCacheFile::getSize() const  {  	return sf_info.frames;  } -const std::string& CacheAudioFile::getFilename() const +const std::string& AudioCacheFile::getFilename() const  {  	return filename;  } -void CacheAudioFile::readChunk(const CacheChannels& channels, +size_t AudioCacheFile::getChannelCount() +{ +	return sf_info.channels; +} + +void AudioCacheFile::readChunk(const CacheChannels& channels,                                 size_t pos, size_t num_samples)  { -	if(fh == nullptr) -	{ -		printf("File handle is null.\n"); -		return; -	} +	assert(fh != nullptr); // File handle must never be nullptr  	if(pos > sf_info.frames)  	{ -		printf("pos > sf_info.frames\n"); +		printf("pos (%d) > sf_info.frames (%d)\n", pos, (int)sf_info.frames);  		return;  	} -	sf_seek(fh, pos, -	        SEEK_SET); +	sf_seek(fh, pos, SEEK_SET);  	size_t size = sf_info.frames - pos;  	if(size > num_samples) @@ -118,28 +121,35 @@ void CacheAudioFile::readChunk(const CacheChannels& channels,  	}  } -CacheAudioFile& CacheAudioFiles::getAudioFile(const std::string filename) +AudioCacheFile& AudioCacheFiles::getFile(const std::string& filename)  { +	AudioCacheFile* cacheAudioFile = nullptr; +  	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); +		cacheAudioFile = new AudioCacheFile(filename); +		audiofiles.insert(std::make_pair(filename, cacheAudioFile)); +	} +	else +	{ +		cacheAudioFile = it->second;  	} -	auto afile = it->second; +	assert(cacheAudioFile);  	// Increase ref count. -	++afile->ref; +	++cacheAudioFile->ref; -	return *afile; +	return *cacheAudioFile;  } -void CacheAudioFiles::release(const std::string filename) +void AudioCacheFiles::releaseFile(const std::string& filename)  {  	auto it = audiofiles.find(filename);  	if(it == audiofiles.end())  	{ +		assert(false); // This should never happen!  		return; // not open  	} diff --git a/src/audiocachefile.h b/src/audiocachefile.h new file mode 100644 index 0000000..2ed1d47 --- /dev/null +++ b/src/audiocachefile.h @@ -0,0 +1,95 @@ +/* -*- 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 <sndfile.h> + +#include <audiotypes.h> + +class CacheChannel { +public: +	size_t channel; +	sample_t* samples; +	size_t num_samples; +	volatile bool* ready; +}; + +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..5d4248b --- /dev/null +++ b/src/audiocacheidmanager.cc @@ -0,0 +1,128 @@ +/* -*- 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() +{ +} + +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 < 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> activeIDs; + +	for(auto& cache : id2cache) +	{ +		if(cache.id != CACHE_NOID) +		{ +			activeIDs.push_back(cache.id); +		} +	} + +	return activeIDs; +} diff --git a/src/audiocacheidmanager.h b/src/audiocacheidmanager.h new file mode 100644 index 0000000..7f203c9 --- /dev/null +++ b/src/audiocacheidmanager.h @@ -0,0 +1,88 @@ +/* -*- 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> + +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(); +	~AudioCacheIDManager(); + +	void init(unsigned int capacity); + +	// #thread safe +	// #hard real-time safe +	cache_t& getCache(cacheid_t id); + +	// Thread safe +	// Real-time safe +	cacheid_t registerID(const cache_t& cache); + +	// Thread safe +	// Real-time safe +	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/cacheaudiofile.h b/src/cacheaudiofile.h deleted file mode 100644 index 4f78247..0000000 --- a/src/cacheaudiofile.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -*- 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 deleted file mode 100644 index d394d8c..0000000 --- a/src/cachemanager.cc +++ /dev/null @@ -1,438 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/*************************************************************************** - *            cachemanager.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 "cachemanager.h" - -#include <string.h> -#include <stdio.h> -#include <assert.h> - -#include <hugin.hpp> - -#include "cacheaudiofile.h" - -#define CHUNKSIZE(x) (x * CHUNK_MULTIPLIER) - - -CacheManager::CacheManager() -	: framesize(0) -	, nodata(nullptr) -{ -} - -CacheManager::~CacheManager() -{ -	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(); -	} -} - -void CacheManager::deinit() -{ -	if(!running) return; -	running = false; -	if(threaded) -	{ -		sem.post(); -		wait_stop(); -	} -} - -// 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; -	} - -	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) -{ -	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; -	} - -	cevent_t e = createCloseEvent(id); -	pushEvent(e); -} - -void CacheManager::setFrameSize(size_t framesize) -{ -	MutexAutolock le(m_events); -	MutexAutolock li(m_ids); - -	if(framesize > this->framesize) -	{ -		delete[] nodata; -		nodata = new sample_t[framesize]; - -		for(size_t i = 0; i < framesize; ++i) -		{ -			nodata[i] = 0.0f; -		} -	} - -	// Run through all active cache_ts and disable them. -	for(auto it = id2cache.begin(); it != id2cache.end(); ++it) -	{ -		cacheid_t cacheid = it - id2cache.begin(); - -		// Look up id in available ids: -		bool found = false; -		for(auto i = availableids.begin(); i != availableids.end(); ++i) -		{ -			if(cacheid == *i) -			{ -				found = true; -				break; -			} -		} - -		if(!found) -		{ -			// Force use of nodata in all of the rest of the next() calls. -			it->localpos = CHUNKSIZE(framesize); -			it->ready = false; -		} -	} - -	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; -} - -void CacheManager::handleLoadNextEvent(cevent_t &event) -{ -	event.afile->readChunk(event.channels, event.pos, CHUNKSIZE(framesize)); -} - -void CacheManager::handleCloseEvent(cevent_t &e) -{ -	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; -	} -} - -void CacheManager::thread_main() -{ -	sem_run.post(); // Signal that the thread has been started - -	while(running) -	{ -		sem.wait(); - -		m_events.lock(); -		if(eventqueue.empty()) -		{ -			m_events.unlock(); -			continue; -		} - -		cevent_t e = eventqueue.front(); -		eventqueue.pop_front(); -		m_events.unlock(); - -		// TODO: Skip event if e.pos < cache.pos -		// if(!e.active) continue; - -		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->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(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; - -	CacheChannel c; -	c.channel = channel; -	c.samples = buffer; - -	*ready = false; -	c.ready = ready; - -	e.channels.insert(e.channels.end(), c); - -	return e; -} - -CacheManager::cevent_t -CacheManager::createCloseEvent(cacheid_t id) -{ -	cevent_t e; -	e.cmd = CLOSE; -	e.id = id; -	return e; -} diff --git a/src/cachemanager.h b/src/cachemanager.h deleted file mode 100644 index a43d19a..0000000 --- a/src/cachemanager.h +++ /dev/null @@ -1,221 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/*************************************************************************** - *            cachemanager.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 "thread.h" -#include "semaphore.h" -#include "mutex.h" - -#include "audiotypes.h" -#include "audiofile.h" -#include "cacheaudiofile.h" - -#define CACHE_DUMMYID -2 -#define CACHE_NOID -1 - -#define CHUNK_MULTIPLIER 16 - -class AudioFile; -class CacheAudioFile; -class CacheAudioFiles; - -typedef int cacheid_t; - -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. -// 2: Make semaphore in thread to block init call until thread has been started. - -//// next -// Pre: preloaded contains 2 x framesize. chunk size is framesize. -// allocate 2 chunks and copy initial_samples_needed to first buffer from -// preloaded data and enough to fill up the second buffer from preloaded -// returns the first buffer and its size in &size. -// get id from "free stack" and store pointers to buffers in id vector. -// event: open sndfile handle (if not already open) and increase refcount - -//// next -// Return which ever buffer is the front, swap them and add event to load the -// next chunk. - -//// close -// decrement file handle refcounter and close file if it is 0. -// free the 2 buffers -// (do not erase from the id vector), push index to -// "free stack" for reuse. - -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); - -private: -	///! Internal thread main method -	void thread_main(); - -	size_t framesize; -	sample_t *nodata; - -	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. - -		sample_t* preloaded_samples; // NULL means not active. -		size_t preloaded_samples_size; - -	} cache_t; - -	typedef enum { -		LOADNEXT = 0, -		CLOSE = 1 -	} cmd_t; - -	typedef struct { -		cmd_t cmd; - -		// For close event: -		cacheid_t id; - -		// For load next event: -		size_t pos; -		CacheAudioFile* afile; -		CacheChannels channels; -	} cevent_t; - -	cevent_t createLoadNextEvent(CacheAudioFile* afile, size_t channel, -	                             size_t pos, sample_t* buffer, -	                             volatile bool* ready); -	cevent_t createCloseEvent(cacheid_t id); - -	void handleLoadNextEvent(cevent_t& e); -	void handleCloseEvent(cevent_t& e); -	void handleCloseCache(cacheid_t& cacheid); - -	void handleEvent(cevent_t& e); -	void pushEvent(cevent_t& e); - -	std::vector<cache_t> id2cache; - -	// Protected by mutex: -	std::list<cevent_t> eventqueue; -	std::list<cacheid_t> availableids; - -	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; - -	CacheAudioFiles files; -	//std::map<std::string, CacheAudioFile*> files; -}; diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index 5348324..00e93d1 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -54,12 +54,12 @@ DrumGizmo::DrumGizmo(AudioOutputEngine *o, AudioInputEngine *i)    , freewheel(false)  {    is_stopping = false; -  cacheManager.init(1000, true); // start thread +  audioCache.init(1000); // start thread  }  DrumGizmo::~DrumGizmo()  { -  cacheManager.deinit(); // stop thread +  audioCache.deinit(); // stop thread  }  bool DrumGizmo::loadkit(std::string file) @@ -185,8 +185,8 @@ void DrumGizmo::setFrameSize(size_t framesize)      // Update framesize in drumkitloader and cachemanager:      loader.setFrameSize(framesize);      printf("loader.setFrameSize\n"); fflush(stdout); -    cacheManager.setFrameSize(framesize); -    printf("cacheManager.setFrameSize\n"); fflush(stdout); +    audioCache.setFrameSize(framesize); +    printf("audioCache.setFrameSize\n"); fflush(stdout);    }  } @@ -196,7 +196,7 @@ void DrumGizmo::setFreeWheel(bool freewheel)    // than realtime.    if(freewheel != this->freewheel) {      this->freewheel = freewheel; -    cacheManager.setAsyncMode(!freewheel); +    audioCache.setAsyncMode(!freewheel);    }  } @@ -424,7 +424,7 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz)          if(evt->cache_id == CACHE_NOID) {            size_t initial_chunksize = (pos + sz) - evt->offset; -          evt->buffer = cacheManager.open(af, initial_chunksize, +          evt->buffer = audioCache.open(af, initial_chunksize,                                            af->filechannel, evt->cache_id);            evt->buffer_size = initial_chunksize;          } @@ -484,13 +484,13 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz)          evt->t += evt->buffer_size; // Add internal buffer counter to "global" event counter.          if((evt->t < af->size) && (evt->rampdown != 0)) { -          evt->buffer = cacheManager.next(evt->cache_id, evt->buffer_size); +          evt->buffer = audioCache.next(evt->cache_id, evt->buffer_size);          } else {            removeevent = true;          }          if(removeevent) { -          cacheManager.close(evt->cache_id); +          audioCache.close(evt->cache_id);          }          }        } diff --git a/src/drumgizmo.h b/src/drumgizmo.h index 041ca35..1e38e08 100644 --- a/src/drumgizmo.h +++ b/src/drumgizmo.h @@ -37,7 +37,7 @@  #include "drumkit.h"  #include "drumkitloader.h" -#include "cachemanager.h" +#include "audiocache.h"  #include "mutex.h" @@ -96,7 +96,7 @@ private:    std::map<std::string, AudioFile *> audiofiles; -  CacheManager cacheManager; +  AudioCache audioCache;    DrumKit kit;    size_t framesize; diff --git a/src/drumkitloader.cc b/src/drumkitloader.cc index 413d3f4..203db04 100644 --- a/src/drumkitloader.cc +++ b/src/drumkitloader.cc @@ -30,7 +30,6 @@  #include "drumkitparser.h"  #include "drumgizmo.h" -#include "cachemanager.h"  DrumKitLoader::DrumKitLoader()    : semaphore("drumkitloader") diff --git a/src/events.h b/src/events.h index ea897f1..26eaf3f 100644 --- a/src/events.h +++ b/src/events.h @@ -35,7 +35,7 @@  #include "audiofile.h"  #include "audio.h"  #include "mutex.h" -#include "cachemanager.h" +#include "audiocache.h"  typedef unsigned int timepos_t; | 
