diff options
| -rw-r--r-- | src/Makefile.am.drumgizmo | 1 | ||||
| -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 | ||||
| -rw-r--r-- | vst/Makefile.mingw32.in | 2 | 
6 files changed, 658 insertions, 486 deletions
| diff --git a/src/Makefile.am.drumgizmo b/src/Makefile.am.drumgizmo index ae50497..54f830f 100644 --- a/src/Makefile.am.drumgizmo +++ b/src/Makefile.am.drumgizmo @@ -4,6 +4,7 @@ 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/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 54c42e0..804c97c 100644 --- a/vst/Makefile.mingw32.in +++ b/vst/Makefile.mingw32.in @@ -53,6 +53,8 @@ GUI_SRC = \  	@top_srcdir@/plugingui/pixelbuffer.cc \  	@top_srcdir@/plugingui/lineedit.cc \  	@top_srcdir@/plugingui/led.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 \ | 
