diff options
m--------- | hugin | 0 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/Makefile.am.drumgizmo | 3 | ||||
-rw-r--r-- | src/audiofile.cc | 20 | ||||
-rw-r--r-- | src/audiofile.h | 5 | ||||
-rw-r--r-- | src/cachemanager.cc | 326 | ||||
-rw-r--r-- | src/cachemanager.h | 192 | ||||
-rw-r--r-- | src/drumgizmo.cc | 56 | ||||
-rw-r--r-- | src/drumgizmo.h | 6 | ||||
-rw-r--r-- | src/drumkitloader.cc | 3 | ||||
-rw-r--r-- | src/events.h | 6 | ||||
-rw-r--r-- | test/Makefile.am | 18 | ||||
-rw-r--r-- | test/cachemanagertest.cc | 136 | ||||
-rw-r--r-- | test/kit/ride-multi-channel.wav | bin | 0 -> 28591492 bytes | |||
-rw-r--r-- | test/kit/ride-single-channel.wav | bin | 0 -> 694144 bytes |
15 files changed, 747 insertions, 28 deletions
diff --git a/hugin b/hugin -Subproject bb7388b685ed043b4a3030da86f7f1e49141477 +Subproject 7e734710be0098ea77ca2d3f54fb626b65bbf47 diff --git a/src/Makefile.am b/src/Makefile.am index df9f4ca..cb44909 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,6 +9,7 @@ EXTRA_DIST = \ channelmixer.h \ chresampler.h \ configuration.h \ + cachemanager.h \ configparser.h \ drumgizmo.h \ drumkit.h \ @@ -39,6 +40,7 @@ EXTRA_DIST = \ audioinputenginemidi.cc \ audiooutputengine.cc \ beatmapper.cc \ + cachemanager.cc \ channel.cc \ channelmixer.cc \ chresampler.cc \ @@ -63,4 +65,4 @@ EXTRA_DIST = \ semaphore.cc \ thread.cc \ velocity.cc \ - versionstr.cc
\ No newline at end of file + versionstr.cc diff --git a/src/Makefile.am.drumgizmo b/src/Makefile.am.drumgizmo index 1a3c857..ae50497 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/cachemanager.cc \ $(top_srcdir)/src/configfile.cc \ $(top_srcdir)/src/configuration.cc \ $(top_srcdir)/src/configparser.cc \ @@ -28,4 +29,4 @@ DRUMGIZMO_SOURCES = \ $(top_srcdir)/src/velocity.cc \ $(top_srcdir)/src/versionstr.cc -DRUMGIZMO_LIBS = $(ZITA_LIBS) $(SNDFILE_LIBS) $(EXPAT_LIBS) $(SAMPLERATE_LIBS)
\ No newline at end of file +DRUMGIZMO_LIBS = $(ZITA_LIBS) $(SNDFILE_LIBS) $(EXPAT_LIBS) $(SAMPLERATE_LIBS) diff --git a/src/audiofile.cc b/src/audiofile.cc index 59e0c14..7ab21a9 100644 --- a/src/audiofile.cc +++ b/src/audiofile.cc @@ -115,20 +115,20 @@ void AudioFile::load(int num_samples) } size = sf_info.frames; + preloadedsize = sf_info.frames; double ratio = (double)Conf::samplerate / (double)sf_info.samplerate; if(num_samples != ALL_SAMPLES) { // Make sure we read enough samples, even after conversion. num_samples /= ratio; - if((int)size > num_samples) size = num_samples; + if((int)preloadedsize > num_samples) preloadedsize = num_samples; } - sample_t* data = new sample_t[size]; + sample_t* data = new sample_t[preloadedsize]; if(sf_info.channels == 1) { - size = sf_read_float(fh, data, size); - } - else { + preloadedsize = sf_read_float(fh, data, preloadedsize); + } else { // check filechannel exists if(filechannel >= sf_info.channels) { filechannel = sf_info.channels - 1; @@ -139,15 +139,17 @@ void AudioFile::load(int num_samples) int read; do { read = sf_readf_float(fh, buffer, readsize); - for (int i = 0; i < read; i++) { + for (int i = 0; i < read && totalread < num_samples; i++) { data[totalread++] = buffer[i * sf_info.channels + filechannel]; } - } while(read > 0 && totalread < (int)size); + } while( (read > 0) && + (totalread < (int)preloadedsize) && + (totalread < num_samples) ); // set data size to total bytes read - size = totalread; + preloadedsize = totalread; } - DEBUG(audiofile,"Loaded %d samples %p\n", (int)size, this); + DEBUG(audiofile,"Loaded %d samples %p\n", (int)preloadedsize, this); sf_close(fh); diff --git a/src/audiofile.h b/src/audiofile.h index 98bf101..5f93584 100644 --- a/src/audiofile.h +++ b/src/audiofile.h @@ -79,8 +79,9 @@ public: bool isLoaded(); - volatile size_t size; - volatile sample_t *data; + volatile size_t size; // Full size of the file + volatile size_t preloadedsize; // Number of samples preloaded (in data) + sample_t *data; std::string filename; diff --git a/src/cachemanager.cc b/src/cachemanager.cc new file mode 100644 index 0000000..61fdb3e --- /dev/null +++ b/src/cachemanager.cc @@ -0,0 +1,326 @@ +/* -*- 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 <sndfile.h> + +#include <hugin.hpp> + +static sample_t nodata[FRAMESIZE]; + +#define BUFFER_SIZE 4092 + +static size_t readChunk(std::string filename, int filechannel, size_t pos, + size_t num_samples, sample_t* buf) +{ + SF_INFO sf_info; + SNDFILE *fh = sf_open(filename.c_str(), SFM_READ, &sf_info); + if(!fh) { + ERR(audiofile,"SNDFILE Error (%s): %s\n", + filename.c_str(), sf_strerror(fh)); + return 0; + } + + if(pos > sf_info.frames) { + return 0; + } + + sf_seek(fh, pos, SEEK_SET); + + size_t size = sf_info.frames - pos; + if(size > num_samples) size = num_samples; + + sample_t* data = buf; + if(sf_info.channels == 1) { + size = sf_readf_float(fh, data, size); + } else { + // check filechannel exists + if(filechannel >= sf_info.channels) { + filechannel = sf_info.channels - 1; + } + sample_t buffer[BUFFER_SIZE]; + int readsize = BUFFER_SIZE / sf_info.channels; + int totalread = 0; + int read; + do { + read = sf_readf_float(fh, buffer, readsize); + for (int i = 0; i < read && totalread < (int)size; i++) { + data[totalread++] = buffer[i * sf_info.channels + filechannel]; + } + } while(read > 0 && totalread < (int)size && totalread < sf_info.frames); + // set data size to total bytes read + size = totalread; + } + + sf_close(fh); + + return size; +} + +CacheManager::CacheManager() {} + +CacheManager::~CacheManager() +{ + deinit(); +} + +void CacheManager::init(size_t poolsize, bool threaded) +{ + this->threaded = threaded; + + for(size_t i = 0; i < FRAMESIZE; i++) { + nodata[i] = 0; + } + + 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 +// Proposal: preloaded > 2 x CHUNKSIZE? So that we can fill c.front immediatly on open +sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, + int channel, cacheid_t &id) +{ + { + MutexAutolock l(m_ids); + if(availableids.empty()) { + id = CACHE_DUMMYID; + } else { + id = availableids.front(); + availableids.pop_front(); + } + } + + if(id == CACHE_DUMMYID) { + return nodata; + } + + cache_t c; + c.file = file; + c.channel = channel; + c.pos = initial_samples_needed; + c.localpos = 0; + c.front = new sample_t[CHUNKSIZE]; + c.back = new sample_t[CHUNKSIZE]; + + size_t size = CHUNKSIZE; + if(size > (file->preloadedsize - c.pos)) size = (file->preloadedsize - c.pos); + memcpy(c.front, c.file->data + c.pos, size * sizeof(sample_t)); + c.ready = false; + c.pos += size; + + // Increase audio ref count + + { + MutexAutolock l(m_ids); + id2cache[id] = c; + } + + // Only load next buffer if there are more data in the file to be loaded... + if(c.pos < file->size) { + cevent_t e = + createLoadNextEvent(c.file, c.channel, c.pos, c.back); + e.ready = &id2cache[id].ready; + pushEvent(e); + } + + return file->data; // preloaded data +} + +sample_t *CacheManager::next(cacheid_t id, size_t &size) +{ + size = FRAMESIZE; + + if(id == CACHE_DUMMYID) { + return nodata; + } + + cache_t& c = id2cache[id]; + if(c.localpos < CHUNKSIZE) { + sample_t *s = c.front + c.localpos; + c.localpos += size; + return s; + } + + if(!c.ready) { + printf("#%d: NOT READY!\n", id); // TODO: Count and show in UI? + } + + // Swap buffers + sample_t *tmp = c.front; + c.front = c.back; + c.back = tmp; + + c.localpos = size; // Next time we go here we have already read the first frame. + + c.pos += CHUNKSIZE; + + if(c.pos < c.file->size) { + cevent_t e = createLoadNextEvent(c.file, c.channel, c.pos, c.back); + c.ready = false; + e.ready = &c.ready; + pushEvent(e); + } + + return c.front; +} + +void CacheManager::close(cacheid_t id) +{ + if(id == CACHE_DUMMYID) { + return; + } + + cevent_t e = createCloseEvent(id); + pushEvent(e); +} + +void CacheManager::handleLoadNextEvent(cevent_t &e) +{ +#if 0 // memcpy + size_t size = CHUNKSIZE; + if(size > (e.file->size - e.pos)) { + size = (e.file->size - e.pos); + } + memcpy(e.buffer, e.file->data + e.pos, size * sizeof(sample_t)); +#elif 1 // diskread + //memset(e.buffer, 0, CHUNKSIZE * sizeof(sample_t)); + readChunk(e.file->filename, e.channel, e.pos, CHUNKSIZE, e.buffer); +#endif + *e.ready = true; +} + +void CacheManager::handleCloseEvent(cevent_t &e) +{ + cache_t& c = id2cache[e.id]; + delete[] c.front; + delete[] c.back; + + { + MutexAutolock l(m_ids); + availableids.push_back(e.id); + } + + // TODO: Count down ref coutner on c.file and close it if 0. +} + + +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; + } + + // Check that if event should be merged (Maybe by event queue (ie. push + // in front). + { + MutexAutolock l(m_events); + eventqueue.push_back(e); + } + + sem.post(); +} + +CacheManager::cevent_t +CacheManager::createLoadNextEvent(AudioFile *file, size_t channel, size_t pos, + sample_t* buffer) +{ + cevent_t e; + e.cmd = LOADNEXT; + e.pos = pos; + e.buffer = buffer; + e.file = file; + e.channel = channel; + 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 new file mode 100644 index 0000000..f2c0122 --- /dev/null +++ b/src/cachemanager.h @@ -0,0 +1,192 @@ +/* -*- 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. + */ +#ifndef __DRUMGIZMO_CACHEMANAGER_H__ +#define __DRUMGIZMO_CACHEMANAGER_H__ + +#include <string> +#include <list> +#include <vector> + +#include "thread.h" +#include "semaphore.h" +#include "mutex.h" + +#include "audiotypes.h" +#include "audiofile.h" + +#define CACHE_DUMMYID -2 +#define CACHE_NOID -1 + +#define FRAMESIZE 2048 +#define CHUNKSIZE (FRAMESIZE * 16) +#define PRELOADSIZE (FRAMESIZE + CHUNKSIZE) + +class AudioFile; +typedef int cacheid_t; + + +//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); + + ///! Internal thread main method - needs to be public. + void thread_main(); + +private: + typedef struct { + AudioFile *file; + 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; + + 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; + sample_t *buffer; + volatile bool *ready; + AudioFile *file; + size_t channel; + } cevent_t; + + cevent_t createLoadNextEvent(AudioFile *file, size_t channel, size_t pos, + sample_t* buffer); + cevent_t createCloseEvent(cacheid_t id); + + void handleLoadNextEvent(cevent_t &e); + void handleCloseEvent(cevent_t &e); + + 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; +}; + +#endif/*__DRUMGIZMO_CACHEMANAGER_H__*/ diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index 7ce05ef..fb4f042 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -50,10 +50,12 @@ DrumGizmo::DrumGizmo(AudioOutputEngine *o, AudioInputEngine *i) loader(), oe(o), ie(i) { is_stopping = false; + cacheManager.init(1000, true); // start thread } DrumGizmo::~DrumGizmo() { + cacheManager.deinit(); // stop thread } bool DrumGizmo::loadkit(std::string file) @@ -398,43 +400,73 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) break; } + // Don't handle event now is is scheduled for a future iteration? + if(evt->offset > (pos + sz)) { + continue; + } + + size_t buffer_offset = 0; + + if(evt->cache_id == CACHE_NOID) { + size_t initial_chunksize = (pos + sz) - evt->offset; + evt->buffer = + cacheManager.open(af, initial_chunksize, ch, evt->cache_id); + evt->buffer_size = initial_chunksize; + } + else { + buffer_offset = evt->offset; + } + { MutexAutolock l(af->mutex); - size_t n = 0; + size_t n = 0; // default start point is 0. + + // If we are not at offset 0 in current buffer: if(evt->offset > (size_t)pos) n = evt->offset - pos; - size_t end = sz; + + size_t end = sz; // default end point is the end of the buffer. + + // Find the end point intra-buffer if((evt->t + end - n) > af->size) end = af->size - evt->t + n; + + // This should not be necessary but make absolutely shure that we do + // not write over the end of the buffer. if(end > sz) end = sz; + size_t t = 0; // Internal buffer counter if(evt->rampdown == NO_RAMPDOWN) { #ifdef SSE -// DEBUG(drumgizmo,"%d\n", evt->t); fflush(stdout); - size_t optend = ((end - n) / N) * N + n; - for(; n < optend; n += N) { - *(vNsf*)&(s[n]) += *(vNsf*)&(af->data[evt->t]); - evt->t += N; - } + size_t optend = ((end - n) / N) * N + n; + for(; n < optend; n += N) { + *(vNsf*)&(s[n]) += *(vNsf*)&(evt->buffer[t]); + t += N; + } #endif for(; n < end; n++) { - s[n] += af->data[evt->t]; - evt->t++; + s[n] += evt->buffer[t]; + t++; } } else { // Ramp down in progress. for(; n < end && evt->rampdown; n++) { float scale = (float)evt->rampdown/(float)evt->ramp_start; - s[n] += af->data[evt->t] * scale; - evt->t++; + s[n] += evt->buffer[t] * scale; + t++; evt->rampdown--; } if(evt->rampdown == 0) { removeevent = true; // Down ramp done. Remove event. + cacheManager.close(evt->cache_id); } } + evt->t += t; // Add internal buffer counter to "global" event counter. if(evt->t >= af->size) { removeevent = true; + cacheManager.close(evt->cache_id); + } else { + evt->buffer = cacheManager.next(evt->cache_id, evt->buffer_size); } } diff --git a/src/drumgizmo.h b/src/drumgizmo.h index 5e58ba5..a8f007a 100644 --- a/src/drumgizmo.h +++ b/src/drumgizmo.h @@ -38,6 +38,7 @@ #include "drumkit.h" #include "drumkitloader.h" +#include "cachemanager.h" #include "mutex.h" @@ -95,6 +96,11 @@ private: std::map<std::string, AudioFile *> audiofiles; + CacheManager cacheManager; + +#ifdef TEST_DRUMGIZMO +public: +#endif DrumKit kit; }; diff --git a/src/drumkitloader.cc b/src/drumkitloader.cc index bf01db6..2b66ae0 100644 --- a/src/drumkitloader.cc +++ b/src/drumkitloader.cc @@ -30,6 +30,7 @@ #include "drumkitparser.h" #include "drumgizmo.h" +#include "cachemanager.h" DrumKitLoader::DrumKitLoader() : semaphore("drumkitloader") @@ -136,7 +137,7 @@ void DrumKitLoader::thread_main() AudioFile *audiofile = load_queue.front(); load_queue.pop_front(); filename = audiofile->filename; - audiofile->load(); + audiofile->load(PRELOADSIZE); } loaded++; diff --git a/src/events.h b/src/events.h index fa0147b..ea897f1 100644 --- a/src/events.h +++ b/src/events.h @@ -35,6 +35,7 @@ #include "audiofile.h" #include "audio.h" #include "mutex.h" +#include "cachemanager.h" typedef unsigned int timepos_t; @@ -58,6 +59,7 @@ public: EventSample(channel_t c, float g, AudioFile *af, std::string grp, void *instr) { + cache_id = CACHE_NOID; channel = c; gain = g; t = 0; @@ -70,6 +72,10 @@ public: Event::type_t type() { return Event::sample; } + cacheid_t cache_id; + sample_t *buffer; + size_t buffer_size; + float gain; unsigned int t; AudioFile *file; diff --git a/test/Makefile.am b/test/Makefile.am index 5aaf33f..e5760c8 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,13 +1,27 @@ # Rules for the test code (use `make check` to execute) include $(top_srcdir)/src/Makefile.am.drumgizmo -TESTS = engine gui resampler lv2 +TESTS = engine gui resampler lv2 cachemanager check_PROGRAMS = $(TESTS) +cachemanager_CXXFLAGS = -DOUTPUT=\"cachemanager\" $(CPPUNIT_CFLAGS) \ + -I$(top_srcdir)/src -I$(top_srcdir)/include \ + -I$(top_srcdir)/hugin -DDISABLE_HUGIN $(PTHREAD_CFLAGS) $(SNDFILE_CFLAGS) +cachemanager_LDFLAGS = $(PTHREAD_LIBS) $(CPPUNIT_LIBS) $(SNDFILE_LIBS) +cachemanager_SOURCES = \ + $(top_srcdir)/src/cachemanager.cc \ + $(top_srcdir)/src/thread.cc \ + $(top_srcdir)/src/mutex.cc \ + $(top_srcdir)/src/semaphore.cc \ + $(top_srcdir)/src/configuration.cc \ + $(top_srcdir)/src/audiofile.cc \ + test.cc \ + cachemanagertest.cc + engine_CXXFLAGS = -DOUTPUT=\"engine\" $(CPPUNIT_CFLAGS) \ -I$(top_srcdir)/src -I$(top_srcdir)/include \ - -I$(top_srcdir)/hugin -DDISABLE_HUGIN + -I$(top_srcdir)/hugin -DDISABLE_HUGIN $(PTHREAD_CFLAGS) engine_CFLAGS = -DDISABLE_HUGIN engine_LDFLAGS = $(CPPUNIT_LIBS) $(DRUMGIZMO_LIBS) $(PTHREAD_LIBS) engine_SOURCES = \ diff --git a/test/cachemanagertest.cc b/test/cachemanagertest.cc new file mode 100644 index 0000000..ae346db --- /dev/null +++ b/test/cachemanagertest.cc @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * cachemanagertest.cc + * + * Sun Apr 19 10:15:59 CEST 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 <cppunit/extensions/HelperMacros.h> + +#include <cachemanager.h> +#include <unistd.h> + +class test_cachemanager : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(test_cachemanager); + CPPUNIT_TEST(singlechannel_nonthreaded); + CPPUNIT_TEST(singlechannel_threaded); + CPPUNIT_TEST(multichannel_nonthreaded); + CPPUNIT_TEST(multichannel_threaded); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp() {} + void tearDown() {} + + void testit(const char *filename, int channel, bool threaded) + { + + // Reference file: + AudioFile afref(filename, channel); + printf("afref.load\n"); + afref.load(ALL_SAMPLES); + + // Input file: + AudioFile af(filename, channel); + printf("af.load\n"); + af.load(ALL_SAMPLES); + //af.load(PRELOADSIZE); + + CacheManager cm; + printf("cm.init\n"); + cm.init(100, threaded); + + cacheid_t id; + // TODO: test 0 ... FRAMESIZE - 1 + size_t initial_samples_needed = (FRAMESIZE - 1) / 2; + + printf("open\n"); + sample_t *s = cm.open(&af, initial_samples_needed, channel, id); + size_t size = initial_samples_needed; + size_t offset = 0; + + // Test pre cache: + for(size_t i = 0; i < size; i++) { + CPPUNIT_ASSERT_EQUAL(afref.data[offset], s[i]); + offset++; + } + + // Test the rest + while(offset < afref.size) { + + if(threaded) { + usleep(1000000.0 / 44100.0 * FRAMESIZE); + //sleep(1); // sleep 1 second + } + + //printf("offset: %d\t", offset); + s = cm.next(id, size); + //printf("next -> size: %d\n", size); + for(size_t i = 0; i < size && (offset < afref.size); i++) { + CPPUNIT_ASSERT_EQUAL(afref.data[offset], s[i]); + offset++; + } + } + + printf("done\n"); + } + + void singlechannel_nonthreaded() + { + const char filename[] = "kit/ride-single-channel.wav"; + int channel = 0; + bool threaded = false; + testit(filename, channel, threaded); + } + + void singlechannel_threaded() + { + const char filename[] = "kit/ride-single-channel.wav"; + int channel = 0; + bool threaded = true; + testit(filename, channel, threaded); + } + + void multichannel_nonthreaded() + { + const char filename[] = "kit/ride-multi-channel.wav"; + int channel = 0; + bool threaded = false; + testit(filename, channel, threaded); + } + + void multichannel_threaded() + { + const char filename[] = "kit/ride-multi-channel.wav"; + int channel = 0; + bool threaded = true; + testit(filename, channel, threaded); + } + +}; + +// Registers the fixture into the 'registry' +CPPUNIT_TEST_SUITE_REGISTRATION(test_cachemanager); + + + diff --git a/test/kit/ride-multi-channel.wav b/test/kit/ride-multi-channel.wav Binary files differnew file mode 100644 index 0000000..3dec8a9 --- /dev/null +++ b/test/kit/ride-multi-channel.wav diff --git a/test/kit/ride-single-channel.wav b/test/kit/ride-single-channel.wav Binary files differnew file mode 100644 index 0000000..1760697 --- /dev/null +++ b/test/kit/ride-single-channel.wav |