From 189f156f231b9e06f6d4683c1fc951fe78b6d92f Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Fri, 10 Apr 2015 10:40:47 +0200 Subject: Initial design of cache manager. --- src/cachemanager.cc | 28 ++++++++++++++++++ src/cachemanager.h | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 src/cachemanager.cc create mode 100644 src/cachemanager.h diff --git a/src/cachemanager.cc b/src/cachemanager.cc new file mode 100644 index 0000000..2e9eab9 --- /dev/null +++ b/src/cachemanager.cc @@ -0,0 +1,28 @@ +/* -*- 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 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 "cachemanager.h" + diff --git a/src/cachemanager.h b/src/cachemanager.h new file mode 100644 index 0000000..ed89d12 --- /dev/null +++ b/src/cachemanager.h @@ -0,0 +1,84 @@ +/* -*- 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 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. + */ +#ifndef __DRUMGIZMO_CACHEMANAGER_H__ +#define __DRUMGIZMO_CACHEMANAGER_H__ + +#include +#include + +class AudioFile; + +class CacheManager { +public: + + class Request { + public: + std::string filename; + std::map targets; + size_t pos; + }; + + // 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 + sample_t *hello(AudioFile *file, int initial_samples_needed, int channel, id_t &new_id); + + // Return which ever buffer is the front, swap them and add event to load the + // next chunk. + sample_t *getNextBuffer(id_t id, size_t &size); + + // 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. + void goodbye(id_t id); + + // id vector: + // { + // AudioFile* + // channel + // buffer1, buffer2 + // position + // (ready?) + // } + +private: + class File { + public: + sndfile_t *handle; + int refcounter; + }; + + std::list requests; + std::map + +}; + +#endif/*__DRUMGIZMO_CACHEMANAGER_H__*/ -- cgit v1.2.3 From 96df5c64a03de57732eb61c3f65c4e17d14c6688 Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Fri, 10 Apr 2015 16:28:03 +0200 Subject: Added unit test shell for cachemanager. Added cache manager logic - still needing audio related code. --- src/Makefile.am | 4 +- src/Makefile.am.drumgizmo | 3 +- src/audiofile.h | 2 +- src/cachemanager.cc | 107 +++++++++++++++++++++++++++++++++++++++++++++- src/cachemanager.h | 71 ++++++++++++++++++++++-------- test/Makefile.am | 13 +++++- 6 files changed, 175 insertions(+), 25 deletions(-) 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 26e2a30..d8be9fc 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/configuration.cc \ $(top_srcdir)/src/configparser.cc \ $(top_srcdir)/src/drumgizmo.cc \ @@ -27,4 +28,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.h b/src/audiofile.h index 98bf101..5b57db8 100644 --- a/src/audiofile.h +++ b/src/audiofile.h @@ -80,7 +80,7 @@ public: bool isLoaded(); volatile size_t size; - volatile sample_t *data; + sample_t *data; std::string filename; diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 2e9eab9..5e2adf0 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -3,8 +3,8 @@ * cachemanager.cc * * Fri Apr 10 10:39:24 CEST 2015 - * Copyright 2015 Bent Bisballe Nyeng - * deva@aasimon.org + * Copyright 2015 Jonas Suhr Christensen + * jsc@umbraculum.org ****************************************************************************/ /* @@ -26,3 +26,106 @@ */ #include "cachemanager.h" +#include + +CacheManager::CacheManager() +{ +} + +CacheManager::~CacheManager() +{ + wait_stop(); +} + +void CacheManager::init(int poolsize) +{ + id2cache.resize(poolsize); + for(int i = 0; i < poolsize; i++) { + availableids.push_back(i); + } + + running = true; + run(); +} + +// Invariant: initial_samples_needed < preloaded audio data +sample_t *CacheManager::open(AudioFile *file, int initial_samples_needed, int channel, cacheid_t &id) +{ + // What if no free ids is available? + m_ids.lock(); + id = availableids.front(); + availableids.pop_front(); + m_ids.unlock(); + + cache_t c; + c.file = file; + c.channel = channel; + c.pos = initial_samples_needed; + // Allocate buffers + // Increase audio ref count + + id2cache[id] = c; + + event_t e; + e.id = id; + pushEvent(e); + + return file->data; +} + +void CacheManager::close(cacheid_t id) +{ + m_ids.lock(); + availableids.push_back(id); + m_ids.unlock(); + + // Clean cache_t mapped to event + // Clean event list for other events mapped to this id? + // Maybe we need an event for this, that we can push in front of eventqueue (con: we dont read from disk when doing this stuff). + // Decrement audiofile ref count +} + +sample_t *CacheManager::next(cacheid_t id, size_t &size) +{ + sample_t *s = NULL; + return s; +} + +void CacheManager::loadNext(cacheid_t id) +{ + printf("Loading next...\n"); + + // If more is left of file + if(false) { + event_t e; + e.id = id; + pushEvent(e); + } +} + +void CacheManager::thread_main() +{ + while(running) { + sem.wait(); + + m_events.lock(); + if(eventqueue.empty()) { + event_t e = eventqueue.front(); + eventqueue.pop_front(); + m_events.unlock(); + + loadNext(e.id); + } + else { + m_events.unlock(); + } + } +} + +void CacheManager::pushEvent(event_t e) +{ + // Check that if event should be merged (Maybe by event queue (ie. push in front). + m_events.lock(); + eventqueue.push_back(e); + m_events.unlock(); +} diff --git a/src/cachemanager.h b/src/cachemanager.h index ed89d12..e170bf4 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -3,8 +3,8 @@ * cachemanager.h * * Fri Apr 10 10:39:24 CEST 2015 - * Copyright 2015 Bent Bisballe Nyeng - * deva@aasimon.org + * Copyright 2015 Jonas Suhr Christensen + * jsc@umbraculum.org ****************************************************************************/ /* @@ -29,18 +29,28 @@ #include #include +#include +#include "thread.h" +#include "semaphore.h" +#include "mutex.h" + +#include "audiotypes.h" +#include "audiofile.h" + +#define CACHEMANAGER_NOID -1; class AudioFile; +typedef int cacheid_t; -class CacheManager { +class CacheManager : public Thread { public: + CacheManager(); + ~CacheManager(); - class Request { - public: - std::string filename; - std::map targets; - size_t pos; - }; + void init(int poolsize); + void deinit(); + + void thread_main(); // Pre: preloaded contains 2 x framesize. chunk size is framesize. // allocate 2 chunks and copy initial_samples_needed to first buffer from @@ -48,17 +58,17 @@ public: // 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 - sample_t *hello(AudioFile *file, int initial_samples_needed, int channel, id_t &new_id); + sample_t *open(AudioFile *file, int initial_samples_needed, int channel, cacheid_t &new_id); // Return which ever buffer is the front, swap them and add event to load the // next chunk. - sample_t *getNextBuffer(id_t id, size_t &size); + sample_t *next(cacheid_t id, size_t &size); // 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. - void goodbye(id_t id); + void close(cacheid_t id); // id vector: // { @@ -70,15 +80,38 @@ public: // } private: - class File { - public: - sndfile_t *handle; - int refcounter; - }; - std::list requests; - std::map + void pushEvent(); + + typedef struct { + AudioFile *file; + int channel; + size_t pos; + void *a; + void *b; + } cache_t; + + typedef struct { + cacheid_t id; + } event_t; + + void loadNext(cacheid_t id); + void pushEvent(event_t e); + cacheid_t leaseId(); + cacheid_t releaseId(); + + // Protected by mutex + std::list eventqueue; + std::list availableids; + std::vector id2cache; + + Mutex m_events; + Mutex m_ids; + Mutex m_caches; + + Semaphore sem; + int running; }; #endif/*__DRUMGIZMO_CACHEMANAGER_H__*/ diff --git a/test/Makefile.am b/test/Makefile.am index 5aaf33f..3d0d405 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,7 +1,7 @@ # Rules for the test code (use `make check` to execute) include $(top_srcdir)/src/Makefile.am.drumgizmo -TESTS = engine gui resampler lv2 +TESTS = engine cache gui resampler lv2 check_PROGRAMS = $(TESTS) @@ -16,6 +16,17 @@ engine_SOURCES = \ test.cc \ engine.cc +cache_CXXFLAGS = -DOUTPUT=\"cache\" $(CPPUNIT_CFLAGS) \ + -I$(top_srcdir)/src -I$(top_srcdir)/include \ + -I$(top_srcdir)/hugin -DDISABLE_HUGIN +cache_CFLAGS = -DDISABLE_HUGIN +cache_LDFLAGS = $(CPPUNIT_LIBS) $(DRUMGIZMO_LIBS) $(PTHREAD_LIBS) +cache_SOURCES = \ + $(DRUMGIZMO_SOURCES) \ + $(top_srcdir)/hugin/hugin.c \ + test.cc \ + cache.cc + gui_CXXFLAGS = -DOUTPUT=\"gui\" $(CPPUNIT_CFLAGS) gui_LDFLAGS = $(CPPUNIT_LIBS) gui_SOURCES = \ -- cgit v1.2.3 From be83681d67fb5407181240bff816ff202818b808 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Fri, 10 Apr 2015 16:37:32 +0200 Subject: Add preliminary functionality of next method. --- src/cachemanager.cc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 5e2adf0..25bbae5 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -52,7 +52,7 @@ void CacheManager::init(int poolsize) sample_t *CacheManager::open(AudioFile *file, int initial_samples_needed, int channel, cacheid_t &id) { // What if no free ids is available? - m_ids.lock(); + m_ids.lock(); id = availableids.front(); availableids.pop_front(); m_ids.unlock(); @@ -87,8 +87,15 @@ void CacheManager::close(cacheid_t id) sample_t *CacheManager::next(cacheid_t id, size_t &size) { - sample_t *s = NULL; - return s; + cache_t *c; + { + MutexAutolock l(m_caches); + c = &id2cache[id]; + } + size = 256; + sample_t *s = c->file->data + c->pos; + c->pos += size; + return s; } void CacheManager::loadNext(cacheid_t id) -- cgit v1.2.3 From 3fa874b20824d7bc61352f4f980114ba4a6399b6 Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Fri, 10 Apr 2015 16:42:42 +0200 Subject: Doing nothing in loadNext function. --- src/cachemanager.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 25bbae5..455b935 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -100,6 +100,7 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) void CacheManager::loadNext(cacheid_t id) { +/* printf("Loading next...\n"); // If more is left of file @@ -108,6 +109,7 @@ void CacheManager::loadNext(cacheid_t id) e.id = id; pushEvent(e); } +*/ } void CacheManager::thread_main() -- cgit v1.2.3 From 0f6c63ac08ed1a4e3977087cdc4cfec246ac3928 Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Fri, 10 Apr 2015 16:48:52 +0200 Subject: Added deinit() implementation. Fixed semicolon in typedef. --- src/cachemanager.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 455b935..0ca8bb0 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -34,7 +34,7 @@ CacheManager::CacheManager() CacheManager::~CacheManager() { - wait_stop(); + deinit(); } void CacheManager::init(int poolsize) @@ -48,6 +48,13 @@ void CacheManager::init(int poolsize) run(); } +void CacheManager::deinit() +{ + if(!running) return; + running = false; + wait_stop(); +} + // Invariant: initial_samples_needed < preloaded audio data sample_t *CacheManager::open(AudioFile *file, int initial_samples_needed, int channel, cacheid_t &id) { -- cgit v1.2.3 From e364e0ae265d5fc80a51dd8fa75f8a135552289f Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Fri, 10 Apr 2015 16:53:54 +0200 Subject: Fixed semicolon in typedef. --- src/cachemanager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cachemanager.h b/src/cachemanager.h index e170bf4..b30de4c 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -38,7 +38,7 @@ #include "audiotypes.h" #include "audiofile.h" -#define CACHEMANAGER_NOID -1; +#define CACHEMANAGER_NOID -1 class AudioFile; typedef int cacheid_t; -- cgit v1.2.3 From 1f43d7514387b4585b9a4e0ffac361a03d593e5e Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Fri, 10 Apr 2015 17:05:14 +0200 Subject: Creating new events until file is completely loaded. --- src/cachemanager.cc | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 0ca8bb0..c035703 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -28,6 +28,8 @@ #include +#define CHUNKSIZE 256 + CacheManager::CacheManager() { } @@ -99,7 +101,7 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) MutexAutolock l(m_caches); c = &id2cache[id]; } - size = 256; + size = CHUNKSIZE; sample_t *s = c->file->data + c->pos; c->pos += size; return s; @@ -107,16 +109,18 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) void CacheManager::loadNext(cacheid_t id) { -/* - printf("Loading next...\n"); + m_caches.lock(); + cache_t c = id2cache[id]; + c.pos += CHUNKSIZE; + id2cache[id] = c; + m_caches.unlock(); // If more is left of file - if(false) { + if(c.pos < c.file->size) { event_t e; e.id = id; pushEvent(e); } -*/ } void CacheManager::thread_main() -- cgit v1.2.3 From 483d4a25ee529f0f577ea1ff26c570ecd88c2a63 Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Fri, 10 Apr 2015 17:39:19 +0200 Subject: Added clean type event. Help functions. And more cache manager logic. --- src/cachemanager.cc | 98 ++++++++++++++++++++++++++++++++--------------------- src/cachemanager.h | 10 ++++-- 2 files changed, 67 insertions(+), 41 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index c035703..719df17 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -66,86 +66,106 @@ sample_t *CacheManager::open(AudioFile *file, int initial_samples_needed, int ch availableids.pop_front(); m_ids.unlock(); - cache_t c; - c.file = file; - c.channel = channel; - c.pos = initial_samples_needed; - // Allocate buffers - // Increase audio ref count - - id2cache[id] = c; + if(initial_samples_needed < file->size) { + cache_t c; + c.file = file; + c.channel = channel; + c.pos = initial_samples_needed; + + // Allocate buffers + // Increase audio ref count + + id2cache[id] = c; - event_t e; - e.id = id; - pushEvent(e); + event_t e = createEvent(id, LOADNEXT); + pushEvent(e); + } return file->data; } void CacheManager::close(cacheid_t id) { - m_ids.lock(); - availableids.push_back(id); - m_ids.unlock(); - + { + event_t e = createEvent(id, CLEAN); + MutexAutolock l(m_events); + eventqueue.push_front(e); + } + + { + MutexAutolock l(m_ids); + availableids.push_back(id); + } // Clean cache_t mapped to event - // Clean event list for other events mapped to this id? - // Maybe we need an event for this, that we can push in front of eventqueue (con: we dont read from disk when doing this stuff). // Decrement audiofile ref count } sample_t *CacheManager::next(cacheid_t id, size_t &size) { - cache_t *c; - { - MutexAutolock l(m_caches); - c = &id2cache[id]; - } size = CHUNKSIZE; - sample_t *s = c->file->data + c->pos; - c->pos += size; + m_caches.lock(); + cache_t c = id2cache[id]; + c.pos += size; + id2cache[id] = c; + m_caches.unlock(); + + // If more is left of file + if(c.pos < c.file->size) { + event_t e = createEvent(id, LOADNEXT); + pushEvent(e); + } + + sample_t *s = c.file->data + c.pos; return s; } void CacheManager::loadNext(cacheid_t id) { - m_caches.lock(); + MutexAutolock l(m_caches); cache_t c = id2cache[id]; c.pos += CHUNKSIZE; id2cache[id] = c; - m_caches.unlock(); - - // If more is left of file - if(c.pos < c.file->size) { - event_t e; - e.id = id; - pushEvent(e); - } } void CacheManager::thread_main() { while(running) { sem.wait(); - + m_events.lock(); if(eventqueue.empty()) { event_t e = eventqueue.front(); eventqueue.pop_front(); m_events.unlock(); - - loadNext(e.id); + + if(!e.active) continue; + + switch(e.cmd) { + case LOADNEXT: + loadNext(e.id); + break; + case CLEAN: + break; + } } else { m_events.unlock(); - } + } } } void CacheManager::pushEvent(event_t e) { // Check that if event should be merged (Maybe by event queue (ie. push in front). - m_events.lock(); + MutexAutolock l(m_events); eventqueue.push_back(e); - m_events.unlock(); +} + +CacheManager::event_t CacheManager::createEvent(cacheid_t id, cmd_t cmd) +{ + event_t e; + e.active = true; + e.id = id; + e.cmd = cmd; + return e; } diff --git a/src/cachemanager.h b/src/cachemanager.h index b30de4c..6a14429 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -91,14 +91,20 @@ private: void *b; } cache_t; + enum cmd_t { + LOADNEXT = 0, + CLEAN = 1 + }; + typedef struct { + bool active; cacheid_t id; + cmd_t cmd; } event_t; + CacheManager::event_t createEvent(cacheid_t id, cmd_t type); void loadNext(cacheid_t id); void pushEvent(event_t e); - cacheid_t leaseId(); - cacheid_t releaseId(); // Protected by mutex std::list eventqueue; -- cgit v1.2.3 From 379f0fec9247a74f05fef6538198898af02ce8e2 Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Fri, 10 Apr 2015 17:42:56 +0200 Subject: Changed size of parameter from int to size_t. --- src/cachemanager.cc | 2 +- src/cachemanager.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 719df17..9b18882 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -58,7 +58,7 @@ void CacheManager::deinit() } // Invariant: initial_samples_needed < preloaded audio data -sample_t *CacheManager::open(AudioFile *file, int initial_samples_needed, int channel, cacheid_t &id) +sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int channel, cacheid_t &id) { // What if no free ids is available? m_ids.lock(); diff --git a/src/cachemanager.h b/src/cachemanager.h index 6a14429..4b5c64d 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -58,7 +58,7 @@ public: // 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 - sample_t *open(AudioFile *file, int initial_samples_needed, int channel, cacheid_t &new_id); + sample_t *open(AudioFile *file, size_t initial_samples_needed, int channel, cacheid_t &new_id); // Return which ever buffer is the front, swap them and add event to load the // next chunk. -- cgit v1.2.3 From 499b2cb31f1c66fb9f793582bcdfffb0b5c2395d Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Fri, 10 Apr 2015 18:03:54 +0200 Subject: Added handling of cache calling when no free ids is available. --- src/cachemanager.cc | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 9b18882..2c0315d 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -30,8 +30,13 @@ #define CHUNKSIZE 256 +sample_t nodata[CHUNKSIZE]; + CacheManager::CacheManager() { + for(int i = 0; i < CHUNKSIZE; i++) { + nodata[i] = 0; + } } CacheManager::~CacheManager() @@ -62,11 +67,17 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int { // What if no free ids is available? m_ids.lock(); - id = availableids.front(); - availableids.pop_front(); + if(availableids.empty()) { + id = CACHEMANAGER_NOID; + } + else { + id = availableids.front(); + availableids.pop_front(); + } m_ids.unlock(); - if(initial_samples_needed < file->size) { + if(id != CACHEMANAGER_NOID && + initial_samples_needed < file->size) { cache_t c; c.file = file; c.channel = channel; @@ -81,11 +92,17 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int pushEvent(e); } + if(id == CACHEMANAGER_NOID) { + return nodata; + } + return file->data; } void CacheManager::close(cacheid_t id) { + if(CACHEMANAGER_NOID) return; + { event_t e = createEvent(id, CLEAN); MutexAutolock l(m_events); @@ -103,6 +120,11 @@ void CacheManager::close(cacheid_t id) sample_t *CacheManager::next(cacheid_t id, size_t &size) { size = CHUNKSIZE; + + if(id == CACHEMANAGER_NOID) { + return nodata; + } + m_caches.lock(); cache_t c = id2cache[id]; c.pos += size; -- cgit v1.2.3 From a669ef74a829d12897c70e6d00dea0a0ead25070 Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Fri, 10 Apr 2015 20:35:43 +0200 Subject: Added CACHE_DUMMYID. --- src/cachemanager.cc | 18 ++++++++---------- src/cachemanager.h | 4 +++- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 2c0315d..e72ee37 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -65,10 +65,9 @@ void CacheManager::deinit() // Invariant: initial_samples_needed < preloaded audio data sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int channel, cacheid_t &id) { - // What if no free ids is available? m_ids.lock(); if(availableids.empty()) { - id = CACHEMANAGER_NOID; + id = CACHE_DUMMYID; } else { id = availableids.front(); @@ -76,8 +75,11 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int } m_ids.unlock(); - if(id != CACHEMANAGER_NOID && - initial_samples_needed < file->size) { + if(id == CACHE_DUMMYID) { + return nodata; + } + + if(initial_samples_needed < file->size) { cache_t c; c.file = file; c.channel = channel; @@ -92,16 +94,12 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int pushEvent(e); } - if(id == CACHEMANAGER_NOID) { - return nodata; - } - return file->data; } void CacheManager::close(cacheid_t id) { - if(CACHEMANAGER_NOID) return; + if(CACHE_DUMMYID) return; { event_t e = createEvent(id, CLEAN); @@ -121,7 +119,7 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) { size = CHUNKSIZE; - if(id == CACHEMANAGER_NOID) { + if(id == CACHE_DUMMYID) { return nodata; } diff --git a/src/cachemanager.h b/src/cachemanager.h index 4b5c64d..19749a7 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -38,7 +38,9 @@ #include "audiotypes.h" #include "audiofile.h" -#define CACHEMANAGER_NOID -1 +#define CACHE_DUMMYID -2 +#define CACHE_NOID -1 + class AudioFile; typedef int cacheid_t; -- cgit v1.2.3 From a567c1e293909d15d6ad76c4e4af0fc1b0978ad3 Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Fri, 10 Apr 2015 20:41:32 +0200 Subject: Voluntered to go for a beer walk. --- src/cachemanager.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index e72ee37..81d809a 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -99,7 +99,7 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int void CacheManager::close(cacheid_t id) { - if(CACHE_DUMMYID) return; + if(id == CACHE_DUMMYID) return; { event_t e = createEvent(id, CLEAN); -- cgit v1.2.3 From c03a0668977fd86d34d76bbac11027bec07c119e Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Fri, 10 Apr 2015 20:52:49 +0200 Subject: Posting semaphore on eventpush. --- src/cachemanager.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 81d809a..366cd49 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -177,8 +177,11 @@ void CacheManager::thread_main() void CacheManager::pushEvent(event_t e) { // 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::event_t CacheManager::createEvent(cacheid_t id, cmd_t cmd) -- cgit v1.2.3 From 3742ac29b3ab8b39b3c8cb820ed988b553de27bb Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Sat, 11 Apr 2015 11:19:42 +0200 Subject: Added pos to event_t. Fixed bufs. --- hugin | 2 +- src/cachemanager.cc | 42 +++++++++++++++++++++++++----------------- src/cachemanager.h | 4 +++- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/hugin b/hugin index bb7388b..7e73471 160000 --- a/hugin +++ b/hugin @@ -1 +1 @@ -Subproject commit bb7388b685ed043b4a3030da86f7f1e491414773 +Subproject commit 7e734710be0098ea77ca2d3f54fb626b65bbf477 diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 366cd49..641d984 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -88,9 +88,12 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int // Allocate buffers // Increase audio ref count + { + MutexAutolock l(m_ids); id2cache[id] = c; + } - event_t e = createEvent(id, LOADNEXT); + event_t e = createLoadNextEvent(id, c.pos, LOADNEXT); pushEvent(e); } @@ -102,9 +105,9 @@ void CacheManager::close(cacheid_t id) if(id == CACHE_DUMMYID) return; { - event_t e = createEvent(id, CLEAN); - MutexAutolock l(m_events); - eventqueue.push_front(e); +// event_t e = createEvent(id, CLEAN); +// MutexAutolock l(m_events); +// eventqueue.push_front(e); } { @@ -115,6 +118,14 @@ void CacheManager::close(cacheid_t id) // Decrement audiofile ref count } +const CacheManager::cache_t CacheManager::getNextCache(cacheid_t id) +{ + MutexAutolock l(m_caches); + cache_t c = id2cache[id]; + id2cache[id].pos += CHUNKSIZE; + return c; +} + sample_t *CacheManager::next(cacheid_t id, size_t &size) { size = CHUNKSIZE; @@ -123,19 +134,15 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) return nodata; } - m_caches.lock(); - cache_t c = id2cache[id]; - c.pos += size; - id2cache[id] = c; - m_caches.unlock(); - + const cache_t c = getNextCache(id); + // If more is left of file if(c.pos < c.file->size) { - event_t e = createEvent(id, LOADNEXT); + event_t e = createLoadNextEvent(id, c.pos + CHUNKSIZE, LOADNEXT); pushEvent(e); - } + } - sample_t *s = c.file->data + c.pos; + sample_t *s = c.file->data + (c.pos - size); return s; } @@ -143,7 +150,6 @@ void CacheManager::loadNext(cacheid_t id) { MutexAutolock l(m_caches); cache_t c = id2cache[id]; - c.pos += CHUNKSIZE; id2cache[id] = c; } @@ -153,11 +159,12 @@ void CacheManager::thread_main() sem.wait(); m_events.lock(); - if(eventqueue.empty()) { + if(!eventqueue.empty()) { event_t e = eventqueue.front(); eventqueue.pop_front(); m_events.unlock(); - + + // TODO: Skip event if e.pos < cache.pos if(!e.active) continue; switch(e.cmd) { @@ -184,11 +191,12 @@ void CacheManager::pushEvent(event_t e) sem.post(); } -CacheManager::event_t CacheManager::createEvent(cacheid_t id, cmd_t cmd) +CacheManager::event_t CacheManager::createLoadNextEvent(cacheid_t id, size_t pos, cmd_t cmd) { event_t e; e.active = true; e.id = id; e.cmd = cmd; + e.pos = pos; return e; } diff --git a/src/cachemanager.h b/src/cachemanager.h index 19749a7..264f644 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -101,12 +101,14 @@ private: typedef struct { bool active; cacheid_t id; + size_t pos; cmd_t cmd; } event_t; - CacheManager::event_t createEvent(cacheid_t id, cmd_t type); + CacheManager::event_t createLoadNextEvent(cacheid_t id, size_t pos, cmd_t type); void loadNext(cacheid_t id); void pushEvent(event_t e); + const cache_t getNextCache(cacheid_t id); // Protected by mutex std::list eventqueue; -- cgit v1.2.3 From 9380bfd197d25712b08be41c0fc91e11ea364a94 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sat, 11 Apr 2015 12:11:16 +0200 Subject: Add documentation of CacheManager public API. --- src/cachemanager.h | 99 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 25 deletions(-) diff --git a/src/cachemanager.h b/src/cachemanager.h index 264f644..f28eb5f 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -44,42 +44,91 @@ 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(); - void init(int poolsize); + /** + * 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(); - void thread_main(); - - // 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 - sample_t *open(AudioFile *file, size_t initial_samples_needed, int channel, cacheid_t &new_id); - - // Return which ever buffer is the front, swap them and add event to load the - // next chunk. + /** + * 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); - // 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. + /** + * Unregister cache entry. + * Close associated file handles and free associated buffers. + * @param id The cache id to close. + */ void close(cacheid_t id); - // id vector: - // { - // AudioFile* - // channel - // buffer1, buffer2 - // position - // (ready?) - // } + ///! Internal thread main method - needs to be public. + void thread_main(); private: -- cgit v1.2.3 From ac5125e6eb16ec769b4fa541366898e0ebc83923 Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Sat, 11 Apr 2015 12:53:34 +0200 Subject: Handling frames in next. --- src/cachemanager.cc | 103 ++++++++++++++++++++++++++++++++++------------------ src/cachemanager.h | 6 +-- 2 files changed, 71 insertions(+), 38 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 641d984..ca7fde1 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -28,15 +28,17 @@ #include -#define CHUNKSIZE 256 +#define FRAMESIZE 256 +#define CHUNKSIZE FRAMESIZE*8 -sample_t nodata[CHUNKSIZE]; +sample_t nodata[FRAMESIZE]; + +static std::vector localcachepos; +static std::vector localcache; CacheManager::CacheManager() { - for(int i = 0; i < CHUNKSIZE; i++) { - nodata[i] = 0; - } + } CacheManager::~CacheManager() @@ -44,15 +46,26 @@ CacheManager::~CacheManager() deinit(); } -void CacheManager::init(int poolsize) +void CacheManager::init(size_t poolsize) { + for(size_t i = 0; i < FRAMESIZE; i++) { + nodata[i] = 0; + } + id2cache.resize(poolsize); - for(int i = 0; i < poolsize; i++) { + for(size_t i = 0; i < poolsize; i++) { availableids.push_back(i); } + localcachepos.resize(poolsize); + for(size_t i = 0; i < poolsize; i++) { + localcachepos[i] = 0; + } + running = true; run(); + + // TODO: Add semaphore } void CacheManager::deinit() @@ -65,39 +78,41 @@ void CacheManager::deinit() // Invariant: initial_samples_needed < preloaded audio data sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int channel, cacheid_t &id) { - m_ids.lock(); - if(availableids.empty()) { - id = CACHE_DUMMYID; - } - else { - id = availableids.front(); - availableids.pop_front(); + { + MutexAutolock l(m_ids); + if(availableids.empty()) { + id = CACHE_DUMMYID; + } else { + id = availableids.front(); + availableids.pop_front(); + } } - m_ids.unlock(); if(id == CACHE_DUMMYID) { return nodata; } - if(initial_samples_needed < file->size) { - cache_t c; - c.file = file; - c.channel = channel; - c.pos = initial_samples_needed; - - // Allocate buffers - // Increase audio ref count - { + cache_t c; + c.file = file; + c.channel = channel; + c.pos = initial_samples_needed; + c.front = file->data; + c.back = nodata; + // Allocate buffers + // Increase audio ref count + + { MutexAutolock l(m_ids); id2cache[id] = c; - } - + } + + if(initial_samples_needed < file->size) { event_t e = createLoadNextEvent(id, c.pos, LOADNEXT); pushEvent(e); } - return file->data; + return c.front; } void CacheManager::close(cacheid_t id) @@ -128,29 +143,47 @@ const CacheManager::cache_t CacheManager::getNextCache(cacheid_t id) sample_t *CacheManager::next(cacheid_t id, size_t &size) { - size = CHUNKSIZE; + size = FRAMESIZE; if(id == CACHE_DUMMYID) { return nodata; } + size_t localpos = localcachepos[id]; + sample_t* localbuf = localcache[id]; + if(localpos < CHUNKSIZE) { + localcachepos[id] += FRAMESIZE; + return localbuf + FRAMESIZE; + } + + localcachepos[id] = 0; + const cache_t c = getNextCache(id); + + localcache[id] = c.front; - // If more is left of file if(c.pos < c.file->size) { event_t e = createLoadNextEvent(id, c.pos + CHUNKSIZE, LOADNEXT); pushEvent(e); } - sample_t *s = c.file->data + (c.pos - size); - return s; +// sample_t *s = c.file->data + (c.pos - size); + return c.front; +// return s; } void CacheManager::loadNext(cacheid_t id) { - MutexAutolock l(m_caches); + m_caches.lock(); cache_t c = id2cache[id]; + sample_t *tmp; + c.front = c.back; + c.back = tmp; id2cache[id] = c; + m_caches.unlock(); + + // do work + tmp = tmp + CHUNKSIZE; } void CacheManager::thread_main() @@ -165,14 +198,14 @@ void CacheManager::thread_main() m_events.unlock(); // TODO: Skip event if e.pos < cache.pos - if(!e.active) continue; +// if(!e.active) continue; switch(e.cmd) { case LOADNEXT: loadNext(e.id); break; - case CLEAN: - break; +// case CLEAN: +// break; } } else { diff --git a/src/cachemanager.h b/src/cachemanager.h index 264f644..6a60880 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -49,7 +49,7 @@ public: CacheManager(); ~CacheManager(); - void init(int poolsize); + void init(size_t poolsize); void deinit(); void thread_main(); @@ -89,8 +89,8 @@ private: AudioFile *file; int channel; size_t pos; - void *a; - void *b; + sample_t *front; + sample_t *back; } cache_t; enum cmd_t { -- cgit v1.2.3 From 116995d58e0e1f4bb85f8dd10d7d07afe06e8f28 Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Sat, 11 Apr 2015 16:50:44 +0200 Subject: Fixed compile warnings. --- src/cachemanager.cc | 2 +- src/cachemanager.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index ca7fde1..c193db8 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -176,7 +176,7 @@ void CacheManager::loadNext(cacheid_t id) { m_caches.lock(); cache_t c = id2cache[id]; - sample_t *tmp; + sample_t *tmp = c.front; c.front = c.back; c.back = tmp; id2cache[id] = c; diff --git a/src/cachemanager.h b/src/cachemanager.h index d2af6bf..e0bf7f9 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -144,7 +144,7 @@ private: enum cmd_t { LOADNEXT = 0, - CLEAN = 1 +// CLEAN = 1 }; typedef struct { -- cgit v1.2.3 From 9c03659b323eb27ca447f7448512aa0e6c03e025 Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Sat, 11 Apr 2015 16:55:16 +0200 Subject: Initializing localcache. --- src/cachemanager.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index c193db8..f7fc730 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -62,6 +62,8 @@ void CacheManager::init(size_t poolsize) localcachepos[i] = 0; } + localcache.resize(poolsize); + running = true; run(); -- cgit v1.2.3 From be990c6057033c298dd7d69dbf52872833534b85 Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Sat, 11 Apr 2015 17:01:19 +0200 Subject: Setting local cache in open call. --- src/cachemanager.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index f7fc730..9536cdf 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -61,7 +61,10 @@ void CacheManager::init(size_t poolsize) for(size_t i = 0; i < poolsize; i++) { localcachepos[i] = 0; } - + + for(size_t i = 0; i < poolsize; i++) { + localcache[i] = NULL; + } localcache.resize(poolsize); running = true; @@ -109,6 +112,9 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int id2cache[id] = c; } + localcachepos[id] = 0; + localcache[id] = c.front; + if(initial_samples_needed < file->size) { event_t e = createLoadNextEvent(id, c.pos, LOADNEXT); pushEvent(e); @@ -159,6 +165,7 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) } localcachepos[id] = 0; + localcache[id] = NULL; const cache_t c = getNextCache(id); -- cgit v1.2.3 From 4d82b13f332ea8c3af3493e9ee2ac9cacdfa7f22 Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Sat, 11 Apr 2015 17:02:51 +0200 Subject: Resizing before initializing values in vector. --- src/cachemanager.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 9536cdf..d047d66 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -62,10 +62,10 @@ void CacheManager::init(size_t poolsize) localcachepos[i] = 0; } + localcache.resize(poolsize); for(size_t i = 0; i < poolsize; i++) { localcache[i] = NULL; } - localcache.resize(poolsize); running = true; run(); -- cgit v1.2.3 From 5fa47e69fb003238e1558a93b3e3cd50e3eb260e Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sat, 11 Apr 2015 17:05:04 +0200 Subject: Integrate CacheManager into the engine. --- src/drumgizmo.cc | 34 +++++++++++++++++++++++++++------- src/drumgizmo.h | 3 +++ src/events.h | 6 ++++++ 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index 7ce05ef..d6b0f9e 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); // start thread } DrumGizmo::~DrumGizmo() { + cacheManager.deinit(); // stop thread } bool DrumGizmo::loadkit(std::string file) @@ -373,6 +375,7 @@ void DrumGizmo::run(int endpos) free(samples); } +#undef SSE #ifdef SSE #define N 8 typedef float vNsf __attribute__ ((vector_size(sizeof(float)*N))); @@ -398,6 +401,18 @@ 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; + } + + 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; + } + { MutexAutolock l(af->mutex); @@ -409,32 +424,37 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) 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]); + size_t optend = ((end - n) / N) * N + n; + size_t t; + for(; n < optend; n += N) { + t = evt->t % evt->buffer_size; + *(vNsf*)&(s[n]) += *(vNsf*)&(evt->buffer[t]); evt->t += N; - } + } #endif for(; n < end; n++) { - s[n] += af->data[evt->t]; + s[n] += evt->buffer[evt->t % evt->buffer_size]; evt->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; + s[n] += evt->buffer[evt->t % evt->buffer_size] * scale; evt->t++; evt->rampdown--; } if(evt->rampdown == 0) { removeevent = true; // Down ramp done. Remove event. + cacheManager.close(evt->cache_id); } } 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 f8d45f5..b6711b4 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" @@ -92,6 +93,8 @@ private: std::map audiofiles; + CacheManager cacheManager; + #ifdef TEST_DRUMGIZMO public: #endif 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; -- cgit v1.2.3 From b7e50d0f21e5619e746dafac129bb66b746c8d64 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sat, 11 Apr 2015 17:05:39 +0200 Subject: Re-enable SSE --- src/drumgizmo.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index d6b0f9e..2f04488 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -375,7 +375,6 @@ void DrumGizmo::run(int endpos) free(samples); } -#undef SSE #ifdef SSE #define N 8 typedef float vNsf __attribute__ ((vector_size(sizeof(float)*N))); -- cgit v1.2.3 From 9c52ab4a6688782d0db723bd411db745fed08087 Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Sat, 11 Apr 2015 19:11:31 +0200 Subject: ADded local cache. --- src/cachemanager.cc | 54 ++++++++++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index d047d66..9288ed1 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -81,6 +81,7 @@ void CacheManager::deinit() } // 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) { { @@ -97,13 +98,12 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int return nodata; } - cache_t c; c.file = file; c.channel = channel; c.pos = initial_samples_needed; - c.front = file->data; - c.back = nodata; + c.front = file->data + initial_samples_needed; + c.back = file->data; // Allocate buffers // Increase audio ref count @@ -116,11 +116,11 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int localcache[id] = c.front; if(initial_samples_needed < file->size) { - event_t e = createLoadNextEvent(id, c.pos, LOADNEXT); + event_t e = createLoadNextEvent(id, c.pos, &c.back); pushEvent(e); } - return c.front; + return file->data; // preloaded data } void CacheManager::close(cacheid_t id) @@ -141,11 +141,16 @@ void CacheManager::close(cacheid_t id) // Decrement audiofile ref count } -const CacheManager::cache_t CacheManager::getNextCache(cacheid_t id) +CacheManager::cache_t CacheManager::getNextCache(cacheid_t id) { MutexAutolock l(m_caches); cache_t c = id2cache[id]; + + sample_t *tmp = id2cache[id].front; + id2cache[id].front = c.back; + id2cache[id].back = tmp; id2cache[id].pos += CHUNKSIZE; + return c; } @@ -157,22 +162,18 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) return nodata; } - size_t localpos = localcachepos[id]; - sample_t* localbuf = localcache[id]; - if(localpos < CHUNKSIZE) { - localcachepos[id] += FRAMESIZE; - return localbuf + FRAMESIZE; + if(localcachepos[id] < CHUNKSIZE) { + localcachepos[id] += size; + return localcache[id] + localcachepos[id]; } - localcachepos[id] = 0; - localcache[id] = NULL; - - const cache_t c = getNextCache(id); + cache_t c = getNextCache(id); + localcachepos[id] = 0; localcache[id] = c.front; if(c.pos < c.file->size) { - event_t e = createLoadNextEvent(id, c.pos + CHUNKSIZE, LOADNEXT); + event_t e = createLoadNextEvent(id, c.pos + CHUNKSIZE, &c.back); pushEvent(e); } @@ -181,18 +182,10 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) // return s; } -void CacheManager::loadNext(cacheid_t id) +void CacheManager::loadNext(event_t &e) { - m_caches.lock(); - cache_t c = id2cache[id]; - sample_t *tmp = c.front; - c.front = c.back; - c.back = tmp; - id2cache[id] = c; - m_caches.unlock(); - - // do work - tmp = tmp + CHUNKSIZE; + cache_t c = id2cache[e.id]; + *e.fillbuffer = c.file->data + e.pos; } void CacheManager::thread_main() @@ -211,7 +204,7 @@ void CacheManager::thread_main() switch(e.cmd) { case LOADNEXT: - loadNext(e.id); + loadNext(e); break; // case CLEAN: // break; @@ -233,12 +226,13 @@ void CacheManager::pushEvent(event_t e) sem.post(); } -CacheManager::event_t CacheManager::createLoadNextEvent(cacheid_t id, size_t pos, cmd_t cmd) +CacheManager::event_t CacheManager::createLoadNextEvent(cacheid_t id, size_t pos, sample_t** fillbuffer) { event_t e; e.active = true; e.id = id; - e.cmd = cmd; + e.cmd = LOADNEXT; e.pos = pos; + e.fillbuffer = fillbuffer; return e; } -- cgit v1.2.3 From abf3b22cde970af44b28e97c2fc1be12e7570740 Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Sat, 11 Apr 2015 19:15:58 +0200 Subject: ADded local cache. --- src/cachemanager.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cachemanager.h b/src/cachemanager.h index e0bf7f9..e565542 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -152,12 +152,13 @@ private: cacheid_t id; size_t pos; cmd_t cmd; + sample_t **fillbuffer; } event_t; - CacheManager::event_t createLoadNextEvent(cacheid_t id, size_t pos, cmd_t type); - void loadNext(cacheid_t id); + CacheManager::event_t createLoadNextEvent(cacheid_t id, size_t pos, sample_t** fillbuffer); + void loadNext(event_t &e); void pushEvent(event_t e); - const cache_t getNextCache(cacheid_t id); + cache_t getNextCache(cacheid_t id); // Protected by mutex std::list eventqueue; -- cgit v1.2.3 From 94b6e63ef94e55fd026eec97bb50a7b50b19f2f3 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 12 Apr 2015 08:25:39 +0200 Subject: Fix bad buffer pointers. --- src/cachemanager.cc | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 9288ed1..0f84f8c 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -116,7 +116,7 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int localcache[id] = c.front; if(initial_samples_needed < file->size) { - event_t e = createLoadNextEvent(id, c.pos, &c.back); + event_t e = createLoadNextEvent(id, c.pos, &id2cache[id].back); pushEvent(e); } @@ -144,12 +144,13 @@ void CacheManager::close(cacheid_t id) CacheManager::cache_t CacheManager::getNextCache(cacheid_t id) { MutexAutolock l(m_caches); - cache_t c = id2cache[id]; + cache_t &c = id2cache[id]; - sample_t *tmp = id2cache[id].front; - id2cache[id].front = c.back; - id2cache[id].back = tmp; - id2cache[id].pos += CHUNKSIZE; + sample_t *tmp = c.front; + c.front = c.back; + c.back = tmp; + + c.pos += CHUNKSIZE; return c; } @@ -173,7 +174,7 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) localcache[id] = c.front; if(c.pos < c.file->size) { - event_t e = createLoadNextEvent(id, c.pos + CHUNKSIZE, &c.back); + event_t e = createLoadNextEvent(id, c.pos + CHUNKSIZE, &id2cache[id].back); pushEvent(e); } -- cgit v1.2.3 From 873bcbbe7b8c226d1efbfd34b6d1d454bcd10639 Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Sun, 12 Apr 2015 09:48:41 +0200 Subject: Added buffers and removed local cache vectors. --- src/cachemanager.cc | 52 ++++++++++++++++++++-------------------------------- src/cachemanager.h | 5 +++-- 2 files changed, 23 insertions(+), 34 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 0f84f8c..6b413bc 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -26,6 +26,7 @@ */ #include "cachemanager.h" +#include #include #define FRAMESIZE 256 @@ -33,8 +34,8 @@ sample_t nodata[FRAMESIZE]; -static std::vector localcachepos; -static std::vector localcache; +//static std::vector localcachepos; +//static std::vector localcache; CacheManager::CacheManager() { @@ -57,16 +58,6 @@ void CacheManager::init(size_t poolsize) availableids.push_back(i); } - localcachepos.resize(poolsize); - for(size_t i = 0; i < poolsize; i++) { - localcachepos[i] = 0; - } - - localcache.resize(poolsize); - for(size_t i = 0; i < poolsize; i++) { - localcache[i] = NULL; - } - running = true; run(); @@ -102,9 +93,11 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int c.file = file; c.channel = channel; c.pos = initial_samples_needed; - c.front = file->data + initial_samples_needed; - c.back = file->data; - // Allocate buffers + c.localpos = 0; + c.front = new sample_t[CHUNKSIZE]; + c.back = new sample_t[CHUNKSIZE]; + + memcpy(c.front, c.file->data + c.pos, CHUNKSIZE); // Increase audio ref count { @@ -112,11 +105,8 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int id2cache[id] = c; } - localcachepos[id] = 0; - localcache[id] = c.front; - if(initial_samples_needed < file->size) { - event_t e = createLoadNextEvent(id, c.pos, &id2cache[id].back); + event_t e = createLoadNextEvent(id, c.pos, c.front); pushEvent(e); } @@ -149,6 +139,7 @@ CacheManager::cache_t CacheManager::getNextCache(cacheid_t id) sample_t *tmp = c.front; c.front = c.back; c.back = tmp; + c.localpos = 0; c.pos += CHUNKSIZE; @@ -163,30 +154,27 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) return nodata; } - if(localcachepos[id] < CHUNKSIZE) { - localcachepos[id] += size; - return localcache[id] + localcachepos[id]; + cache_t& c = id2cache[id]; + + if(c.localpos < CHUNKSIZE) { + c.localpos += size; + return c.front + c.localpos; } - cache_t c = getNextCache(id); + cache_t next = getNextCache(id); - localcachepos[id] = 0; - localcache[id] = c.front; - - if(c.pos < c.file->size) { - event_t e = createLoadNextEvent(id, c.pos + CHUNKSIZE, &id2cache[id].back); + if(next.pos < c.file->size) { + event_t e = createLoadNextEvent(id, next.pos + CHUNKSIZE, c.back); pushEvent(e); } -// sample_t *s = c.file->data + (c.pos - size); return c.front; -// return s; } void CacheManager::loadNext(event_t &e) { cache_t c = id2cache[e.id]; - *e.fillbuffer = c.file->data + e.pos; + memcpy(e.fillbuffer, c.file->data + e.pos, CHUNKSIZE); } void CacheManager::thread_main() @@ -227,7 +215,7 @@ void CacheManager::pushEvent(event_t e) sem.post(); } -CacheManager::event_t CacheManager::createLoadNextEvent(cacheid_t id, size_t pos, sample_t** fillbuffer) +CacheManager::event_t CacheManager::createLoadNextEvent(cacheid_t id, size_t pos, sample_t* fillbuffer) { event_t e; e.active = true; diff --git a/src/cachemanager.h b/src/cachemanager.h index e565542..1b79568 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -140,6 +140,7 @@ private: size_t pos; sample_t *front; sample_t *back; + size_t localpos; } cache_t; enum cmd_t { @@ -152,10 +153,10 @@ private: cacheid_t id; size_t pos; cmd_t cmd; - sample_t **fillbuffer; + sample_t *fillbuffer; } event_t; - CacheManager::event_t createLoadNextEvent(cacheid_t id, size_t pos, sample_t** fillbuffer); + CacheManager::event_t createLoadNextEvent(cacheid_t id, size_t pos, sample_t* fillbuffer); void loadNext(event_t &e); void pushEvent(event_t e); cache_t getNextCache(cacheid_t id); -- cgit v1.2.3 From 9842ada95aa51bc50ae6d4565f0f7aef5af22264 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 12 Apr 2015 10:57:54 +0200 Subject: Fix buffers and clean up a bit. --- src/cachemanager.cc | 70 +++++++++++++++++++++-------------------------------- src/cachemanager.h | 18 ++++++-------- 2 files changed, 36 insertions(+), 52 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 6b413bc..d3fdd9e 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -30,17 +30,11 @@ #include #define FRAMESIZE 256 -#define CHUNKSIZE FRAMESIZE*8 +#define CHUNKSIZE FRAMESIZE*100 -sample_t nodata[FRAMESIZE]; +static sample_t nodata[FRAMESIZE]; -//static std::vector localcachepos; -//static std::vector localcache; - -CacheManager::CacheManager() -{ - -} +CacheManager::CacheManager() {} CacheManager::~CacheManager() { @@ -97,7 +91,8 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int c.front = new sample_t[CHUNKSIZE]; c.back = new sample_t[CHUNKSIZE]; - memcpy(c.front, c.file->data + c.pos, CHUNKSIZE); + memcpy(c.front, c.file->data + c.pos, CHUNKSIZE * sizeof(sample_t)); + // Increase audio ref count { @@ -106,7 +101,7 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int } if(initial_samples_needed < file->size) { - event_t e = createLoadNextEvent(id, c.pos, c.front); + cevent_t e = createLoadNextEvent(c.file, c.pos + CHUNKSIZE, c.back); pushEvent(e); } @@ -131,21 +126,6 @@ void CacheManager::close(cacheid_t id) // Decrement audiofile ref count } -CacheManager::cache_t CacheManager::getNextCache(cacheid_t id) -{ - MutexAutolock l(m_caches); - cache_t &c = id2cache[id]; - - sample_t *tmp = c.front; - c.front = c.back; - c.back = tmp; - c.localpos = 0; - - c.pos += CHUNKSIZE; - - return c; -} - sample_t *CacheManager::next(cacheid_t id, size_t &size) { size = FRAMESIZE; @@ -155,26 +135,31 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) } cache_t& c = id2cache[id]; - if(c.localpos < CHUNKSIZE) { c.localpos += size; return c.front + c.localpos; } - cache_t next = getNextCache(id); + // Swap buffers + sample_t *tmp = c.front; + c.front = c.back; + c.back = tmp; + + c.localpos = 0; + + c.pos += CHUNKSIZE; - if(next.pos < c.file->size) { - event_t e = createLoadNextEvent(id, next.pos + CHUNKSIZE, c.back); + if(c.pos < c.file->size) { + cevent_t e = createLoadNextEvent(c.file, c.pos, c.back); pushEvent(e); } return c.front; } -void CacheManager::loadNext(event_t &e) +void CacheManager::loadNext(cevent_t &e) { - cache_t c = id2cache[e.id]; - memcpy(e.fillbuffer, c.file->data + e.pos, CHUNKSIZE); + memcpy(e.buffer, e.file->data + e.pos, CHUNKSIZE * sizeof(sample_t)); } void CacheManager::thread_main() @@ -184,7 +169,7 @@ void CacheManager::thread_main() m_events.lock(); if(!eventqueue.empty()) { - event_t e = eventqueue.front(); + cevent_t e = eventqueue.front(); eventqueue.pop_front(); m_events.unlock(); @@ -205,23 +190,24 @@ void CacheManager::thread_main() } } -void CacheManager::pushEvent(event_t e) +void CacheManager::pushEvent(cevent_t e) { // Check that if event should be merged (Maybe by event queue (ie. push in front). { - MutexAutolock l(m_events); - eventqueue.push_back(e); + MutexAutolock l(m_events); + eventqueue.push_back(e); } sem.post(); } -CacheManager::event_t CacheManager::createLoadNextEvent(cacheid_t id, size_t pos, sample_t* fillbuffer) +CacheManager::cevent_t CacheManager::createLoadNextEvent(AudioFile *file, + size_t pos, + sample_t* buffer) { - event_t e; - e.active = true; - e.id = id; + cevent_t e; e.cmd = LOADNEXT; e.pos = pos; - e.fillbuffer = fillbuffer; + e.buffer = buffer; + e.file = file; return e; } diff --git a/src/cachemanager.h b/src/cachemanager.h index 1b79568..557ba23 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -149,20 +149,18 @@ private: }; typedef struct { - bool active; - cacheid_t id; - size_t pos; cmd_t cmd; - sample_t *fillbuffer; - } event_t; + size_t pos; + sample_t *buffer; + AudioFile *file; + } cevent_t; - CacheManager::event_t createLoadNextEvent(cacheid_t id, size_t pos, sample_t* fillbuffer); - void loadNext(event_t &e); - void pushEvent(event_t e); - cache_t getNextCache(cacheid_t id); + cevent_t createLoadNextEvent(AudioFile *file, size_t pos, sample_t* buffer); + void loadNext(cevent_t &e); + void pushEvent(cevent_t e); // Protected by mutex - std::list eventqueue; + std::list eventqueue; std::list availableids; std::vector id2cache; -- cgit v1.2.3 From 2067d8af88cdeb14ecee0deaaeb873e3bc1eb513 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 12 Apr 2015 11:27:31 +0200 Subject: Added close event to free cache buffers. --- src/cachemanager.cc | 44 +++++++++++++++++++++++++++++++++----------- src/cachemanager.h | 38 +++++++++++++++++++++++--------------- 2 files changed, 56 insertions(+), 26 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index d3fdd9e..f7d155b 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -101,7 +101,8 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int } if(initial_samples_needed < file->size) { - cevent_t e = createLoadNextEvent(c.file, c.pos + CHUNKSIZE, c.back); + cevent_t e = + createLoadNextEvent(c.file, c.channel, c.pos + CHUNKSIZE, c.back); pushEvent(e); } @@ -112,6 +113,9 @@ void CacheManager::close(cacheid_t id) { if(id == CACHE_DUMMYID) return; + cevent_t e = createCloseEvent(id); + pushEvent(e); + { // event_t e = createEvent(id, CLEAN); // MutexAutolock l(m_events); @@ -150,18 +154,26 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) c.pos += CHUNKSIZE; if(c.pos < c.file->size) { - cevent_t e = createLoadNextEvent(c.file, c.pos, c.back); + cevent_t e = createLoadNextEvent(c.file, c.channel, c.pos, c.back); pushEvent(e); } return c.front; } -void CacheManager::loadNext(cevent_t &e) +void CacheManager::handleLoadNextEvent(cevent_t &e) { memcpy(e.buffer, e.file->data + e.pos, CHUNKSIZE * sizeof(sample_t)); } +void CacheManager::handleCloseEvent(cevent_t &e) +{ + cache_t& c = id2cache[e.id]; + delete[] c.front; + delete[] c.back; + // TODO: Count down ref coutner on c.file and close it if 0. +} + void CacheManager::thread_main() { while(running) { @@ -178,13 +190,13 @@ void CacheManager::thread_main() switch(e.cmd) { case LOADNEXT: - loadNext(e); + handleLoadNextEvent(e); + break; + case CLOSE: + handleCloseEvent(e); break; -// case CLEAN: -// break; } - } - else { + } else { m_events.unlock(); } } @@ -200,14 +212,24 @@ void CacheManager::pushEvent(cevent_t e) sem.post(); } -CacheManager::cevent_t CacheManager::createLoadNextEvent(AudioFile *file, - size_t pos, - sample_t* buffer) +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 index 557ba23..21579d9 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -131,46 +131,54 @@ public: void thread_main(); private: - - void pushEvent(); - typedef struct { AudioFile *file; - int channel; - size_t pos; + size_t channel; + size_t pos; //< File possition sample_t *front; sample_t *back; - size_t localpos; + size_t localpos; //< Intra buffer (front) position. } cache_t; - enum cmd_t { + typedef enum { LOADNEXT = 0, -// CLEAN = 1 - }; + 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; AudioFile *file; + size_t channel; } cevent_t; - cevent_t createLoadNextEvent(AudioFile *file, size_t pos, sample_t* buffer); - void loadNext(cevent_t &e); + 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 pushEvent(cevent_t e); - // Protected by mutex + std::vector id2cache; + + // Protected by mutex: std::list eventqueue; std::list availableids; - std::vector id2cache; Mutex m_events; Mutex m_ids; - Mutex m_caches; Semaphore sem; - int running; + bool running; }; #endif/*__DRUMGIZMO_CACHEMANAGER_H__*/ -- cgit v1.2.3 From e80ac13bfb7a16a79dc96f94c5816b7089b62efa Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Sun, 12 Apr 2015 15:43:57 +0200 Subject: Added audiofile reading. --- src/cachemanager.cc | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index f7d155b..51323d1 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -29,11 +29,57 @@ #include #include +#include + +#include + #define FRAMESIZE 256 #define CHUNKSIZE FRAMESIZE*100 static sample_t nodata[FRAMESIZE]; +#define BUFFER_SIZE FRAMESIZE +static size_t readChunk(std::string filename, int filechannel, size_t from, 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; + } + + sf_seek(fh, from, SEEK_SET); + + size_t size = num_samples; + sample_t* data = buf; + if(sf_info.channels == 1) { + size = sf_read_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; i++) { + data[totalread++] = buffer[i * sf_info.channels + filechannel]; + } + } while(read > 0 && totalread < (int)size); + // set data size to total bytes read + size = totalread; + } + + sf_close(fh); + + return size; +} + CacheManager::CacheManager() {} CacheManager::~CacheManager() @@ -163,7 +209,8 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) void CacheManager::handleLoadNextEvent(cevent_t &e) { - memcpy(e.buffer, e.file->data + e.pos, CHUNKSIZE * sizeof(sample_t)); +// memcpy(e.buffer, e.file->data + e.pos, CHUNKSIZE * sizeof(sample_t)); + readChunk(e.file->filename, e.channel, e.pos, CHUNKSIZE, e.buffer); } void CacheManager::handleCloseEvent(cevent_t &e) -- cgit v1.2.3 From fcfa4676a2be89fff6ea55ff13c5e3e73ba59a74 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 12 Apr 2015 20:23:19 +0200 Subject: Fix partial loading of multichannel files. --- src/audiofile.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/audiofile.cc b/src/audiofile.cc index 59e0c14..f6a2c1d 100644 --- a/src/audiofile.cc +++ b/src/audiofile.cc @@ -139,10 +139,10 @@ 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)size && totalread < num_samples); // set data size to total bytes read size = totalread; } -- cgit v1.2.3 From d4b217d3c2902bab3daf438714bcb7cb3ccbca51 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 12 Apr 2015 20:24:16 +0200 Subject: Fix file loading. --- src/cachemanager.cc | 26 ++++++++++++++------------ src/cachemanager.h | 3 +++ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 51323d1..3984447 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -33,13 +33,12 @@ #include -#define FRAMESIZE 256 -#define CHUNKSIZE FRAMESIZE*100 - static sample_t nodata[FRAMESIZE]; -#define BUFFER_SIZE FRAMESIZE -static size_t readChunk(std::string filename, int filechannel, size_t from, size_t num_samples, sample_t* buf) +#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); @@ -49,14 +48,17 @@ static size_t readChunk(std::string filename, int filechannel, size_t from, size return 0; } - sf_seek(fh, from, SEEK_SET); + if(pos > sf_info.frames) return 0; + + sf_seek(fh, pos, SEEK_SET); - size_t size = num_samples; - sample_t* data = buf; + 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_read_float(fh, data, size); - } - else { + } else { // check filechannel exists if(filechannel >= sf_info.channels) { filechannel = sf_info.channels - 1; @@ -67,10 +69,10 @@ static size_t readChunk(std::string filename, int filechannel, size_t from, size int read; do { read = sf_readf_float(fh, buffer, readsize); - for (int i = 0; i < read; i++) { + for (int i = 0; i < read && totalread < (int)size; i++) { data[totalread++] = buffer[i * sf_info.channels + filechannel]; } - } while(read > 0 && totalread < (int)size); + } while(read > 0 && totalread < (int)size && totalread < sf_info.frames); // set data size to total bytes read size = totalread; } diff --git a/src/cachemanager.h b/src/cachemanager.h index 21579d9..25115c3 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -41,6 +41,9 @@ #define CACHE_DUMMYID -2 #define CACHE_NOID -1 +#define FRAMESIZE 256 +#define CHUNKSIZE FRAMESIZE*100 + class AudioFile; typedef int cacheid_t; -- cgit v1.2.3 From 83316b2cc7cba508c9f9a7dfcc4c82026507badf Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Thu, 23 Apr 2015 15:13:07 +0200 Subject: Added 'preloadedsize' to AudioFile to reflect how much data was loaded and not just how big the entire file is. --- src/audiofile.cc | 18 ++++++++++-------- src/audiofile.h | 3 ++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/audiofile.cc b/src/audiofile.cc index f6a2c1d..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; @@ -142,12 +142,14 @@ void AudioFile::load(int num_samples) for (int i = 0; i < read && totalread < num_samples; i++) { data[totalread++] = buffer[i * sf_info.channels + filechannel]; } - } while(read > 0 && totalread < (int)size && totalread < num_samples); + } 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 5b57db8..5f93584 100644 --- a/src/audiofile.h +++ b/src/audiofile.h @@ -79,7 +79,8 @@ public: bool isLoaded(); - volatile size_t size; + 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; -- cgit v1.2.3 From 672e34d2f2066dcffdd54b554fbe9b89d5b85470 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Thu, 23 Apr 2015 15:20:48 +0200 Subject: Added run sempahore (wait until thread actually started). Fixed localpos bug. Added 'threaded' argument to init that handles events either directly or in a thread when dispatched. Added cachemanager unit test (currently failing). --- src/cachemanager.cc | 151 +++++++++++++++++++++++++-------------- src/cachemanager.h | 13 ++-- src/drumgizmo.cc | 14 +++- src/drumkitloader.cc | 3 +- test/Makefile.am | 29 ++++---- test/cachemanagertest.cc | 136 +++++++++++++++++++++++++++++++++++ test/kit/ride-multi-channel.wav | Bin 0 -> 28591492 bytes test/kit/ride-single-channel.wav | Bin 0 -> 694144 bytes 8 files changed, 272 insertions(+), 74 deletions(-) create mode 100644 test/cachemanagertest.cc create mode 100644 test/kit/ride-multi-channel.wav create mode 100644 test/kit/ride-single-channel.wav diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 3984447..e6c43cc 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -48,7 +48,9 @@ static size_t readChunk(std::string filename, int filechannel, size_t pos, return 0; } - if(pos > sf_info.frames) return 0; + if(pos > sf_info.frames) { + return 0; + } sf_seek(fh, pos, SEEK_SET); @@ -57,11 +59,11 @@ static size_t readChunk(std::string filename, int filechannel, size_t pos, sample_t* data = buf; if(sf_info.channels == 1) { - size = sf_read_float(fh, data, size); + size = sf_readf_float(fh, data, size); } else { // check filechannel exists if(filechannel >= sf_info.channels) { - filechannel = sf_info.channels - 1; + filechannel = sf_info.channels - 1; } sample_t buffer[BUFFER_SIZE]; int readsize = BUFFER_SIZE / sf_info.channels; @@ -89,8 +91,10 @@ CacheManager::~CacheManager() deinit(); } -void CacheManager::init(size_t poolsize) +void CacheManager::init(size_t poolsize, bool threaded) { + this->threaded = threaded; + for(size_t i = 0; i < FRAMESIZE; i++) { nodata[i] = 0; } @@ -101,21 +105,26 @@ void CacheManager::init(size_t poolsize) } running = true; - run(); - - // TODO: Add semaphore + if(threaded) { + run(); + sem_run.wait(); + } } void CacheManager::deinit() { if(!running) return; running = false; - wait_stop(); + 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) +sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, + int channel, cacheid_t &id) { { MutexAutolock l(m_ids); @@ -139,7 +148,11 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int c.front = new sample_t[CHUNKSIZE]; c.back = new sample_t[CHUNKSIZE]; - memcpy(c.front, c.file->data + c.pos, CHUNKSIZE * sizeof(sample_t)); + size_t size = CHUNKSIZE; + if(size > file->size) size = file->size; + memcpy(c.front, c.file->data + c.pos, size * sizeof(sample_t)); + c.ready = false; + //c.pos += size; // Increase audio ref count @@ -148,36 +161,17 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, int id2cache[id] = c; } + // Only load next buffer if there are more data in the file to be loaded... if(initial_samples_needed < file->size) { cevent_t e = createLoadNextEvent(c.file, c.channel, c.pos + CHUNKSIZE, c.back); + e.ready = &id2cache[id].ready; pushEvent(e); } return file->data; // preloaded data } -void CacheManager::close(cacheid_t id) -{ - if(id == CACHE_DUMMYID) return; - - cevent_t e = createCloseEvent(id); - pushEvent(e); - - { -// event_t e = createEvent(id, CLEAN); -// MutexAutolock l(m_events); -// eventqueue.push_front(e); - } - - { - MutexAutolock l(m_ids); - availableids.push_back(id); - } - // Clean cache_t mapped to event - // Decrement audiofile ref count -} - sample_t *CacheManager::next(cacheid_t id, size_t &size) { size = FRAMESIZE; @@ -188,8 +182,13 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) cache_t& c = id2cache[id]; if(c.localpos < CHUNKSIZE) { + sample_t *s = c.front + c.localpos; c.localpos += size; - return c.front + c.localpos; + return s; + } + + if(!c.ready) { + printf("\nNOT READY!\n"); } // Swap buffers @@ -197,22 +196,46 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) c.front = c.back; c.back = tmp; - c.localpos = 0; + 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); + cevent_t e = createLoadNextEvent(c.file, c.channel, c.pos + CHUNKSIZE, c.back); + c.ready = false; + e.ready = &c.ready; pushEvent(e); } return c.front; } +void CacheManager::close(cacheid_t id) +{ +return; + + if(id == CACHE_DUMMYID) return; + + cevent_t e = createCloseEvent(id); + pushEvent(e); + + // Clean cache_t mapped to event + // Decrement audiofile ref count +} + void CacheManager::handleLoadNextEvent(cevent_t &e) { -// memcpy(e.buffer, e.file->data + e.pos, CHUNKSIZE * sizeof(sample_t)); +#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) @@ -220,44 +243,66 @@ 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()) { - cevent_t e = eventqueue.front(); - eventqueue.pop_front(); - m_events.unlock(); - - // TODO: Skip event if e.pos < cache.pos -// if(!e.active) continue; - - switch(e.cmd) { - case LOADNEXT: - handleLoadNextEvent(e); - break; - case CLOSE: - handleCloseEvent(e); - break; - } - } else { + 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) { - // Check that if event should be merged (Maybe by event queue (ie. push in front). + 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(); } diff --git a/src/cachemanager.h b/src/cachemanager.h index 25115c3..f2c0122 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -41,8 +41,9 @@ #define CACHE_DUMMYID -2 #define CACHE_NOID -1 -#define FRAMESIZE 256 -#define CHUNKSIZE FRAMESIZE*100 +#define FRAMESIZE 2048 +#define CHUNKSIZE (FRAMESIZE * 16) +#define PRELOADSIZE (FRAMESIZE + CHUNKSIZE) class AudioFile; typedef int cacheid_t; @@ -88,7 +89,7 @@ public: * This method blocks until the thread has been started. * @param poolsize The maximum number of parellel events supported. */ - void init(size_t poolsize); + void init(size_t poolsize, bool threaded); /** * Stop thread and clean up resources. @@ -138,6 +139,7 @@ private: 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. @@ -157,6 +159,7 @@ private: // For load next event: size_t pos; sample_t *buffer; + volatile bool *ready; AudioFile *file; size_t channel; } cevent_t; @@ -168,6 +171,7 @@ private: void handleLoadNextEvent(cevent_t &e); void handleCloseEvent(cevent_t &e); + void handleEvent(cevent_t &e); void pushEvent(cevent_t e); std::vector id2cache; @@ -179,8 +183,9 @@ private: 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; }; diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index 2f04488..6c40748 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -50,7 +50,7 @@ DrumGizmo::DrumGizmo(AudioOutputEngine *o, AudioInputEngine *i) loader(), oe(o), ie(i) { is_stopping = false; - cacheManager.init(1000); // start thread + cacheManager.init(1000, true); // start thread } DrumGizmo::~DrumGizmo() @@ -415,10 +415,18 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) { 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; if(evt->rampdown == NO_RAMPDOWN) { 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/test/Makefile.am b/test/Makefile.am index 3d0d405..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 cache 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 = \ @@ -16,17 +30,6 @@ engine_SOURCES = \ test.cc \ engine.cc -cache_CXXFLAGS = -DOUTPUT=\"cache\" $(CPPUNIT_CFLAGS) \ - -I$(top_srcdir)/src -I$(top_srcdir)/include \ - -I$(top_srcdir)/hugin -DDISABLE_HUGIN -cache_CFLAGS = -DDISABLE_HUGIN -cache_LDFLAGS = $(CPPUNIT_LIBS) $(DRUMGIZMO_LIBS) $(PTHREAD_LIBS) -cache_SOURCES = \ - $(DRUMGIZMO_SOURCES) \ - $(top_srcdir)/hugin/hugin.c \ - test.cc \ - cache.cc - gui_CXXFLAGS = -DOUTPUT=\"gui\" $(CPPUNIT_CFLAGS) gui_LDFLAGS = $(CPPUNIT_LIBS) gui_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 + +#include +#include + +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 new file mode 100644 index 0000000..3dec8a9 Binary files /dev/null and b/test/kit/ride-multi-channel.wav differ diff --git a/test/kit/ride-single-channel.wav b/test/kit/ride-single-channel.wav new file mode 100644 index 0000000..1760697 Binary files /dev/null and b/test/kit/ride-single-channel.wav differ -- cgit v1.2.3 From 4520f65a409db424d33f5e1edf4dd8eef3449139 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Thu, 23 Apr 2015 15:53:48 +0200 Subject: Fix size checks in initial copy from preloaded data. Re-enable close event. --- src/cachemanager.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index e6c43cc..d7618c0 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -149,7 +149,7 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, c.back = new sample_t[CHUNKSIZE]; size_t size = CHUNKSIZE; - if(size > file->size) size = file->size; + 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; @@ -162,7 +162,7 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, } // Only load next buffer if there are more data in the file to be loaded... - if(initial_samples_needed < file->size) { + if(c.pos + CHUNKSIZE < file->size) { cevent_t e = createLoadNextEvent(c.file, c.channel, c.pos + CHUNKSIZE, c.back); e.ready = &id2cache[id].ready; @@ -212,8 +212,6 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) void CacheManager::close(cacheid_t id) { -return; - if(id == CACHE_DUMMYID) return; cevent_t e = createCloseEvent(id); -- cgit v1.2.3 From 18a5a05fd7a521a93a84cde9b3087a949856a0b9 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sat, 25 Apr 2015 10:55:06 +0200 Subject: Fix invariant about c.pos. It shows the position from which the next read call must be made. --- src/cachemanager.cc | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index d7618c0..61fdb3e 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -152,7 +152,7 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, 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; + c.pos += size; // Increase audio ref count @@ -162,9 +162,9 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, } // Only load next buffer if there are more data in the file to be loaded... - if(c.pos + CHUNKSIZE < file->size) { + if(c.pos < file->size) { cevent_t e = - createLoadNextEvent(c.file, c.channel, c.pos + CHUNKSIZE, c.back); + createLoadNextEvent(c.file, c.channel, c.pos, c.back); e.ready = &id2cache[id].ready; pushEvent(e); } @@ -188,7 +188,7 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) } if(!c.ready) { - printf("\nNOT READY!\n"); + printf("#%d: NOT READY!\n", id); // TODO: Count and show in UI? } // Swap buffers @@ -201,7 +201,7 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) c.pos += CHUNKSIZE; if(c.pos < c.file->size) { - cevent_t e = createLoadNextEvent(c.file, c.channel, c.pos + CHUNKSIZE, c.back); + cevent_t e = createLoadNextEvent(c.file, c.channel, c.pos, c.back); c.ready = false; e.ready = &c.ready; pushEvent(e); @@ -212,13 +212,12 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) void CacheManager::close(cacheid_t id) { - if(id == CACHE_DUMMYID) return; + if(id == CACHE_DUMMYID) { + return; + } cevent_t e = createCloseEvent(id); pushEvent(e); - - // Clean cache_t mapped to event - // Decrement audiofile ref count } void CacheManager::handleLoadNextEvent(cevent_t &e) -- cgit v1.2.3 From badd4bd7812e737300c4069fe85585810ff5bedf Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Sat, 25 Apr 2015 22:42:08 +0200 Subject: Fixed wrong indexing in local buffer caused when FRAMESIZE is not divisible by initial_sample_size. --- src/drumgizmo.cc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index 6c40748..dd3c08d 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -405,6 +405,8 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) continue; } + bool initial = evt->cache_id == CACHE_NOID? 1 : 0; + if(evt->cache_id == CACHE_NOID) { size_t initial_chunksize = (pos + sz) - evt->offset; evt->buffer = @@ -417,8 +419,10 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) 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; + if(initial) { + // If we are not at offset 0 in current buffer: + if(evt->offset > (size_t)pos) n = evt->offset - pos; + } size_t end = sz; // default end point is the end of the buffer. @@ -430,6 +434,7 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) if(end > sz) end = sz; if(evt->rampdown == NO_RAMPDOWN) { + #ifdef SSE size_t optend = ((end - n) / N) * N + n; size_t t; @@ -440,7 +445,10 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) } #endif for(; n < end; n++) { - s[n] += evt->buffer[evt->t % evt->buffer_size]; + if(initial) + s[n] += evt->buffer[evt->t % evt->buffer_size]; + else + s[n] += evt->buffer[(evt->t + evt->offset) % evt->buffer_size]; evt->t++; } } else { // Ramp down in progress. -- cgit v1.2.3 From 779c392e4065027859741dc3dc44222098fdafb7 Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Sun, 26 Apr 2015 07:49:33 +0200 Subject: Fixed wrong position in buffer by adding buffer_offset to all inner loops. --- src/drumgizmo.cc | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index dd3c08d..890de59 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -405,7 +405,7 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) continue; } - bool initial = evt->cache_id == CACHE_NOID? 1 : 0; + size_t buffer_offset = 0; if(evt->cache_id == CACHE_NOID) { size_t initial_chunksize = (pos + sz) - evt->offset; @@ -413,16 +413,17 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) 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; // default start point is 0. - if(initial) { - // If we are not at offset 0 in current buffer: - if(evt->offset > (size_t)pos) n = evt->offset - pos; - } + // If we are not at offset 0 in current buffer: + if(evt->offset > (size_t)pos) n = evt->offset - pos; size_t end = sz; // default end point is the end of the buffer. @@ -434,27 +435,23 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) if(end > sz) end = sz; if(evt->rampdown == NO_RAMPDOWN) { - #ifdef SSE size_t optend = ((end - n) / N) * N + n; size_t t; for(; n < optend; n += N) { - t = evt->t % evt->buffer_size; + t = (evt->t + buffer_offset) % evt->buffer_size; *(vNsf*)&(s[n]) += *(vNsf*)&(evt->buffer[t]); evt->t += N; } #endif for(; n < end; n++) { - if(initial) - s[n] += evt->buffer[evt->t % evt->buffer_size]; - else - s[n] += evt->buffer[(evt->t + evt->offset) % evt->buffer_size]; + s[n] += evt->buffer[ (evt->t + buffer_offset) % evt->buffer_size]; evt->t++; } } else { // Ramp down in progress. for(; n < end && evt->rampdown; n++) { float scale = (float)evt->rampdown/(float)evt->ramp_start; - s[n] += evt->buffer[evt->t % evt->buffer_size] * scale; + s[n] += evt->buffer[ (evt->t + buffer_offset) % evt->buffer_size] * scale; evt->t++; evt->rampdown--; } -- cgit v1.2.3 From c91ae2624f3d3c003c6b2065f3cc128b1b039801 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 26 Apr 2015 09:18:05 +0200 Subject: Simplify (and slightly optimize) buffer counter code. --- src/drumgizmo.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index 890de59..fb4f042 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -434,25 +434,24 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) // 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 size_t optend = ((end - n) / N) * N + n; - size_t t; for(; n < optend; n += N) { - t = (evt->t + buffer_offset) % evt->buffer_size; *(vNsf*)&(s[n]) += *(vNsf*)&(evt->buffer[t]); - evt->t += N; + t += N; } #endif for(; n < end; n++) { - s[n] += evt->buffer[ (evt->t + buffer_offset) % evt->buffer_size]; - 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] += evt->buffer[ (evt->t + buffer_offset) % evt->buffer_size] * scale; - evt->t++; + s[n] += evt->buffer[t] * scale; + t++; evt->rampdown--; } @@ -461,6 +460,7 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) cacheManager.close(evt->cache_id); } } + evt->t += t; // Add internal buffer counter to "global" event counter. if(evt->t >= af->size) { removeevent = true; -- cgit v1.2.3 From 4f81c8e94dd8c9b54ecde9fe7206ee5328eb29f2 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sat, 16 May 2015 08:38:39 +0200 Subject: New ignores. --- .gitignore | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 4cfbcc4..2196119 100644 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,10 @@ Makefile Makefile.in aclocal.m4 autom4te.cache/ +compile config.guess config.h -config.h.in +config.h.in* config.log config.status config.sub @@ -16,6 +17,7 @@ libtool ltmain.sh missing stamp-h1 +test-driver *.o *.a *.la @@ -31,4 +33,6 @@ plugingui/rcgen test/result_*.xml test/resampler test/engine -test/gui \ No newline at end of file +test/gui +drumgizmo-*.tar.gz +tst \ No newline at end of file -- cgit v1.2.3 From 30dde35f3fdd2b165f76f7bceeaef1f112020155 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Fri, 22 May 2015 09:56:17 +0200 Subject: Ignore generated mingw32 makefile. --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2196119..dcaeccb 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,5 @@ test/resampler test/engine test/gui drumgizmo-*.tar.gz -tst \ No newline at end of file +tst +vst/Makefile.mingw32 \ No newline at end of file -- cgit v1.2.3 From acf59a4f960b3e45db566675551b8158b47e1554 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Fri, 22 May 2015 10:04:37 +0200 Subject: Remove semaphore debug output. --- src/semaphore.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/semaphore.cc b/src/semaphore.cc index 47ce8e0..b478eb1 100644 --- a/src/semaphore.cc +++ b/src/semaphore.cc @@ -46,7 +46,7 @@ struct semaphore_private_t { Semaphore::Semaphore(const char *name) { this->name = name; - DEBUG(semaphore, "Create [%s]\n", name); + // DEBUG(semaphore, "Create [%s]\n", name); prv = new struct semaphore_private_t(); @@ -62,7 +62,7 @@ Semaphore::Semaphore(const char *name) Semaphore::~Semaphore() { - DEBUG(semaphore, "Delete [%s]\n", name); + // DEBUG(semaphore, "Delete [%s]\n", name); #ifdef WIN32 CloseHandle(prv->semaphore); @@ -75,7 +75,7 @@ Semaphore::~Semaphore() void Semaphore::post() { - DEBUG(semaphore, "Post [%s]\n", name); + // DEBUG(semaphore, "Post [%s]\n", name); #ifdef WIN32 ReleaseSemaphore(prv->semaphore, 1, NULL); @@ -86,7 +86,7 @@ void Semaphore::post() void Semaphore::wait() { - DEBUG(semaphore, "Wait [%s]\n", name); + // DEBUG(semaphore, "Wait [%s]\n", name); #ifdef WIN32 WaitForSingleObject(prv->semaphore, INFINITE); -- cgit v1.2.3 From bef1d5542f926a3b942374707dd56041013d35ff Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Fri, 22 May 2015 10:34:21 +0200 Subject: Added framesize control mechanism to the engine, and made us of it in the cachemanager and drumkitloader. --- drumgizmo/drumgizmoc.cc | 19 +++++++++++++- drumgizmo/output/alsa/alsa.cc | 12 +++++++++ lv2/lv2.cc | 34 ++++++++++++------------ src/audiooutputengine.h | 5 ---- src/cachemanager.cc | 51 +++++++++++++++++++++++------------- src/cachemanager.h | 9 ++++--- src/drumgizmo.cc | 60 ++++++++++++++++++++++--------------------- src/drumgizmo.h | 21 +++++---------- src/drumkitloader.cc | 12 ++++++++- src/drumkitloader.h | 5 ++++ vst/drumgizmo_vst.cc | 1 + 11 files changed, 140 insertions(+), 89 deletions(-) diff --git a/drumgizmo/drumgizmoc.cc b/drumgizmo/drumgizmoc.cc index d6fea9e..612973f 100644 --- a/drumgizmo/drumgizmoc.cc +++ b/drumgizmo/drumgizmoc.cc @@ -322,6 +322,8 @@ int CliMain::run(int argc, char *argv[]) DrumGizmo gizmo(oe, ie); + gizmo.setFrameSize(oe->getBufferSize()); + if(kitfile == "" || !gizmo.loadkit(kitfile)) { printf("Failed to load \"%s\".\n", kitfile.c_str()); return 1; @@ -349,7 +351,22 @@ int CliMain::run(int argc, char *argv[]) return 1; } - gizmo.run(endpos); + size_t pos = 0; + size_t nsamples = oe->getBufferSize(); + sample_t *samples = (sample_t *)malloc(nsamples * sizeof(sample_t)); + + ie->start(); + oe->start(); + + while(gizmo.run(pos, samples, nsamples) == true) { + pos += nsamples; + if(endpos != -1 && pos >= (size_t)endpos) break; + } + + ie->stop(); + oe->stop(); + + free(samples); printf("Quit.\n"); fflush(stdout); diff --git a/drumgizmo/output/alsa/alsa.cc b/drumgizmo/output/alsa/alsa.cc index dc2ac73..0de3cdb 100644 --- a/drumgizmo/output/alsa/alsa.cc +++ b/drumgizmo/output/alsa/alsa.cc @@ -49,6 +49,7 @@ public: void pre(size_t size); void run(int channel, sample_t* data, size_t size); void post(size_t size); + size_t bufsize(); size_t samplerate(); private: @@ -158,6 +159,11 @@ void Alsa::post(size_t size) snd_pcm_writei(handle, data, size); } +size_t Alsa::bufsize() +{ + return frames; +} + size_t Alsa::samplerate() { return srate; @@ -217,6 +223,12 @@ extern "C" { alsa->post(s); } + size_t bufsize(void *h) + { + Alsa *alsa = (Alsa*)h; + return alsa->bufsize(); + } + size_t samplerate(void *h) { Alsa *alsa = (Alsa*)h; diff --git a/lv2/lv2.cc b/lv2/lv2.cc index d87665d..687f989 100644 --- a/lv2/lv2.cc +++ b/lv2/lv2.cc @@ -47,12 +47,11 @@ static DrumGizmo *dg_get_pci(LV2_Handle instance) return dglv2->dg; } -LV2_State_Status -dg_save(LV2_Handle instance, - LV2_State_Store_Function store, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature *const * features) +LV2_State_Status dg_save(LV2_Handle instance, + LV2_State_Store_Function store, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const * features) { DGLV2 *dglv2 = (DGLV2 *)instance; @@ -77,12 +76,11 @@ dg_save(LV2_Handle instance, return LV2_STATE_SUCCESS; } -LV2_State_Status -dg_restore(LV2_Handle instance, - LV2_State_Retrieve_Function retrieve, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature *const * features) +LV2_State_Status dg_restore(LV2_Handle instance, + LV2_State_Retrieve_Function retrieve, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const * features) { DGLV2 *dglv2 = (DGLV2 *)instance; @@ -148,9 +146,7 @@ LV2_Handle instantiate(const struct _LV2_Descriptor *descriptor, return (LV2_Handle)dglv2; } -void connect_port(LV2_Handle instance, - uint32_t port, - void *data_location) +void connect_port(LV2_Handle instance, uint32_t port, void *data_location) { DGLV2 *dglv2 = (DGLV2 *)instance; @@ -171,12 +167,15 @@ void activate(LV2_Handle instance) (void)dglv2; } -void run(LV2_Handle instance, - uint32_t sample_count) +void run(LV2_Handle instance, uint32_t sample_count) { static size_t pos = 0; DGLV2 *dglv2 = (DGLV2 *)instance; + if(dglv2->buffer_size != sample_count) { + dglv2->buffer_size = sample_count; + dglv2->dg->setFrameSize(sample_count); + } dglv2->dg->run(pos, dglv2->buffer, sample_count); pos += sample_count; @@ -184,7 +183,6 @@ void run(LV2_Handle instance, void deactivate(LV2_Handle instance) { - // We don't really need to do anything here. DGLV2 *dglv2 = (DGLV2 *)instance; dglv2->dg->stop(); } diff --git a/src/audiooutputengine.h b/src/audiooutputengine.h index 7f15e49..8b2b768 100644 --- a/src/audiooutputengine.h +++ b/src/audiooutputengine.h @@ -50,11 +50,6 @@ public: // Reimplement this if you wish to use internal buffer directly. virtual sample_t *getBuffer(int ch) { return NULL; } - - /* - * Overload this method to force engine to use different buffer size. - */ - virtual size_t getBufferSize() { return 1024; } }; #endif/*__DRUMGIZMO_AUDIOOUTPUTENGINE_H__*/ diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 61fdb3e..4053f05 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -28,15 +28,16 @@ #include #include +#include #include #include -static sample_t nodata[FRAMESIZE]; - #define BUFFER_SIZE 4092 +#define CHUNKSIZE(x) (x * CHUNK_MULTIPLIER) + static size_t readChunk(std::string filename, int filechannel, size_t pos, size_t num_samples, sample_t* buf) { @@ -84,21 +85,22 @@ static size_t readChunk(std::string filename, int filechannel, size_t pos, return size; } -CacheManager::CacheManager() {} +CacheManager::CacheManager() + : framesize(0) + , nodata(NULL) +{ +} CacheManager::~CacheManager() { deinit(); + delete[] nodata; } 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); @@ -137,6 +139,7 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, } if(id == CACHE_DUMMYID) { + assert(nodata); return nodata; } @@ -145,10 +148,10 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, c.channel = channel; c.pos = initial_samples_needed; c.localpos = 0; - c.front = new sample_t[CHUNKSIZE]; - c.back = new sample_t[CHUNKSIZE]; + c.front = new sample_t[CHUNKSIZE(framesize)]; + c.back = new sample_t[CHUNKSIZE(framesize)]; - size_t size = CHUNKSIZE; + size_t size = CHUNKSIZE(framesize); 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; @@ -174,21 +177,22 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, sample_t *CacheManager::next(cacheid_t id, size_t &size) { - size = FRAMESIZE; + size = framesize; if(id == CACHE_DUMMYID) { + assert(nodata); return nodata; } cache_t& c = id2cache[id]; - if(c.localpos < CHUNKSIZE) { + if(c.localpos < CHUNKSIZE(framesize)) { 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? + //printf("#%d: NOT READY!\n", id); // TODO: Count and show in UI? } // Swap buffers @@ -198,7 +202,7 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) c.localpos = size; // Next time we go here we have already read the first frame. - c.pos += CHUNKSIZE; + c.pos += CHUNKSIZE(framesize); if(c.pos < c.file->size) { cevent_t e = createLoadNextEvent(c.file, c.channel, c.pos, c.back); @@ -220,17 +224,28 @@ void CacheManager::close(cacheid_t id) pushEvent(e); } +void CacheManager::setFrameSize(size_t framesize) +{ + this->framesize = framesize; + delete[] nodata; + nodata = new sample_t[framesize]; + + for(size_t i = 0; i < framesize; i++) { + nodata[i] = 0; + } +} + void CacheManager::handleLoadNextEvent(cevent_t &e) { #if 0 // memcpy - size_t size = CHUNKSIZE; + size_t size = CHUNKSIZE(framesize); 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); + //memset(e.buffer, 0, CHUNKSIZE(framesize) * sizeof(sample_t)); + readChunk(e.file->filename, e.channel, e.pos, CHUNKSIZE(framesize), e.buffer); #endif *e.ready = true; } @@ -246,7 +261,7 @@ void CacheManager::handleCloseEvent(cevent_t &e) availableids.push_back(e.id); } - // TODO: Count down ref coutner on c.file and close it if 0. + // TODO: Count down ref counter on c.file and close it if 0. } diff --git a/src/cachemanager.h b/src/cachemanager.h index f2c0122..d98d66c 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -41,9 +41,7 @@ #define CACHE_DUMMYID -2 #define CACHE_NOID -1 -#define FRAMESIZE 2048 -#define CHUNKSIZE (FRAMESIZE * 16) -#define PRELOADSIZE (FRAMESIZE + CHUNKSIZE) +#define CHUNK_MULTIPLIER 16 class AudioFile; typedef int cacheid_t; @@ -131,10 +129,15 @@ public: */ void close(cacheid_t id); + void setFrameSize(size_t framesize); + ///! Internal thread main method - needs to be public. void thread_main(); private: + size_t framesize; + sample_t *nodata; + typedef struct { AudioFile *file; size_t channel; diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index fb4f042..8661232 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -46,8 +46,11 @@ #include "nolocale.h" DrumGizmo::DrumGizmo(AudioOutputEngine *o, AudioInputEngine *i) - : MessageReceiver(MSGRCV_ENGINE), - loader(), oe(o), ie(i) + : MessageReceiver(MSGRCV_ENGINE) + , loader() + , oe(o) + , ie(i) + , framesize(0) { is_stopping = false; cacheManager.init(1000, true); // start thread @@ -166,8 +169,30 @@ void DrumGizmo::handleMessage(Message *msg) } } +void DrumGizmo::setFrameSize(size_t framesize) +{ + // If we are resampling override the frame size. + if(resampler[0].ratio() != 1) { + framesize = RESAMPLER_INPUT_BUFFER; + } + + if(this->framesize != framesize) { + printf("New framesize: %d\n", framesize); + + this->framesize = 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); + } +} + bool DrumGizmo::run(size_t pos, sample_t *samples, size_t nsamples) { + setFrameSize(nsamples); + // Handle engine messages, at most one in each iteration: handleMessages(1); @@ -355,26 +380,6 @@ bool DrumGizmo::run(size_t pos, sample_t *samples, size_t nsamples) return true; } -void DrumGizmo::run(int endpos) -{ - size_t pos = 0; - size_t nsamples = oe->getBufferSize(); - sample_t *samples = (sample_t *)malloc(nsamples * sizeof(sample_t)); - - ie->start(); - oe->start(); - - while(run(pos, samples, nsamples) == true) { - pos += nsamples; - if(endpos != -1 && pos >= (size_t)endpos) break; - } - - ie->stop(); - oe->stop(); - - free(samples); -} - #ifdef SSE #define N 8 typedef float vNsf __attribute__ ((vector_size(sizeof(float)*N))); @@ -383,7 +388,7 @@ typedef float vNsf __attribute__ ((vector_size(sizeof(float)*N))); void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) { std::list< Event* >::iterator i = activeevents[ch].begin(); - while(i != activeevents[ch].end()) { + for(; i != activeevents[ch].end(); ++i) { bool removeevent = false; Event *event = *i; @@ -405,17 +410,12 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t 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); @@ -479,7 +479,6 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) i = activeevents[ch].erase(i); continue; } - i++; } } @@ -500,6 +499,9 @@ void DrumGizmo::setSamplerate(int samplerate) for(int i = 0; i < MAX_NUM_CHANNELS; i++) { resampler[i].setup(kit.samplerate(), Conf::samplerate); } + if(resampler[0].ratio() != 1) { + setFrameSize(RESAMPLER_INPUT_BUFFER); + } #endif/*WITH_RESAMPLER*/ } diff --git a/src/drumgizmo.h b/src/drumgizmo.h index a8f007a..4c0740e 100644 --- a/src/drumgizmo.h +++ b/src/drumgizmo.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_DRUMGIZMO_H__ -#define __DRUMGIZMO_DRUMGIZMO_H__ +#pragma once #include #include @@ -52,6 +51,7 @@ #define MAX_NUM_CHANNELS 64 #define REFSFILE "refs.conf" +#define RESAMPLER_INPUT_BUFFER 64 class DrumGizmo : public MessageReceiver { public: @@ -62,10 +62,6 @@ public: bool init(); - /** - * @param endpos number of samples to process, -1 := never stop. - */ - void run(int endpos); bool run(size_t pos, sample_t *samples, size_t nsamples); void stop(); @@ -79,6 +75,8 @@ public: int samplerate(); void setSamplerate(int samplerate); + void setFrameSize(size_t framesize); + private: DrumKitLoader loader; @@ -92,17 +90,12 @@ private: CHResampler resampler[MAX_NUM_CHANNELS]; sample_t resampler_output_buffer[MAX_NUM_CHANNELS][4096]; - sample_t resampler_input_buffer[MAX_NUM_CHANNELS][64]; + sample_t resampler_input_buffer[MAX_NUM_CHANNELS][RESAMPLER_INPUT_BUFFER]; std::map audiofiles; CacheManager cacheManager; - -#ifdef TEST_DRUMGIZMO -public: -#endif DrumKit kit; -}; - -#endif/*__DRUMGIZMO_DRUMGIZMO_H__*/ + size_t framesize; +}; diff --git a/src/drumkitloader.cc b/src/drumkitloader.cc index 2b66ae0..413d3f4 100644 --- a/src/drumkitloader.cc +++ b/src/drumkitloader.cc @@ -34,6 +34,7 @@ DrumKitLoader::DrumKitLoader() : semaphore("drumkitloader") + , framesize(0) { run(); run_semaphore.wait(); // Wait for the thread to actually start. @@ -64,6 +65,13 @@ void DrumKitLoader::skip() load_queue.clear(); } +void DrumKitLoader::setFrameSize(size_t framesize) +{ + MutexAutolock l(mutex); + this->framesize = framesize; + framesize_semaphore.post(); // Signal that the framesize has been set. +} + bool DrumKitLoader::isDone() { MutexAutolock l(mutex); @@ -120,6 +128,8 @@ void DrumKitLoader::thread_main() run_semaphore.post(); // Signal that the thread has been started. + framesize_semaphore.wait(); // Wait until the framesize has been set. + while(running) { size_t size; { @@ -137,7 +147,7 @@ void DrumKitLoader::thread_main() AudioFile *audiofile = load_queue.front(); load_queue.pop_front(); filename = audiofile->filename; - audiofile->load(PRELOADSIZE); + audiofile->load(framesize * CHUNK_MULTIPLIER + framesize); } loaded++; diff --git a/src/drumkitloader.h b/src/drumkitloader.h index 2c0ea8e..550d885 100644 --- a/src/drumkitloader.h +++ b/src/drumkitloader.h @@ -85,15 +85,20 @@ public: */ void skip(); + void setFrameSize(size_t framesize); + private: Semaphore run_semaphore; Semaphore semaphore; + Semaphore framesize_semaphore; Mutex mutex; volatile bool running; std::list load_queue; size_t total_num_audiofiles; size_t fraction; size_t loaded; + + size_t framesize; }; #endif/*__DRUMGIZMO_DRUMKITLOADER_H__*/ diff --git a/vst/drumgizmo_vst.cc b/vst/drumgizmo_vst.cc index 6aec4f2..1e40852 100644 --- a/vst/drumgizmo_vst.cc +++ b/vst/drumgizmo_vst.cc @@ -450,6 +450,7 @@ void DrumGizmoVst::processReplacing(float** inputs, float** outputs, if(buffer) free(buffer); buffer_size = sampleFrames; buffer = (sample_t*)malloc(sizeof(sample_t) * buffer_size); + drumgizmo->setFrameSize(buffer_size); } drumgizmo->run(pos, buffer, buffer_size); -- cgit v1.2.3 From 2928c4624bd0b970881dc6bcfda6892353eb63e7 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 28 Jun 2015 08:32:43 +0200 Subject: Fix warning. --- plugingui/font.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugingui/font.cc b/plugingui/font.cc index 828286c..f96bd95 100644 --- a/plugingui/font.cc +++ b/plugingui/font.cc @@ -68,7 +68,7 @@ std::string GUI::Font::face() void GUI::Font::setSize(size_t points) { - points = points; + (void)points; } size_t GUI::Font::size() -- cgit v1.2.3 From 94b45fce79d371a80243b923c76e65d8606e737b Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 28 Jun 2015 08:33:02 +0200 Subject: Fix warning. --- plugingui/widget.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugingui/widget.cc b/plugingui/widget.cc index 05966aa..b3e3cc8 100644 --- a/plugingui/widget.cc +++ b/plugingui/widget.cc @@ -134,7 +134,7 @@ GUI::Widget *GUI::Widget::find(size_t x, size_t y) i++; } - if(x > width() || x < 0 || y > height() || y < 0) return NULL; + if(x > width() /*|| x < 0*/ || y > height() /*|| y < 0*/) return NULL; return this; } -- cgit v1.2.3 From 59c16d2e8a424f0f4c2a93888ae78b945bd8defb Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 28 Jun 2015 16:22:11 +0200 Subject: Fix read of memory that was alreay freed. Fix misaligned SIMD copy. --- src/drumgizmo.cc | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index 8661232..28ac2c6 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -430,15 +430,22 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) // 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 + // This should not be necessary but make absolutely sure 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) { + if(n > 0) { + // We cannot use SIMD on this buffer. + for(; n < end; n++) { + s[n] += evt->buffer[t]; + t++; + } + } #ifdef SSE size_t optend = ((end - n) / N) * N + n; - for(; n < optend; n += N) { + for(; (n < optend) && (t < evt->buffer_size); n += N) { *(vNsf*)&(s[n]) += *(vNsf*)&(evt->buffer[t]); t += N; } @@ -454,21 +461,19 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) 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 { + if((evt->t < af->size) && (evt->rampdown != 0)) { evt->buffer = cacheManager.next(evt->cache_id, evt->buffer_size); + } else { + removeevent = true; } + if(removeevent) { + cacheManager.close(evt->cache_id); + } } } break; -- cgit v1.2.3 From 79e1c5eb90fb754f6cd40f5e32d9a1b7284c9717 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 28 Jun 2015 20:15:08 +0200 Subject: Fix unit tests to run with new setFramesize feature. --- test/cachemanagertest.cc | 4 ++++ test/engine.cc | 1 + 2 files changed, 5 insertions(+) diff --git a/test/cachemanagertest.cc b/test/cachemanagertest.cc index ae346db..e1d8289 100644 --- a/test/cachemanagertest.cc +++ b/test/cachemanagertest.cc @@ -29,6 +29,8 @@ #include #include +#define FRAMESIZE 1024 + class test_cachemanager : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(test_cachemanager); @@ -60,6 +62,8 @@ public: printf("cm.init\n"); cm.init(100, threaded); + cm.setFrameSize(FRAMESIZE); + cacheid_t id; // TODO: test 0 ... FRAMESIZE - 1 size_t initial_samples_needed = (FRAMESIZE - 1) / 2; diff --git a/test/engine.cc b/test/engine.cc index 69d2a37..4c49a6c 100644 --- a/test/engine.cc +++ b/test/engine.cc @@ -43,6 +43,7 @@ public: AudioOutputEngine *oe = NULL; AudioInputEngine *ie = NULL; DrumGizmo dg(oe, ie); + dg.setFrameSize(100); // Switch kits emmidiately with giving the loader time to work: for(int i = 0; i < 100; i++) { -- cgit v1.2.3 From a73382d1685fa864a3d4a46be486caf028523dae Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Fri, 10 Jul 2015 09:32:38 +0200 Subject: Make test input module work in a more logical way. --- drumgizmo/input/test/test.cc | 48 ++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/drumgizmo/input/test/test.cc b/drumgizmo/input/test/test.cc index b7b4a7d..225a7a2 100644 --- a/drumgizmo/input/test/test.cc +++ b/drumgizmo/input/test/test.cc @@ -31,7 +31,11 @@ class Test { public: - Test() { p = 0.1; instr = -1; len = -1; } + Test() + : p(0.1) + , instr(-1) + {} + ~Test() {} bool init(int instruments, char *inames[]); @@ -48,11 +52,12 @@ public: private: float p; int instr; - int len; + int num_instruments; }; bool Test::init(int instruments, char *inames[]) { + num_instruments = instruments; return true; } @@ -60,7 +65,6 @@ void Test::setParm(std::string parm, std::string value) { if(parm == "p") p = atof(value.c_str()); if(parm == "instr") instr = atoi(value.c_str()); - if(parm == "len") len = atoi(value.c_str()); } bool Test::start() @@ -76,23 +80,37 @@ void Test::pre() { } +#define BUFFER_MAX 1000 event_t *Test::run(size_t pos, size_t nsamples, size_t *nevents) { - if((float)rand() / (float)RAND_MAX > p) { - *nevents = 0; - return NULL; - } + *nevents = 0; + event_t *evs = (event_t *)malloc(sizeof(event_t) * BUFFER_MAX); + + for(size_t i = 0; i < nsamples; ++i) { + if((float)rand() / (float)RAND_MAX > p) { + continue; + } + + evs[*nevents].type = TYPE_ONSET; - *nevents = 1; - event_t *evs = (event_t *)malloc(sizeof(event_t)); - evs[0].type = TYPE_ONSET; - if(len != -1 && (int)pos > len * 44100) evs[0].type = TYPE_STOP; + if(instr != -1) { + // Use specified instrument + evs[*nevents].instrument = instr; + } else { + // Use random instrument + evs[*nevents].instrument = rand() % num_instruments; + } - if(instr != -1) evs[0].instrument = instr; - else evs[0].instrument = rand() % 32; + evs[*nevents].velocity = (float)rand()/(float)RAND_MAX; + evs[*nevents].offset = nsamples?rand()%nsamples:0; + + (*nevents)++; + + if(*nevents == BUFFER_MAX) { + break; + } + } - evs[0].velocity = (float)rand()/(float)RAND_MAX; - evs[0].offset = nsamples?rand()%nsamples:0; return evs; } -- cgit v1.2.3 From 3cc158998d18822ac253ef3eae9bf182df1b1d38 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Fri, 10 Jul 2015 09:33:55 +0200 Subject: Fix channel swapping when playing samples after the initially loaded. --- src/audiofile.h | 3 ++- src/drumgizmo.cc | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/audiofile.h b/src/audiofile.h index 5f93584..aefb8d6 100644 --- a/src/audiofile.h +++ b/src/audiofile.h @@ -99,10 +99,11 @@ public: Mutex mutex; + int filechannel; + private: void *magic; volatile bool is_loaded; - int filechannel; }; #endif/*__DRUMGIZMO_AUDIOFILE_H__*/ diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index 28ac2c6..e7843d4 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -412,8 +412,8 @@ 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, ch, evt->cache_id); + evt->buffer = cacheManager.open(af, initial_chunksize, + af->filechannel, evt->cache_id); evt->buffer_size = initial_chunksize; } -- cgit v1.2.3 From 674e3694ac724b1bed475e60c0ab70139ceba286 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Fri, 17 Jul 2015 09:56:46 +0200 Subject: Fix event body size. --- test/lv2_test_host.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/lv2_test_host.cc b/test/lv2_test_host.cc index 07685d0..375ae40 100644 --- a/test/lv2_test_host.cc +++ b/test/lv2_test_host.cc @@ -166,10 +166,11 @@ void LV2TestHost::Sequence::clear() // Keep this to support atom extension from lv2 < 1.10 static inline LV2_Atom_Event* _lv2_atom_sequence_append_event(LV2_Atom_Sequence* seq, - uint32_t capacity, - const LV2_Atom_Event* event) + uint32_t capacity, + const LV2_Atom_Event* event) { const uint32_t total_size = (uint32_t)sizeof(*event) + event->body.size; + if (capacity - seq->atom.size < total_size) { return NULL; } @@ -195,7 +196,7 @@ void LV2TestHost::Sequence::addMidiNote(uint64_t pos, MIDINoteEvent ev; ev.event.time.frames = pos;// sample position ev.event.body.type = map.map(map.handle, LV2_MIDI__MidiEvent); - ev.event.body.size = sizeof(MIDINoteEvent); + ev.event.body.size = sizeof(ev.msg); ev.msg[0] = note_on; ev.msg[1] = key; -- cgit v1.2.3 From a440d77c3396d26ebf81f178a933b106f8d2fefb Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 19 Jul 2015 13:54:15 +0200 Subject: Fix compiling with -Wall/error but no hugin. --- drumgizmo/drumgizmoc.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drumgizmo/drumgizmoc.cc b/drumgizmo/drumgizmoc.cc index 612973f..6111c72 100644 --- a/drumgizmo/drumgizmoc.cc +++ b/drumgizmo/drumgizmoc.cc @@ -122,8 +122,10 @@ int CliMain::run(int argc, char *argv[]) { int c; +#ifndef DISABLE_HUGIN const char *hugin_filter = "+all"; unsigned int hugin_flags = HUG_FLAG_OUTPUT_TO_STDOUT; +#endif/*DISABLE_HUGIN*/ std::string outputengine; std::string inputengine; -- cgit v1.2.3 From ea2d9e8ee5db8ae5ec1aaa15eebda3296a027baf Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 19 Jul 2015 13:54:33 +0200 Subject: Fix compiling with -Wall/error but no hugin. --- lv2/input_lv2.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lv2/input_lv2.cc b/lv2/input_lv2.cc index e70d293..f65cf83 100644 --- a/lv2/input_lv2.cc +++ b/lv2/input_lv2.cc @@ -86,7 +86,7 @@ event_t *InputLV2::run(size_t pos, size_t len, size_t *nevents) if ((data[0] & 0xF0) == 0x80) { // note off int key = data[1]; - + (void)key; DEBUG(lv2input, "Event (off) key:%d\n", key); } -- cgit v1.2.3 From e35b527e8bc261bb1f4ecbff474d1ff6fcd1db60 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 19 Jul 2015 13:55:22 +0200 Subject: Fix loading with num_samples==ALL_SAMPLES (-1). --- src/audiofile.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/audiofile.cc b/src/audiofile.cc index 7ab21a9..5ceff59 100644 --- a/src/audiofile.cc +++ b/src/audiofile.cc @@ -114,15 +114,15 @@ void AudioFile::load(int num_samples) return; } + if(num_samples == ALL_SAMPLES) { + num_samples = sf_info.frames; + } + 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)preloadedsize > num_samples) preloadedsize = num_samples; + if(preloadedsize > (size_t)num_samples) { + preloadedsize = num_samples; } sample_t* data = new sample_t[preloadedsize]; @@ -139,7 +139,7 @@ void AudioFile::load(int num_samples) int read; do { read = sf_readf_float(fh, buffer, readsize); - for (int i = 0; i < read && totalread < num_samples; i++) { + for (int i = 0; (i < read) && (totalread < num_samples); i++) { data[totalread++] = buffer[i * sf_info.channels + filechannel]; } } while( (read > 0) && @@ -148,7 +148,7 @@ void AudioFile::load(int num_samples) // set data size to total bytes read preloadedsize = totalread; } - + DEBUG(audiofile,"Loaded %d samples %p\n", (int)preloadedsize, this); sf_close(fh); -- cgit v1.2.3 From 83f15563dabdb25224abdda6df5d731148bed335 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 19 Jul 2015 13:58:41 +0200 Subject: Fix cachemanager test. --- test/cachemanagertest.cc | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/test/cachemanagertest.cc b/test/cachemanagertest.cc index e1d8289..d521f83 100644 --- a/test/cachemanagertest.cc +++ b/test/cachemanagertest.cc @@ -35,7 +35,7 @@ class test_cachemanager : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(test_cachemanager); CPPUNIT_TEST(singlechannel_nonthreaded); - CPPUNIT_TEST(singlechannel_threaded); + CPPUNIT_TEST(singlechannel_threaded); CPPUNIT_TEST(multichannel_nonthreaded); CPPUNIT_TEST(multichannel_threaded); CPPUNIT_TEST_SUITE_END(); @@ -55,8 +55,8 @@ public: // Input file: AudioFile af(filename, channel); printf("af.load\n"); - af.load(ALL_SAMPLES); - //af.load(PRELOADSIZE); + //af.load(ALL_SAMPLES); + af.load(4096); CacheManager cm; printf("cm.init\n"); @@ -68,7 +68,7 @@ public: // TODO: test 0 ... FRAMESIZE - 1 size_t initial_samples_needed = (FRAMESIZE - 1) / 2; - printf("open\n"); + printf("open: initial_samples_needed: %d\n", initial_samples_needed); sample_t *s = cm.open(&af, initial_samples_needed, channel, id); size_t size = initial_samples_needed; size_t offset = 0; @@ -90,7 +90,12 @@ public: //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++) { + for(size_t i = 0; (i < size) && (offset < afref.size); i++) { + /* + if(afref.data[offset] != s[i]) { + printf("offset: %d, size: %d, diff: %d\n", offset, afref.size, afref.size - offset); + } + */ CPPUNIT_ASSERT_EQUAL(afref.data[offset], s[i]); offset++; } @@ -101,6 +106,7 @@ public: void singlechannel_nonthreaded() { + printf("\nsinglechannel_nonthreaded()\n"); const char filename[] = "kit/ride-single-channel.wav"; int channel = 0; bool threaded = false; @@ -109,6 +115,7 @@ public: void singlechannel_threaded() { + printf("\nsinglechannel_threaded()\n"); const char filename[] = "kit/ride-single-channel.wav"; int channel = 0; bool threaded = true; @@ -117,6 +124,7 @@ public: void multichannel_nonthreaded() { + printf("\nmultichannel_nonthreaded()\n"); const char filename[] = "kit/ride-multi-channel.wav"; int channel = 0; bool threaded = false; @@ -125,6 +133,7 @@ public: void multichannel_threaded() { + printf("\nmultichannel_threaded()\n"); const char filename[] = "kit/ride-multi-channel.wav"; int channel = 0; bool threaded = true; -- cgit v1.2.3 From c7005700ef35d91176c4f347bfd876104ced6a33 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 19 Jul 2015 13:59:17 +0200 Subject: Remove unused variable. --- test/lv2_test_host.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/lv2_test_host.h b/test/lv2_test_host.h index 04f48c6..f0677c7 100644 --- a/test/lv2_test_host.h +++ b/test/lv2_test_host.h @@ -67,8 +67,6 @@ public: int loadConfig(const char *config, size_t size); int run(int num_samples); - - private: LilvWorld* world; const LilvPlugins* plugins; @@ -76,8 +74,6 @@ private: const LilvPlugin* plugin; LilvInstance* instance; - - size_t buffer_size; }; #endif/*__DRUMGIZMO_LV2_TEST_HOST_H__*/ -- cgit v1.2.3 From b06eaaff85cb086829a58d152e1ba7d3206fc368 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 19 Jul 2015 14:03:09 +0200 Subject: Added new nth argument to test input module which plays a note each nth sample. Overrides/disables p argument. --- drumgizmo/input/test/test.cc | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drumgizmo/input/test/test.cc b/drumgizmo/input/test/test.cc index 225a7a2..6032f02 100644 --- a/drumgizmo/input/test/test.cc +++ b/drumgizmo/input/test/test.cc @@ -34,6 +34,8 @@ public: Test() : p(0.1) , instr(-1) + , nth(-1) + , nth_counter(0) {} ~Test() {} @@ -52,6 +54,8 @@ public: private: float p; int instr; + int nth; + int nth_counter; int num_instruments; }; @@ -65,6 +69,7 @@ void Test::setParm(std::string parm, std::string value) { if(parm == "p") p = atof(value.c_str()); if(parm == "instr") instr = atoi(value.c_str()); + if(parm == "nth") nth = atoi(value.c_str()); } bool Test::start() @@ -87,8 +92,17 @@ event_t *Test::run(size_t pos, size_t nsamples, size_t *nevents) event_t *evs = (event_t *)malloc(sizeof(event_t) * BUFFER_MAX); for(size_t i = 0; i < nsamples; ++i) { - if((float)rand() / (float)RAND_MAX > p) { - continue; + + if(nth != -1) { + if(nth_counter < nth) { + ++nth_counter; + continue; + } + nth_counter = 0; + } else { + if((float)rand() / (float)RAND_MAX > p) { + continue; + } } evs[*nevents].type = TYPE_ONSET; -- cgit v1.2.3 From d35c16931056ec4b07706e3955f6198ce0e43cf7 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 19 Jul 2015 14:06:04 +0200 Subject: The SIMD implementation is broken with the current cachemanager version and does not add much to the performance anyway; so now it is disabled. --- src/drumgizmo.cc | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index e7843d4..528b542 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -380,9 +380,10 @@ bool DrumGizmo::run(size_t pos, sample_t *samples, size_t nsamples) return true; } +#undef SSE // SSE broken for now ... so disable it. #ifdef SSE #define N 8 -typedef float vNsf __attribute__ ((vector_size(sizeof(float)*N))); +typedef float vNsf __attribute__ ((vector_size(sizeof(sample_t)*N))); #endif/*SSE*/ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) @@ -423,39 +424,45 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) 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; + if(evt->offset > (size_t)pos) { + n = evt->offset - pos; + } 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; + if((evt->t + end - n) > af->size) { + end = af->size - evt->t + n; + } // This should not be necessary but make absolutely sure that we do // not write over the end of the buffer. - if(end > sz) end = sz; + if(end > sz) { + end = sz; + } size_t t = 0; // Internal buffer counter if(evt->rampdown == NO_RAMPDOWN) { - if(n > 0) { - // We cannot use SIMD on this buffer. - for(; n < end; n++) { - s[n] += evt->buffer[t]; - t++; - } - } + #ifdef SSE size_t optend = ((end - n) / N) * N + n; + + // Force source addr to be 16 byte aligned... (might skip 1 or 2 samples) + while((size_t)&evt->buffer[t] % 16) { + ++t; + } + for(; (n < optend) && (t < evt->buffer_size); n += N) { *(vNsf*)&(s[n]) += *(vNsf*)&(evt->buffer[t]); t += N; - } + } #endif - for(; n < end; n++) { + for(; (n < end) && (t < evt->buffer_size); n++) { s[n] += evt->buffer[t]; t++; } } else { // Ramp down in progress. - for(; n < end && evt->rampdown; n++) { + for(; (n < end) && (t < evt->buffer_size) && evt->rampdown; n++) { float scale = (float)evt->rampdown/(float)evt->ramp_start; s[n] += evt->buffer[t] * scale; t++; @@ -463,7 +470,7 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t *s, size_t sz) } } - evt->t += t; // Add internal buffer counter to "global" event counter. + 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); -- cgit v1.2.3 From 7f19dfdba76e9d127f8f6c1e1c3c547755e7d7c1 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 19 Jul 2015 14:06:10 +0200 Subject: Make cachemanager behave as if it isn't there when CHUNK_MULTIPLIER is big enough to contain all data in an audio file. Add refcounted file handling in cachemanager. --- src/cachemanager.cc | 237 +++++++++++++++++++++++++++++++++++----------------- src/cachemanager.h | 13 ++- 2 files changed, 171 insertions(+), 79 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 4053f05..63f99bc 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -34,23 +34,48 @@ #include -#define BUFFER_SIZE 4092 - #define CHUNKSIZE(x) (x * CHUNK_MULTIPLIER) -static size_t readChunk(std::string filename, int filechannel, size_t pos, - size_t num_samples, sample_t* buf) -{ +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; - 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; + std::string filename; +}; + +static void readChunk(AFile* file, int filechannel, size_t pos, + size_t num_samples, sample_t* buf) +{ + 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) { - return 0; + printf("pos > sf_info.frames\n"); + return; } sf_seek(fh, pos, SEEK_SET); @@ -58,31 +83,24 @@ static size_t readChunk(std::string filename, int filechannel, size_t pos, 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; + 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. } - sf_close(fh); + size_t read_size = sf_readf_float(fh, read_buffer, size); + (void)read_size; - return size; + size_t channel = filechannel; + sample_t *data = buf; + for (size_t i = 0; i < size; i++) { + data[i] = read_buffer[(i * sf_info.channels) + channel]; + } } CacheManager::CacheManager() @@ -124,10 +142,16 @@ void CacheManager::deinit() } // 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) { + 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()) { @@ -143,36 +167,67 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, 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.file = file; + c.file = afile; c.channel = channel; - c.pos = initial_samples_needed; - c.localpos = 0; - c.front = new sample_t[CHUNKSIZE(framesize)]; - c.back = new sample_t[CHUNKSIZE(framesize)]; - - size_t size = CHUNKSIZE(framesize); - if(size > (file->preloadedsize - c.pos)) size = (file->preloadedsize - c.pos); - memcpy(c.front, c.file->data + c.pos, size * sizeof(sample_t)); + + // 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; + c.ready = false; - c.pos += size; - // Increase audio ref count + // 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.file, c.channel, c.pos, c.back); e.ready = &id2cache[id].ready; pushEvent(e); } - return file->data; // preloaded data + return c.preloaded_samples; // return preloaded data } sample_t *CacheManager::next(cacheid_t id, size_t &size) @@ -185,32 +240,58 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) } cache_t& c = id2cache[id]; - if(c.localpos < CHUNKSIZE(framesize)) { - sample_t *s = c.front + c.localpos; - c.localpos += size; - return s; + + 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? + printf("#%d: NOT READY!\n", id); // TODO: Count and show in UI? + return nodata; } // Swap buffers - sample_t *tmp = c.front; - c.front = c.back; - c.back = tmp; + std::swap(c.front, c.back); - c.localpos = size; // Next time we go here we have already read the first frame. + // Next time we go here we have already read the first frame. + c.localpos = framesize; c.pos += CHUNKSIZE(framesize); - if(c.pos < c.file->size) { + if(c.pos < c.file->sf_info.frames) { + if(c.back == NULL) { + c.back = new sample_t[CHUNKSIZE(framesize)]; + } + cevent_t e = createLoadNextEvent(c.file, c.channel, c.pos, c.back); c.ready = false; e.ready = &c.ready; pushEvent(e); } + if(!c.front) { + printf("We shouldn't get here... ever!\n"); + assert(false); + return nodata; + } + return c.front; } @@ -226,33 +307,40 @@ void CacheManager::close(cacheid_t id) void CacheManager::setFrameSize(size_t framesize) { - this->framesize = framesize; - delete[] nodata; - nodata = new sample_t[framesize]; + if(framesize > this->framesize) { + delete[] nodata; + nodata = new sample_t[framesize]; - for(size_t i = 0; i < framesize; i++) { - nodata[i] = 0; + for(size_t i = 0; i < framesize; i++) { + nodata[i] = 0; + } } + + this->framesize = framesize; } void CacheManager::handleLoadNextEvent(cevent_t &e) { -#if 0 // memcpy - size_t size = CHUNKSIZE(framesize); - 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(framesize) * sizeof(sample_t)); - readChunk(e.file->filename, e.channel, e.pos, CHUNKSIZE(framesize), e.buffer); -#endif + assert(files.find(e.file->filename) != files.end()); + readChunk(files[e.file->filename], e.channel, e.pos, CHUNKSIZE(framesize), e.buffer); *e.ready = true; } void CacheManager::handleCloseEvent(cevent_t &e) { cache_t& c = id2cache[e.id]; + + auto f = files.find(c.file->filename); + + if(f != files.end()) { + // Decrease ref count and close file if needed (in AFile destructor). + files[c.file->filename]->ref--; + if(files[c.file->filename]->ref == 0) { + delete f->second; + files.erase(f); + } + } + delete[] c.front; delete[] c.back; @@ -260,8 +348,6 @@ void CacheManager::handleCloseEvent(cevent_t &e) MutexAutolock l(m_ids); availableids.push_back(e.id); } - - // TODO: Count down ref counter on c.file and close it if 0. } @@ -308,8 +394,7 @@ void CacheManager::pushEvent(cevent_t e) return; } - // Check that if event should be merged (Maybe by event queue (ie. push - // in front). + // TODO: Check if event should be merged. { MutexAutolock l(m_events); eventqueue.push_back(e); @@ -319,7 +404,7 @@ void CacheManager::pushEvent(cevent_t e) } CacheManager::cevent_t -CacheManager::createLoadNextEvent(AudioFile *file, size_t channel, size_t pos, +CacheManager::createLoadNextEvent(AFile *file, size_t channel, size_t pos, sample_t* buffer) { cevent_t e; diff --git a/src/cachemanager.h b/src/cachemanager.h index d98d66c..a70af35 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -45,6 +45,7 @@ class AudioFile; typedef int cacheid_t; +class AFile; //TODO: @@ -139,13 +140,17 @@ private: sample_t *nodata; typedef struct { - AudioFile *file; + AFile *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. + + sample_t* preloaded_samples; // NULL means not active. + size_t preloaded_samples_size; + } cache_t; typedef enum { @@ -163,11 +168,11 @@ private: size_t pos; sample_t *buffer; volatile bool *ready; - AudioFile *file; size_t channel; + AFile *file; } cevent_t; - cevent_t createLoadNextEvent(AudioFile *file, size_t channel, size_t pos, + cevent_t createLoadNextEvent(AFile *file, size_t channel, size_t pos, sample_t* buffer); cevent_t createCloseEvent(cacheid_t id); @@ -190,6 +195,8 @@ private: Semaphore sem; Semaphore sem_run; bool running; + + std::map files; }; #endif/*__DRUMGIZMO_CACHEMANAGER_H__*/ -- cgit v1.2.3 From cc9b5ff6d194dc8ead042c5d129ea07e8e68382d Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 19 Jul 2015 16:03:27 +0200 Subject: Collapse LOADNEXT events if they share filename and position. --- src/cachemanager.cc | 98 +++++++++++++++++++++++++++++++++++------------------ src/cachemanager.h | 28 +++++++++------ 2 files changed, 82 insertions(+), 44 deletions(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 63f99bc..dc4b3e7 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -62,8 +62,9 @@ public: std::string filename; }; -static void readChunk(AFile* file, int filechannel, size_t pos, - size_t num_samples, sample_t* buf) +static void readChunk(AFile* file, + std::list& channels, + size_t pos, size_t num_samples) { SNDFILE* fh = file->fh; SF_INFO& sf_info = file->sf_info; @@ -96,10 +97,16 @@ static void readChunk(AFile* file, int filechannel, size_t pos, size_t read_size = sf_readf_float(fh, read_buffer, size); (void)read_size; - size_t channel = filechannel; - sample_t *data = buf; - 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) { + 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; } } @@ -179,7 +186,7 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, afile->ref++; cache_t c; - c.file = afile; + c.afile = afile; c.channel = channel; // next call to 'next' will read from this point. @@ -202,8 +209,6 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, c.preloaded_samples = file->data; c.preloaded_samples_size = cropped_size; - c.ready = false; - // next read from disk will read from this point. c.pos = cropped_size;//c.preloaded_samples_size; @@ -222,8 +227,7 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed, } cevent_t e = - createLoadNextEvent(c.file, c.channel, c.pos, c.back); - e.ready = &id2cache[id].ready; + createLoadNextEvent(c.afile, c.channel, c.pos, c.back, &c.ready); pushEvent(e); } @@ -275,14 +279,12 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size) c.pos += CHUNKSIZE(framesize); - if(c.pos < c.file->sf_info.frames) { + if(c.pos < c.afile->sf_info.frames) { if(c.back == NULL) { c.back = new sample_t[CHUNKSIZE(framesize)]; } - cevent_t e = createLoadNextEvent(c.file, c.channel, c.pos, c.back); - c.ready = false; - e.ready = &c.ready; + cevent_t e = createLoadNextEvent(c.afile, c.channel, c.pos, c.back, &c.ready); pushEvent(e); } @@ -321,23 +323,26 @@ void CacheManager::setFrameSize(size_t framesize) void CacheManager::handleLoadNextEvent(cevent_t &e) { - assert(files.find(e.file->filename) != files.end()); - readChunk(files[e.file->filename], e.channel, e.pos, CHUNKSIZE(framesize), e.buffer); - *e.ready = true; + assert(files.find(e.afile->filename) != files.end()); + readChunk(files[e.afile->filename], e.channels, e.pos, CHUNKSIZE(framesize)); } void CacheManager::handleCloseEvent(cevent_t &e) { cache_t& c = id2cache[e.id]; - auto f = files.find(c.file->filename); + { + MutexAutolock l(m_events); - if(f != files.end()) { - // Decrease ref count and close file if needed (in AFile destructor). - files[c.file->filename]->ref--; - if(files[c.file->filename]->ref == 0) { - delete f->second; - files.erase(f); + 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); + } } } @@ -387,32 +392,59 @@ void CacheManager::thread_main() } } -void CacheManager::pushEvent(cevent_t e) +void CacheManager::pushEvent(cevent_t& e) { if(!threaded) { handleEvent(e); return; } - // TODO: Check if event should be merged. { MutexAutolock l(m_events); - eventqueue.push_back(e); + + 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(); } CacheManager::cevent_t -CacheManager::createLoadNextEvent(AFile *file, size_t channel, size_t pos, - sample_t* buffer) +CacheManager::createLoadNextEvent(AFile *afile, size_t channel, size_t pos, + sample_t* buffer, volatile bool* ready) { cevent_t e; e.cmd = LOADNEXT; e.pos = pos; - e.buffer = buffer; - e.file = file; - e.channel = channel; + e.afile = afile; + + CacheManager::Channel c; + c.channel = channel; + c.samples = buffer; + + *ready = false; + c.ready = ready; + + e.channels.insert(e.channels.end(), c); + return e; } diff --git a/src/cachemanager.h b/src/cachemanager.h index a70af35..4fe813e 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -135,12 +135,20 @@ public: ///! 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; + }; + private: size_t framesize; sample_t *nodata; typedef struct { - AFile *file; + AFile *afile; size_t channel; size_t pos; //< File possition volatile bool ready; @@ -166,21 +174,19 @@ private: // For load next event: size_t pos; - sample_t *buffer; - volatile bool *ready; - size_t channel; - AFile *file; + AFile *afile; + std::list channels; } cevent_t; - cevent_t createLoadNextEvent(AFile *file, size_t channel, size_t pos, - sample_t* buffer); + cevent_t createLoadNextEvent(AFile *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 handleLoadNextEvent(cevent_t& e); + void handleCloseEvent(cevent_t& e); - void handleEvent(cevent_t &e); - void pushEvent(cevent_t e); + void handleEvent(cevent_t& e); + void pushEvent(cevent_t& e); std::vector id2cache; -- cgit v1.2.3 From ec4eb3f535c358e67eb9ae0989b2163b20728d0b Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Mon, 27 Jul 2015 19:36:28 +0200 Subject: Add free-wheel mode to LV2 and DrumGizmo class. --- lv2/drumgizmo.ttl | 47 +++++++++++++++++++++++++++++------------------ lv2/lv2.cc | 35 ++++++++++++++++++++++++++--------- lv2/lv2_instance.h | 2 ++ src/cachemanager.cc | 9 ++++++++- src/cachemanager.h | 9 +++++++++ src/drumgizmo.cc | 11 +++++++++++ src/drumgizmo.h | 3 +++ 7 files changed, 88 insertions(+), 28 deletions(-) diff --git a/lv2/drumgizmo.ttl b/lv2/drumgizmo.ttl index 23345f3..6ce3e91 100644 --- a/lv2/drumgizmo.ttl +++ b/lv2/drumgizmo.ttl @@ -37,108 +37,119 @@ lv2:optionalFeature ; lv2:optionalFeature ; lv2:extensionData state:interface ; -lv2:port [ + lv2:port [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 0 ; + lv2:symbol "lv2_freewheel" ; + lv2:name "Freewheel" ; + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 1.0 ; + lv2:designation ; + lv2:portProperty lv2:toggled ; + lv2:portProperty epp:hasStrictBounds; + ] , [ a atom:AtomPort , lv2:InputPort; atom:bufferType atom:Sequence ; atom:supports ; - lv2:index 0 ; + lv2:index 1 ; lv2:symbol "control" ; lv2:name "Control" ] , [ a lv2:AudioPort , lv2:OutputPort ; - lv2:index 1 ; + lv2:index 2 ; lv2:symbol "out1" ; lv2:name "Out1" ], [ a lv2:AudioPort , lv2:OutputPort ; - lv2:index 2 ; + lv2:index 3 ; lv2:symbol "out2" ; lv2:name "Out2" ], [ a lv2:AudioPort , lv2:OutputPort ; - lv2:index 3 ; + lv2:index 4 ; lv2:symbol "out3" ; lv2:name "Out3" ], [ a lv2:AudioPort , lv2:OutputPort ; - lv2:index 4 ; + lv2:index 5 ; lv2:symbol "out4" ; lv2:name "Out4" ], [ a lv2:AudioPort , lv2:OutputPort ; - lv2:index 5 ; + lv2:index 6 ; lv2:symbol "out5" ; lv2:name "Out5" ], [ a lv2:AudioPort , lv2:OutputPort ; - lv2:index 6 ; + lv2:index 7 ; lv2:symbol "out6" ; lv2:name "Out6" ], [ a lv2:AudioPort , lv2:OutputPort ; - lv2:index 7 ; + lv2:index 8 ; lv2:symbol "out7" ; lv2:name "Out7" ], [ a lv2:AudioPort , lv2:OutputPort ; - lv2:index 8 ; + lv2:index 9 ; lv2:symbol "out8" ; lv2:name "Out8" ], [ a lv2:AudioPort , lv2:OutputPort ; - lv2:index 9 ; + lv2:index 10 ; lv2:symbol "out9" ; lv2:name "Out9" ], [ a lv2:AudioPort , lv2:OutputPort ; - lv2:index 10 ; + lv2:index 11 ; lv2:symbol "out10" ; lv2:name "Out10" ], [ a lv2:AudioPort , lv2:OutputPort ; - lv2:index 11 ; + lv2:index 12 ; lv2:symbol "out11" ; lv2:name "Out11" ], [ a lv2:AudioPort , lv2:OutputPort ; - lv2:index 12 ; + lv2:index 13 ; lv2:symbol "out12" ; lv2:name "Out12" ], [ a lv2:AudioPort , lv2:OutputPort ; - lv2:index 13 ; + lv2:index 14 ; lv2:symbol "out13" ; lv2:name "Out13" ], [ a lv2:AudioPort , lv2:OutputPort ; - lv2:index 14 ; + lv2:index 15 ; lv2:symbol "out14" ; lv2:name "Out14" ], [ a lv2:AudioPort , lv2:OutputPort ; - lv2:index 15 ; + lv2:index 16 ; lv2:symbol "out15" ; lv2:name "Out15" ], [ a lv2:AudioPort , lv2:OutputPort ; - lv2:index 16 ; + lv2:index 17 ; lv2:symbol "out16" ; lv2:name "Out16" ] . diff --git a/lv2/lv2.cc b/lv2/lv2.cc index 687f989..9722fad 100644 --- a/lv2/lv2.cc +++ b/lv2/lv2.cc @@ -35,6 +35,12 @@ #include +enum { + FREE_WHEEL_PORT = 0, + MIDI_PORT, + AUDIO_PORT_BASE +}; + #define DRUMGIZMO_URI "http://drumgizmo.org/lv2" #define NS_DG DRUMGIZMO_URI "/atom#" @@ -125,6 +131,9 @@ LV2_Handle instantiate(const struct _LV2_Descriptor *descriptor, { DGLV2 *dglv2 = new DGLV2; + dglv2->free_wheel_port = NULL; // Not assigned + dglv2->pos = 0; // Start from the beginning + dglv2->map = NULL; for (int i = 0 ; features[i] ; i++) { if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) { @@ -150,13 +159,18 @@ void connect_port(LV2_Handle instance, uint32_t port, void *data_location) { DGLV2 *dglv2 = (DGLV2 *)instance; - if(port == 0) {// MIDI in + if(port == FREE_WHEEL_PORT) { + dglv2->free_wheel_port = (float*)data_location; + } + + if(port == MIDI_PORT) { // MIDI in dglv2->in->eventPort = (LV2_Atom_Sequence*)data_location; - } else {// Audio Port - if(port - 1 < NUM_OUTPUTS) { - dglv2->out->outputPorts[port - 1].samples = (sample_t*)data_location; - dglv2->out->outputPorts[port - 1].size = 0; - } + } + + if(port >= AUDIO_PORT_BASE) { // Audio Port + uint32_t audio_port = port - AUDIO_PORT_BASE; + dglv2->out->outputPorts[audio_port].samples = (sample_t*)data_location; + dglv2->out->outputPorts[audio_port].size = 0; } } @@ -169,16 +183,19 @@ void activate(LV2_Handle instance) void run(LV2_Handle instance, uint32_t sample_count) { - static size_t pos = 0; DGLV2 *dglv2 = (DGLV2 *)instance; + if(dglv2->free_wheel_port) { + dglv2->dg->setFreeWheel(*dglv2->free_wheel_port); + } + if(dglv2->buffer_size != sample_count) { dglv2->buffer_size = sample_count; dglv2->dg->setFrameSize(sample_count); } - dglv2->dg->run(pos, dglv2->buffer, sample_count); + dglv2->dg->run(dglv2->pos, dglv2->buffer, sample_count); - pos += sample_count; + dglv2->pos += sample_count; } void deactivate(LV2_Handle instance) diff --git a/lv2/lv2_instance.h b/lv2/lv2_instance.h index e050e22..0ce98bb 100644 --- a/lv2/lv2_instance.h +++ b/lv2/lv2_instance.h @@ -43,6 +43,8 @@ typedef struct { sample_t *buffer; size_t buffer_size; LV2_URID_Map* map; + float* free_wheel_port; + size_t pos; } DGLV2; #endif/*__DRUMGIZMO_LV2_INSTANCE_H__*/ diff --git a/src/cachemanager.cc b/src/cachemanager.cc index dc4b3e7..4cc1fa5 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -321,6 +321,14 @@ void CacheManager::setFrameSize(size_t framesize) 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 &e) { assert(files.find(e.afile->filename) != files.end()); @@ -355,7 +363,6 @@ void CacheManager::handleCloseEvent(cevent_t &e) } } - void CacheManager::handleEvent(cevent_t &e) { switch(e.cmd) { diff --git a/src/cachemanager.h b/src/cachemanager.h index 4fe813e..1660ecf 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -130,8 +130,17 @@ public: */ 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(); diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index 528b542..5348324 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -51,6 +51,7 @@ DrumGizmo::DrumGizmo(AudioOutputEngine *o, AudioInputEngine *i) , oe(o) , ie(i) , framesize(0) + , freewheel(false) { is_stopping = false; cacheManager.init(1000, true); // start thread @@ -189,6 +190,16 @@ void DrumGizmo::setFrameSize(size_t framesize) } } +void DrumGizmo::setFreeWheel(bool freewheel) +{ + // Freewheel = true means that we are bouncing and therefore running faster + // than realtime. + if(freewheel != this->freewheel) { + this->freewheel = freewheel; + cacheManager.setAsyncMode(!freewheel); + } +} + bool DrumGizmo::run(size_t pos, sample_t *samples, size_t nsamples) { setFrameSize(nsamples); diff --git a/src/drumgizmo.h b/src/drumgizmo.h index 4c0740e..041ca35 100644 --- a/src/drumgizmo.h +++ b/src/drumgizmo.h @@ -77,6 +77,8 @@ public: void setFrameSize(size_t framesize); + void setFreeWheel(bool freewheel); + private: DrumKitLoader loader; @@ -98,4 +100,5 @@ private: DrumKit kit; size_t framesize; + bool freewheel; }; -- cgit v1.2.3 From 509568c03ca4b64266675735d677267ec265c49e Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Mon, 27 Jul 2015 19:50:04 +0200 Subject: Add free-wheel mode to VST (untested). --- vst/drumgizmo_vst.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vst/drumgizmo_vst.cc b/vst/drumgizmo_vst.cc index 1e40852..109f8b9 100644 --- a/vst/drumgizmo_vst.cc +++ b/vst/drumgizmo_vst.cc @@ -444,6 +444,12 @@ void DrumGizmoVst::initProcess() void DrumGizmoVst::processReplacing(float** inputs, float** outputs, VstInt32 sampleFrames) { + long lvl = getCurrentProcessLevel(); + // 0 = realtime/normal + // 1 = non-realtime/rendering + // 2 = offline processing + drumgizmo->setFreeWheel(lvl != 0); + output->setOutputs(outputs); if(buffer_size != (size_t)sampleFrames) { -- cgit v1.2.3 From 9a2da5246960c024907cbf3c4cd9fde61a329207 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Thu, 30 Jul 2015 18:22:16 +0200 Subject: Add free-wheel mode for Jack (untested). --- drumgizmo/audioinputenginedl.cc | 75 ++------------------------------- drumgizmo/audioinputenginedl.h | 4 ++ drumgizmo/audiooutputenginedl.cc | 5 +++ drumgizmo/audiooutputenginedl.h | 4 ++ drumgizmo/drumgizmoc.cc | 4 +- drumgizmo/jackclient.cc | 31 ++++++++++++-- drumgizmo/jackclient.h | 7 +++ drumgizmo/output/jackaudio/jackaudio.cc | 1 - 8 files changed, 54 insertions(+), 77 deletions(-) diff --git a/drumgizmo/audioinputenginedl.cc b/drumgizmo/audioinputenginedl.cc index 93c7079..7feac55 100644 --- a/drumgizmo/audioinputenginedl.cc +++ b/drumgizmo/audioinputenginedl.cc @@ -174,76 +174,7 @@ void AudioInputEngineDL::post() return i_post(ptr); } -//#include "audioinputenginedummy.h" -//#include "audioinputenginejackmidi.h" -//#include "audioinputenginemidifile.h" - -/* - -typedef Device* (*create_func_t)(void); -typedef void (*destroy_func_t)(Device*); - -struct device_t { - Device* dev; - destroy_func_t destroyer; - void* lib; -}; - -int load_shared_device(device_t &dev, std::string devlib, - std::string devfile, ConfMap devconfmap) { - // load library - dev.lib = dlopen(devlib.c_str(), RTLD_LAZY); - if(!dev.lib ) { - printf("Cannot load device: %s\n", dlerror()); - return -1; - } - - create_func_t create_device = (create_func_t) dlsym(dev.lib, "create"); - const char* dlsym_error = dlerror(); - if(dlsym_error) { - printf("Cannot load symbol create: %s\n", dlsym_error); - return -1; - } - - dev.destroyer = (destroy_func_t) dlsym(dev.lib, "destroy"); - dlsym_error = dlerror(); - if(dlsym_error) { - printf("Cannot load symbol destroy: %s\n", dlsym_error); - return -1; - } - - dev.dev = create_device(); - - // initialize device - DevData devdata = dev.dev->init(devfile, devconfmap); - if(devdata.retval != DevData::VALUE_SUCCESS) { - printf("Error while initializing device: %s\n", devdata.msg.c_str()); - return -1; - } - - return 0; -} - -void unload_shared_device(device_t &dev) { - - dev.destroyer(dev.dev); - dlclose(dev.lib); +void AudioInputEngineDL::setEngine(DrumGizmo* drumgizmo) +{ + jackclient->setEngine(drumgizmo); } -*/ - -#ifdef TEST_AUDIOINPUTENGINEDL -//Additional dependency files -//deps: -//Required cflags (autoconf vars may be used) -//cflags: -//Required link options (autoconf vars may be used) -//libs: -#include "test.h" - -TEST_BEGIN; - -// TODO: Put some testcode here (see test.h for usable macros). - -TEST_END; - -#endif/*TEST_AUDIOINPUTENGINEDL*/ diff --git a/drumgizmo/audioinputenginedl.h b/drumgizmo/audioinputenginedl.h index ed1fb27..7178f75 100644 --- a/drumgizmo/audioinputenginedl.h +++ b/drumgizmo/audioinputenginedl.h @@ -40,6 +40,8 @@ typedef void (*input_pre_func_t)(void*); typedef event_t* (*input_run_func_t)(void*,size_t,size_t,size_t*); typedef void (*input_post_func_t)(void*); +class DrumGizmo; + class AudioInputEngineDL : public AudioInputEngine { public: AudioInputEngineDL(std::string name); @@ -56,6 +58,8 @@ public: event_t *run(size_t pos, size_t len, size_t *nevents); void post(); + void setEngine(DrumGizmo* drumgizmo); + private: void *ptr; input_create_func_t i_create; diff --git a/drumgizmo/audiooutputenginedl.cc b/drumgizmo/audiooutputenginedl.cc index dad2c5c..fc887df 100644 --- a/drumgizmo/audiooutputenginedl.cc +++ b/drumgizmo/audiooutputenginedl.cc @@ -193,3 +193,8 @@ size_t AudioOutputEngineDL::samplerate() if(o_samplerate) return o_samplerate(ptr); return 44100; } + +void AudioOutputEngineDL::setEngine(DrumGizmo* drumgizmo) +{ + jackclient->setEngine(drumgizmo); +} diff --git a/drumgizmo/audiooutputenginedl.h b/drumgizmo/audiooutputenginedl.h index 75460f3..62d94f8 100644 --- a/drumgizmo/audiooutputenginedl.h +++ b/drumgizmo/audiooutputenginedl.h @@ -48,6 +48,8 @@ typedef void (*output_post_func_t)(void*, size_t); typedef size_t (*output_bufsize_func_t)(void*); typedef size_t (*output_samplerate_func_t)(void*); +class DrumGizmo; + class AudioOutputEngineDL : public AudioOutputEngine { public: AudioOutputEngineDL(std::string name); @@ -67,6 +69,8 @@ public: size_t getBufferSize(); size_t samplerate(); + void setEngine(DrumGizmo* drumgizmo); + private: void *ptr; output_create_func_t o_create; diff --git a/drumgizmo/drumgizmoc.cc b/drumgizmo/drumgizmoc.cc index 6111c72..708528c 100644 --- a/drumgizmo/drumgizmoc.cc +++ b/drumgizmo/drumgizmoc.cc @@ -229,7 +229,7 @@ int CliMain::run(int argc, char *argv[]) return 1; } - AudioInputEngine *ie = new AudioInputEngineDL(inputengine); + AudioInputEngineDL *ie = new AudioInputEngineDL(inputengine); if(ie == NULL) { printf("Invalid input engine: %s\n", inputengine.c_str()); @@ -323,6 +323,8 @@ int CliMain::run(int argc, char *argv[]) printf("Using kitfile: %s\n", kitfile.c_str()); DrumGizmo gizmo(oe, ie); + oe->setEngine(&gizmo); + ie->setEngine(&gizmo); gizmo.setFrameSize(oe->getBufferSize()); diff --git a/drumgizmo/jackclient.cc b/drumgizmo/jackclient.cc index e4af141..a24d53a 100644 --- a/drumgizmo/jackclient.cc +++ b/drumgizmo/jackclient.cc @@ -26,20 +26,33 @@ */ #include "jackclient.h" +#include + extern "C" { - int _wrap_jack_process(jack_nframes_t nframes, void *arg){ - return ((JackClient*)arg)->process(nframes);} + +static int jack_process_callback(jack_nframes_t nframes, void *arg) +{ + return ((JackClient*)arg)->process(nframes); +} + +static void jack_free_wheel_callback(int starting, void *arg) +{ + ((JackClient*)arg)->setFreeWheel(starting); +} + } // extern "C" JackClient::JackClient() : refcnt(0) + , drumgizmo(NULL) { jack_status_t status; jack_client = jack_client_open("DrumGizmo", JackNullOption, &status); - jack_set_process_callback(jack_client, _wrap_jack_process, this); + jack_set_process_callback(jack_client, jack_process_callback, this); + jack_set_freewheel_callback(jack_client, jack_free_wheel_callback, this); active = false; } @@ -79,6 +92,18 @@ int JackClient::process(jack_nframes_t nframes) return 0; } +void JackClient::setFreeWheel(bool freewheel) +{ + if(drumgizmo) { + drumgizmo->setFreeWheel(freewheel); + } +} + +void JackClient::setEngine(DrumGizmo* drumgizmo) +{ + this->drumgizmo = drumgizmo; +} + JackClient *jackclient = NULL; JackClient *init_jack_client() diff --git a/drumgizmo/jackclient.h b/drumgizmo/jackclient.h index f093220..8c5954d 100644 --- a/drumgizmo/jackclient.h +++ b/drumgizmo/jackclient.h @@ -36,6 +36,8 @@ public: virtual void jack_process(jack_nframes_t nframes) = 0; }; +class DrumGizmo; + class JackClient { public: JackClient(); @@ -51,12 +53,17 @@ public: void activate(); int process(jack_nframes_t nframes); + void setFreeWheel(bool freewheel); + jack_client_t *jack_client; + void setEngine(DrumGizmo* drumgizmo); + // Sort of private... int refcnt; private: + DrumGizmo* drumgizmo; std::set jack_processes; bool active; }; diff --git a/drumgizmo/output/jackaudio/jackaudio.cc b/drumgizmo/output/jackaudio/jackaudio.cc index 5c04146..7e795ef 100644 --- a/drumgizmo/output/jackaudio/jackaudio.cc +++ b/drumgizmo/output/jackaudio/jackaudio.cc @@ -130,7 +130,6 @@ void JackAudio::post(size_t size) void JackAudio::jack_process(jack_nframes_t nframes) { - //printf("o"); fflush(stdout); for(size_t c = 0; c < nchannels; c++) { jack_default_audio_sample_t *out = (jack_default_audio_sample_t *) jack_port_get_buffer(output_port[c], -- cgit v1.2.3 From 1070276519f7a60547da3f567a8f4d9caaa0b0c3 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Thu, 24 Dec 2015 17:49:09 +0100 Subject: Split internal CacheManager AFile class out into it's own file/class CacheAudioFile and improve interface. --- src/Makefile.am.drumgizmo | 1 + src/cacheaudiofile.cc | 156 +++++++++++ src/cacheaudiofile.h | 63 +++++ src/cachemanager.cc | 660 +++++++++++++++++++++------------------------- src/cachemanager.h | 262 +++++++++--------- vst/Makefile.mingw32.in | 2 + 6 files changed, 658 insertions(+), 486 deletions(-) create mode 100644 src/cacheaudiofile.cc create mode 100644 src/cacheaudiofile.h 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 + +#include + +#include + +#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 +#include +#include + +#include + +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 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 #include -#include - #include +#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& 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 #include @@ -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 {}; //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 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 id2cache; + void handleEvent(cevent_t& e); + void pushEvent(cevent_t& e); - // Protected by mutex: - std::list eventqueue; - std::list availableids; - - Mutex m_events; - Mutex m_ids; + std::vector 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 eventqueue; + std::list availableids; - std::map 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 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 \ -- cgit v1.2.3 From 2a9dda465230b3b129dc942095a7c8a99ef5fa50 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Thu, 24 Dec 2015 17:50:38 +0100 Subject: Add experimental 'silencing' of all active events on frame size change. --- src/cachemanager.cc | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/cachemanager.cc b/src/cachemanager.cc index b2e10ac..d394d8c 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -249,6 +249,9 @@ void CacheManager::close(cacheid_t id) void CacheManager::setFrameSize(size_t framesize) { + MutexAutolock le(m_events); + MutexAutolock li(m_ids); + if(framesize > this->framesize) { delete[] nodata; @@ -256,7 +259,31 @@ void CacheManager::setFrameSize(size_t framesize) for(size_t i = 0; i < framesize; ++i) { - nodata[i] = 0; + 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; } } -- cgit v1.2.3 From 22b99e7d0379d1b347ea04c27e46b83ed40d057b Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Tue, 19 Jan 2016 08:32:44 +0100 Subject: Split CacheManager into several AudioCache classes and refactored the lot of them. Unit tests added. --- src/Makefile.am.drumgizmo | 6 +- src/audiocache.cc | 274 +++++++++++++++++++++++ src/audiocache.h | 136 ++++++++++++ src/audiocacheeventhandler.cc | 315 ++++++++++++++++++++++++++ src/audiocacheeventhandler.h | 109 +++++++++ src/audiocachefile.cc | 166 ++++++++++++++ src/audiocachefile.h | 95 ++++++++ src/audiocacheidmanager.cc | 128 +++++++++++ src/audiocacheidmanager.h | 88 ++++++++ src/cacheaudiofile.cc | 156 ------------- src/cacheaudiofile.h | 63 ------ src/cachemanager.cc | 438 ------------------------------------- src/cachemanager.h | 221 ------------------- src/drumgizmo.cc | 16 +- src/drumgizmo.h | 4 +- src/drumkitloader.cc | 1 - src/events.h | 2 +- test/Makefile.am | 55 ++++- test/audiocacheeventhandlertest.cc | 58 +++++ test/audiocachefiletest.cc | 189 ++++++++++++++++ test/audiocacheidmanagertest.cc | 98 +++++++++ test/audiocachetest.cc | 177 +++++++++++++++ test/cachemanagertest.cc | 149 ------------- 23 files changed, 1897 insertions(+), 1047 deletions(-) create mode 100644 src/audiocache.cc create mode 100644 src/audiocache.h create mode 100644 src/audiocacheeventhandler.cc create mode 100644 src/audiocacheeventhandler.h create mode 100644 src/audiocachefile.cc create mode 100644 src/audiocachefile.h create mode 100644 src/audiocacheidmanager.cc create mode 100644 src/audiocacheidmanager.h delete mode 100644 src/cacheaudiofile.cc delete mode 100644 src/cacheaudiofile.h delete mode 100644 src/cachemanager.cc delete mode 100644 src/cachemanager.h create mode 100644 test/audiocacheeventhandlertest.cc create mode 100644 test/audiocachefiletest.cc create mode 100644 test/audiocacheidmanagertest.cc create mode 100644 test/audiocachetest.cc delete mode 100644 test/cachemanagertest.cc 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 + +#include +#include +#include + +#include + +#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 eventHandlerLock(eventHandler); + + // NOTE: Not threaded... + //std::lock_guard 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 +#include +#include + +#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()) + , 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 l(mutex); + return files.getFile(filename); +} + +void AudioCacheEventHandler::clearEvents() +{ + std::lock_guard 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 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 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 +#include +#include + +#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* eventqueue; + + bool threaded{false}; + Semaphore sem; + Semaphore sem_run; + bool running{false}; + + AudioCacheIDManager& idManager; + + size_t chunksize{1024}; +}; diff --git a/src/audiocachefile.cc b/src/audiocachefile.cc new file mode 100644 index 0000000..c1fdd7b --- /dev/null +++ b/src/audiocachefile.cc @@ -0,0 +1,166 @@ +/* -*- 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 "audiocachefile.h" + +#include + +#include + +#include "audiocache.h" + +AudioCacheFile::AudioCacheFile(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)); + } + + if(sf_info.frames == 0) + { + printf("sf_info.frames == 0\n"); + } +} + +AudioCacheFile::~AudioCacheFile() +{ + if(fh) + { + sf_close(fh); + fh = nullptr; + } +} + +size_t AudioCacheFile::getSize() const +{ + return sf_info.frames; +} + +const std::string& AudioCacheFile::getFilename() const +{ + return filename; +} + +size_t AudioCacheFile::getChannelCount() +{ + return sf_info.channels; +} + +void AudioCacheFile::readChunk(const CacheChannels& channels, + size_t pos, size_t num_samples) +{ + assert(fh != nullptr); // File handle must never be nullptr + + if(pos > sf_info.frames) + { + printf("pos (%d) > sf_info.frames (%d)\n", pos, (int)sf_info.frames); + 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; + } +} + +AudioCacheFile& AudioCacheFiles::getFile(const std::string& filename) +{ + AudioCacheFile* cacheAudioFile = nullptr; + + auto it = audiofiles.find(filename); + if(it == audiofiles.end()) + { + cacheAudioFile = new AudioCacheFile(filename); + audiofiles.insert(std::make_pair(filename, cacheAudioFile)); + } + else + { + cacheAudioFile = it->second; + } + + assert(cacheAudioFile); + + // Increase ref count. + ++cacheAudioFile->ref; + + return *cacheAudioFile; +} + +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 + } + + 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/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 +#include +#include +#include + +#include + +#include + +class CacheChannel { +public: + size_t channel; + sample_t* samples; + size_t num_samples; + volatile bool* ready; +}; + +using CacheChannels = std::list; + +//! 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 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 +#include + +AudioCacheIDManager::AudioCacheIDManager() +{ +} + +AudioCacheIDManager::~AudioCacheIDManager() +{ + assert(availableids.size() == id2cache.size()); // All ids should be released. +} + +void AudioCacheIDManager::init(unsigned int capacity) +{ + std::lock_guard 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 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 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 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::max(); + cache.ready = false; + } + } +} + +std::vector AudioCacheIDManager::getActiveIDs() +{ + std::vector 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 + +#include + +#include + +#include + +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 getActiveIDs(); + + std::mutex mutex; + + std::vector id2cache; + std::vector availableids; +}; diff --git a/src/cacheaudiofile.cc b/src/cacheaudiofile.cc deleted file mode 100644 index 0d4aec9..0000000 --- a/src/cacheaudiofile.cc +++ /dev/null @@ -1,156 +0,0 @@ -/* -*- 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 - -#include - -#include - -#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 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 -#include -#include - -#include - -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 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 -#include -#include - -#include - -#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 -#include -#include - -#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 {}; - -//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 id2cache; - - // Protected by mutex: - std::list eventqueue; - std::list 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 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 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; diff --git a/test/Makefile.am b/test/Makefile.am index 6526f22..1f605e5 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,23 +1,66 @@ # Rules for the test code (use `make check` to execute) include $(top_srcdir)/src/Makefile.am.drumgizmo -TESTS = engine gui resampler lv2 configfile cachemanager +TESTS = engine gui resampler lv2 configfile audiocache audiocachefile \ + audiocacheidmanager audiocacheeventhandler check_PROGRAMS = $(TESTS) -cachemanager_CXXFLAGS = -DOUTPUT=\"cachemanager\" $(CPPUNIT_CFLAGS) \ +audiocache_CXXFLAGS = -DOUTPUT=\"audiocache\" $(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 \ +audiocache_LDFLAGS = $(PTHREAD_LIBS) $(CPPUNIT_LIBS) $(SNDFILE_LIBS) +audiocache_SOURCES = \ + $(top_srcdir)/src/audiocache.cc \ + $(top_srcdir)/src/audiocacheeventhandler.cc \ + $(top_srcdir)/src/audiocachefile.cc \ + $(top_srcdir)/src/audiocacheidmanager.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 + audiocachetest.cc + +audiocachefile_CXXFLAGS = -DOUTPUT=\"audiocachefile\" $(CPPUNIT_CFLAGS) \ + -I$(top_srcdir)/src -I$(top_srcdir)/include \ + -I$(top_srcdir)/hugin -DDISABLE_HUGIN $(PTHREAD_CFLAGS) $(SNDFILE_CFLAGS) +audiocachefile_LDFLAGS = $(PTHREAD_LIBS) $(CPPUNIT_LIBS) $(SNDFILE_LIBS) +audiocachefile_SOURCES = \ + $(top_srcdir)/src/audiocachefile.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 \ + audiocachefiletest.cc + +audiocacheidmanager_CXXFLAGS = -DOUTPUT=\"audiocacheidmanager\" \ + $(CPPUNIT_CFLAGS) \ + -I$(top_srcdir)/src -I$(top_srcdir)/include \ + -I$(top_srcdir)/hugin -DDISABLE_HUGIN $(SNDFILE_CFLAGS) +audiocacheidmanager_LDFLAGS = $(CPPUNIT_LIBS) $(SNDFILE_LIBS) +audiocacheidmanager_SOURCES = \ + $(top_srcdir)/src/audiocacheidmanager.cc \ + test.cc \ + audiocacheidmanagertest.cc + +audiocacheeventhandler_CXXFLAGS = -DOUTPUT=\"audiocacheeventhandler\" \ + $(CPPUNIT_CFLAGS) \ + -I$(top_srcdir)/src -I$(top_srcdir)/include \ + -I$(top_srcdir)/hugin -DDISABLE_HUGIN $(PTHREAD_CFLAGS) $(SNDFILE_CFLAGS) +audiocacheeventhandler_LDFLAGS = $(PTHREAD_LIBS) $(CPPUNIT_LIBS) $(SNDFILE_LIBS) +audiocacheeventhandler_SOURCES = \ + $(top_srcdir)/src/audiocacheeventhandler.cc \ + $(top_srcdir)/src/audiocacheidmanager.cc \ + $(top_srcdir)/src/audiocachefile.cc \ + $(top_srcdir)/src/mutex.cc \ + $(top_srcdir)/src/thread.cc \ + $(top_srcdir)/src/semaphore.cc \ + test.cc \ + audiocacheeventhandlertest.cc engine_CXXFLAGS = -DOUTPUT=\"engine\" $(CPPUNIT_CFLAGS) \ -I$(top_srcdir)/src -I$(top_srcdir)/include \ diff --git a/test/audiocacheeventhandlertest.cc b/test/audiocacheeventhandlertest.cc new file mode 100644 index 0000000..a8c3777 --- /dev/null +++ b/test/audiocacheeventhandlertest.cc @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * audiocacheeventhandlertest.cc + * + * Thu Jan 7 15:44:14 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 + +#include + +class TestableAudioCacheEventHandler + : public AudioCacheEventHandler +{ +public: + +}; + +class AudioCacheEventHandlerTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(AudioCacheEventHandlerTest); + CPPUNIT_TEST(threadedTest); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp() {} + void tearDown() {} + + void threadedTest() + { + AudioCacheIDManager idManager; + idManager.init(10); + + AudioCacheEventHandler eventHandler(idManager); + } +}; + +// Registers the fixture into the 'registry' +CPPUNIT_TEST_SUITE_REGISTRATION(AudioCacheEventHandlerTest); diff --git a/test/audiocachefiletest.cc b/test/audiocachefiletest.cc new file mode 100644 index 0000000..a70091c --- /dev/null +++ b/test/audiocachefiletest.cc @@ -0,0 +1,189 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * audiocachefiletest.cc + * + * Thu Jan 7 15:43:12 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 + +#include + +#include +#include + +class TestableAudioCacheFiles : public AudioCacheFiles +{ +public: + //CacheAudioFile& getAudioFile(const std::string& filename); + //void release(const std::string& filename); + int getRef(const std::string& filename) + { + if(audiofiles.find(filename) == audiofiles.end()) + { + return -1; + } + + return audiofiles[filename]->ref; + } +}; + +class AudioCacheFileTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(AudioCacheFileTest); + CPPUNIT_TEST(refTest); + CPPUNIT_TEST(readTest); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp() {} + void tearDown() {} + + void refTest() + { + TestableAudioCacheFiles audiofiles; + std::string filename = "kit/ride-single-channel.wav"; + CPPUNIT_ASSERT_EQUAL(-1, audiofiles.getRef(filename)); + + audiofiles.getFile(filename); + CPPUNIT_ASSERT_EQUAL(1, audiofiles.getRef(filename)); + + audiofiles.getFile(filename); + CPPUNIT_ASSERT_EQUAL(2, audiofiles.getRef(filename)); + + audiofiles.releaseFile(filename); + CPPUNIT_ASSERT_EQUAL(1, audiofiles.getRef(filename)); + + audiofiles.releaseFile(filename); + CPPUNIT_ASSERT_EQUAL(-1, audiofiles.getRef(filename)); + } + + void readTestHelper(size_t bufferSize) + { + printf("Test buffer size: %d samples\n", bufferSize); + + std::string filename = "kit/ride-multi-channel.wav"; + AudioFile* refFile[13]; + for(size_t c = 0; c < 13; ++c) + { + refFile[c] = new AudioFile(filename, c); + refFile[c]->load(); + } + + AudioCacheFile file(filename); + CPPUNIT_ASSERT_EQUAL(filename, file.getFilename()); + CPPUNIT_ASSERT_EQUAL(13, (int)file.getChannelCount()); // Sanity check + + CacheChannels channels; + + sample_t samples[13][bufferSize]; + volatile bool ready[13]; + for(size_t c = 0; c < 13; ++c) + { + for(int c = 0; c < 13; ++c) + { + for(int i = 0; i < bufferSize; ++i) + { + samples[c][i] = 42; + } + } + + channels.push_back( + { + c, // channel + samples[c], // samples + bufferSize, // max_num_samples + &ready[c] // ready + } + ); + } + + for(size_t offset = 0; offset < file.getSize(); offset += bufferSize) + { + for(size_t c = 0; c < 13; ++c) + { + ready[c] = false; + } + + size_t readSize = file.getSize() - offset; + if(readSize > bufferSize) + { + readSize = bufferSize; + } + else + { + printf("Last read: %d samples\n", readSize); + } + + file.readChunk(channels, offset, readSize); + + for(size_t c = 0; c < 13; ++c) + { + CPPUNIT_ASSERT_EQUAL(true, ready[c]?true:false); + } + + sample_t diff[13] = {0.0}; + for(int c = 0; c < 13; ++c) + { + for(int i = 0; i < readSize; ++i) + { + diff[c] += abs(refFile[c]->data[i + offset] - samples[c][i]); + } + } + + for(int c = 0; c < 13; ++c) + { + CPPUNIT_ASSERT_EQUAL((sample_t)0.0, diff[c]); + } + } + + for(size_t c = 0; c < 13; ++c) + { + delete refFile[c]; + } + } + + void readTest() + { + // Exhaustive test for 1...64 + for(size_t bufferSize = 1; bufferSize < 64; ++bufferSize) + { + readTestHelper(bufferSize); + } + + // Binary test for 64 .. 4096 + for(size_t bufferSize = 64; bufferSize < 4096; bufferSize *= 2) + { + readTestHelper(bufferSize); + } + + // And some sporadic tests for some "wierd" sizes. + for(size_t bufferSize = 65; bufferSize < 4096; bufferSize *= 1.1) + { + readTestHelper(bufferSize); + } + } + +}; + +// Registers the fixture into the 'registry' +CPPUNIT_TEST_SUITE_REGISTRATION(AudioCacheFileTest); diff --git a/test/audiocacheidmanagertest.cc b/test/audiocacheidmanagertest.cc new file mode 100644 index 0000000..58bff4e --- /dev/null +++ b/test/audiocacheidmanagertest.cc @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * audiocacheidmanagertest.cc + * + * Thu Jan 7 15:42:31 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 + +#include + +class TestableAudioCacheIDManager : public AudioCacheIDManager { +public: + int getAvailableIds() + { + return availableids.size(); + } +}; + +class AudioCacheIDManagerTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(AudioCacheIDManagerTest); + CPPUNIT_TEST(registerReleaseTest); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp() {} + void tearDown() {} + + void registerReleaseTest() + { + TestableAudioCacheIDManager manager; + manager.init(2); + + cache_t c1; c1.afile = (AudioCacheFile*)1; + auto id1 = manager.registerId(c1); + CPPUNIT_ASSERT(id1 != CACHE_DUMMYID); + CPPUNIT_ASSERT(id1 != CACHE_NOID); + CPPUNIT_ASSERT_EQUAL(1, manager.getAvailableIds()); + + cache_t c2; c2.afile = (AudioCacheFile*)2; + auto id2 = manager.registerId(c2); + CPPUNIT_ASSERT(id2 != CACHE_DUMMYID); + CPPUNIT_ASSERT(id2 != CACHE_NOID); + CPPUNIT_ASSERT_EQUAL(0, manager.getAvailableIds()); + + cache_t c3; c3.afile = (AudioCacheFile*)3; + auto id3 = manager.registerId(c3); + CPPUNIT_ASSERT(id3 == CACHE_DUMMYID); + CPPUNIT_ASSERT_EQUAL(0, manager.getAvailableIds()); + + cache_t& tc1 = manager.getCache(id1); + CPPUNIT_ASSERT_EQUAL(c1.afile, tc1.afile); + + cache_t& tc2 = manager.getCache(id2); + CPPUNIT_ASSERT_EQUAL(c2.afile, tc2.afile); + + manager.releaseId(id1); + CPPUNIT_ASSERT_EQUAL(1, manager.getAvailableIds()); + + cache_t c4; c4.afile = (AudioCacheFile*)4; + auto id4 = manager.registerId(c4); + CPPUNIT_ASSERT(id4 != CACHE_DUMMYID); + CPPUNIT_ASSERT(id4 != CACHE_NOID); + CPPUNIT_ASSERT_EQUAL(0, manager.getAvailableIds()); + + cache_t& tc4 = manager.getCache(id4); + CPPUNIT_ASSERT_EQUAL(c4.afile, tc4.afile); + + manager.releaseId(id2); + CPPUNIT_ASSERT_EQUAL(1, manager.getAvailableIds()); + + manager.releaseId(id4); + CPPUNIT_ASSERT_EQUAL(2, manager.getAvailableIds()); + } +}; + +// Registers the fixture into the 'registry' +CPPUNIT_TEST_SUITE_REGISTRATION(AudioCacheIDManagerTest); diff --git a/test/audiocachetest.cc b/test/audiocachetest.cc new file mode 100644 index 0000000..0296490 --- /dev/null +++ b/test/audiocachetest.cc @@ -0,0 +1,177 @@ +/* -*- 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 + +#include +#include +#include + +#define FRAMESIZE 64//1024 + +class AudioCacheTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(AudioCacheTest); + CPPUNIT_TEST(singleChannelNonThreaded); + CPPUNIT_TEST(singleChannelThreaded); + CPPUNIT_TEST(multiChannelNonThreaded); + CPPUNIT_TEST(multiChannelThreaded); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp() {} + void tearDown() {} + + //! Test runner. + //! \param filename The name of the file to read. + //! \param channel The channel number to do comparison on. + //! \param thread Control if this test is running in threaded mode or not. + //! \param framesize The initial framesize to use. + void testHelper(const char* filename, int channel, bool threaded, + int framesize) + { + // Reference file: + AudioFile audioFileRef(filename, channel); + printf("audioFileRef.load\n"); + audioFileRef.load(ALL_SAMPLES); + + // Input file: + AudioFile audioFile(filename, channel); + printf("audioFile.load\n"); + audioFile.load(4096); + + AudioCache audioCache; + printf("audioCache.init\n"); + audioCache.init(100); + audioCache.setAsyncMode(threaded); + + // Set initial (upper limit) framesize + audioCache.setFrameSize(framesize); + + cacheid_t id; + + for(size_t initial_samples_needed = 0; + initial_samples_needed < (framesize - 1); ++initial_samples_needed) + { + + printf("open: initial_samples_needed: %d\n", initial_samples_needed); + sample_t *samples = + audioCache.open(&audioFile, 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(audioFileRef.data[offset], samples[i]); + ++offset; + } + + // Test the rest + while(offset < audioFileRef.size) + { + if(threaded) + { + // Wait until we are finished reading + int timeout = 1000; + while(!audioCache.isReady(id)) + { + usleep(1000); + if(--timeout == 0) + { + CPPUNIT_ASSERT(false); // timeout + } + } + } + + samples = audioCache.next(id, size); + + CPPUNIT_ASSERT_EQUAL(0, (int)audioCache.getNumberOfUnderruns()); + + for(size_t i = 0; (i < size) && (offset < audioFileRef.size); ++i) + { + if(audioFileRef.data[offset] != samples[i]) + { + printf("-----> offset: %d, size: %d, diff: %d," + " i: %d, size: %d, block-diff: %d\n", + offset, audioFileRef.size, audioFileRef.size - offset, + i, size, size - i); + } + CPPUNIT_ASSERT_EQUAL(audioFileRef.data[offset], samples[i]); + ++offset; + } + } + + audioCache.close(id); + } + + printf("done\n"); + } + + void singleChannelNonThreaded() + { + printf("\nsinglechannel_nonthreaded()\n"); + const char filename[] = "kit/ride-single-channel.wav"; + int channel = 0; + bool threaded = false; + testHelper(filename, channel, threaded, FRAMESIZE); + } + + void singleChannelThreaded() + { + printf("\nsinglechannel_threaded()\n"); + const char filename[] = "kit/ride-single-channel.wav"; + int channel = 0; + bool threaded = true; + testHelper(filename, channel, threaded, FRAMESIZE); + } + + void multiChannelNonThreaded() + { + printf("\nmultichannel_nonthreaded()\n"); + const char filename[] = "kit/ride-multi-channel.wav"; + int channel = 0; + bool threaded = false; + testHelper(filename, channel, threaded, FRAMESIZE); + ++channel; + testHelper(filename, channel, threaded, FRAMESIZE); + } + + void multiChannelThreaded() + { + printf("\nmultichannel_threaded()\n"); + const char filename[] = "kit/ride-multi-channel.wav"; + int channel = 0; + bool threaded = true; + testHelper(filename, channel, threaded, FRAMESIZE); + ++channel; + testHelper(filename, channel, threaded, FRAMESIZE); + } + +}; + +// Registers the fixture into the 'registry' +CPPUNIT_TEST_SUITE_REGISTRATION(AudioCacheTest); diff --git a/test/cachemanagertest.cc b/test/cachemanagertest.cc deleted file mode 100644 index d521f83..0000000 --- a/test/cachemanagertest.cc +++ /dev/null @@ -1,149 +0,0 @@ -/* -*- 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 - -#include -#include - -#define FRAMESIZE 1024 - -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(4096); - - CacheManager cm; - printf("cm.init\n"); - cm.init(100, threaded); - - cm.setFrameSize(FRAMESIZE); - - cacheid_t id; - // TODO: test 0 ... FRAMESIZE - 1 - size_t initial_samples_needed = (FRAMESIZE - 1) / 2; - - printf("open: initial_samples_needed: %d\n", initial_samples_needed); - 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++) { - /* - if(afref.data[offset] != s[i]) { - printf("offset: %d, size: %d, diff: %d\n", offset, afref.size, afref.size - offset); - } - */ - CPPUNIT_ASSERT_EQUAL(afref.data[offset], s[i]); - offset++; - } - } - - printf("done\n"); - } - - void singlechannel_nonthreaded() - { - printf("\nsinglechannel_nonthreaded()\n"); - const char filename[] = "kit/ride-single-channel.wav"; - int channel = 0; - bool threaded = false; - testit(filename, channel, threaded); - } - - void singlechannel_threaded() - { - printf("\nsinglechannel_threaded()\n"); - const char filename[] = "kit/ride-single-channel.wav"; - int channel = 0; - bool threaded = true; - testit(filename, channel, threaded); - } - - void multichannel_nonthreaded() - { - printf("\nmultichannel_nonthreaded()\n"); - const char filename[] = "kit/ride-multi-channel.wav"; - int channel = 0; - bool threaded = false; - testit(filename, channel, threaded); - } - - void multichannel_threaded() - { - printf("\nmultichannel_threaded()\n"); - 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); - - - -- cgit v1.2.3 From 195deff846013de59a10fd23f7a43a3d521f9325 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Tue, 19 Jan 2016 18:16:58 +0100 Subject: Fix some comments. --- src/audiocache.cc | 4 ++-- src/audiocache.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/audiocache.cc b/src/audiocache.cc index 1a2225e..6d86494 100644 --- a/src/audiocache.cc +++ b/src/audiocache.cc @@ -1,6 +1,6 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /*************************************************************************** - * cachemanager.cc + * audiocache.cc * * Fri Apr 10 10:39:24 CEST 2015 * Copyright 2015 Jonas Suhr Christensen @@ -75,7 +75,7 @@ sample_t* AudioCache::open(AudioFile* file, size_t initial_samples_needed, // Register a new id for this cache session. id = idManager.registerID({}); - // If we are out of availabel ids we get CACHE_DUMMYID + // If we are out of available ids we get CACHE_DUMMYID if(id == CACHE_DUMMYID) { // Use nodata buffer instead. diff --git a/src/audiocache.h b/src/audiocache.h index 824db3a..06493e6 100644 --- a/src/audiocache.h +++ b/src/audiocache.h @@ -1,6 +1,6 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /*************************************************************************** - * cachemanager.h + * audiocache.h * * Fri Apr 10 10:39:24 CEST 2015 * Copyright 2015 Jonas Suhr Christensen @@ -102,7 +102,7 @@ public: //! \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. + //! Returns if the next chunk of the supplied id has been read from disk. bool isReady(cacheid_t id); //! Unregister cache entry. -- cgit v1.2.3 From 88260c1ae4c513dc21f3be3f7f0a805665d4e643 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Tue, 19 Jan 2016 19:35:55 +0100 Subject: Check for lock in setChunkSize. Remove lock in clearEvents. --- src/audiocacheeventhandler.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/audiocacheeventhandler.cc b/src/audiocacheeventhandler.cc index 598cc15..897f2fb 100644 --- a/src/audiocacheeventhandler.cc +++ b/src/audiocacheeventhandler.cc @@ -26,6 +26,8 @@ */ #include "audiocacheeventhandler.h" +#include + #include "audiocachefile.h" #include "audiocache.h" #include "audiocacheidmanager.h" @@ -165,6 +167,9 @@ void AudioCacheEventHandler::pushCloseEvent(cacheid_t id) void AudioCacheEventHandler::setChunkSize(size_t chunksize) { + // We should already locked when this method is called. + assert(!mutex.try_lock()); + if(this->chunksize == chunksize) { return; @@ -192,8 +197,6 @@ AudioCacheFile& AudioCacheEventHandler::openFile(const std::string& filename) void AudioCacheEventHandler::clearEvents() { - std::lock_guard l(mutex); - // Iterate all events ignoring load events and handling close events. for(auto& event : *eventqueue) { -- cgit v1.2.3 From 291ce1b62f25a048a3f7480caf18f48e824ce37f Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Tue, 19 Jan 2016 19:36:19 +0100 Subject: Check full initial_samples_needed range. --- test/audiocachetest.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/audiocachetest.cc b/test/audiocachetest.cc index 0296490..7e8b084 100644 --- a/test/audiocachetest.cc +++ b/test/audiocachetest.cc @@ -30,7 +30,7 @@ #include #include -#define FRAMESIZE 64//1024 +#define FRAMESIZE 64 class AudioCacheTest : public CppUnit::TestFixture { @@ -74,7 +74,7 @@ public: cacheid_t id; for(size_t initial_samples_needed = 0; - initial_samples_needed < (framesize - 1); ++initial_samples_needed) + initial_samples_needed < (framesize + 1); ++initial_samples_needed) { printf("open: initial_samples_needed: %d\n", initial_samples_needed); -- cgit v1.2.3 From d1906f2b292153cb4b1011f9a998df83fffa7442 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Wed, 20 Jan 2016 11:53:21 +0100 Subject: Fix new method names. --- test/audiocacheidmanagertest.cc | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/audiocacheidmanagertest.cc b/test/audiocacheidmanagertest.cc index 58bff4e..ae8bfbc 100644 --- a/test/audiocacheidmanagertest.cc +++ b/test/audiocacheidmanagertest.cc @@ -30,7 +30,7 @@ class TestableAudioCacheIDManager : public AudioCacheIDManager { public: - int getAvailableIds() + int getAvailableIDs() { return availableids.size(); } @@ -52,21 +52,21 @@ public: manager.init(2); cache_t c1; c1.afile = (AudioCacheFile*)1; - auto id1 = manager.registerId(c1); + auto id1 = manager.registerID(c1); CPPUNIT_ASSERT(id1 != CACHE_DUMMYID); CPPUNIT_ASSERT(id1 != CACHE_NOID); - CPPUNIT_ASSERT_EQUAL(1, manager.getAvailableIds()); + CPPUNIT_ASSERT_EQUAL(1, manager.getAvailableIDs()); cache_t c2; c2.afile = (AudioCacheFile*)2; - auto id2 = manager.registerId(c2); + auto id2 = manager.registerID(c2); CPPUNIT_ASSERT(id2 != CACHE_DUMMYID); CPPUNIT_ASSERT(id2 != CACHE_NOID); - CPPUNIT_ASSERT_EQUAL(0, manager.getAvailableIds()); + CPPUNIT_ASSERT_EQUAL(0, manager.getAvailableIDs()); cache_t c3; c3.afile = (AudioCacheFile*)3; - auto id3 = manager.registerId(c3); + auto id3 = manager.registerID(c3); CPPUNIT_ASSERT(id3 == CACHE_DUMMYID); - CPPUNIT_ASSERT_EQUAL(0, manager.getAvailableIds()); + CPPUNIT_ASSERT_EQUAL(0, manager.getAvailableIDs()); cache_t& tc1 = manager.getCache(id1); CPPUNIT_ASSERT_EQUAL(c1.afile, tc1.afile); @@ -74,23 +74,23 @@ public: cache_t& tc2 = manager.getCache(id2); CPPUNIT_ASSERT_EQUAL(c2.afile, tc2.afile); - manager.releaseId(id1); - CPPUNIT_ASSERT_EQUAL(1, manager.getAvailableIds()); + manager.releaseID(id1); + CPPUNIT_ASSERT_EQUAL(1, manager.getAvailableIDs()); cache_t c4; c4.afile = (AudioCacheFile*)4; - auto id4 = manager.registerId(c4); + auto id4 = manager.registerID(c4); CPPUNIT_ASSERT(id4 != CACHE_DUMMYID); CPPUNIT_ASSERT(id4 != CACHE_NOID); - CPPUNIT_ASSERT_EQUAL(0, manager.getAvailableIds()); + CPPUNIT_ASSERT_EQUAL(0, manager.getAvailableIDs()); cache_t& tc4 = manager.getCache(id4); CPPUNIT_ASSERT_EQUAL(c4.afile, tc4.afile); - manager.releaseId(id2); - CPPUNIT_ASSERT_EQUAL(1, manager.getAvailableIds()); + manager.releaseID(id2); + CPPUNIT_ASSERT_EQUAL(1, manager.getAvailableIDs()); - manager.releaseId(id4); - CPPUNIT_ASSERT_EQUAL(2, manager.getAvailableIds()); + manager.releaseID(id4); + CPPUNIT_ASSERT_EQUAL(2, manager.getAvailableIDs()); } }; -- cgit v1.2.3 From 14d3bed63b3e51439fbf3632b470729846ca6913 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Wed, 20 Jan 2016 11:55:00 +0100 Subject: Ignore test log output. --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index dcaeccb..12fd74f 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,6 @@ test/engine test/gui drumgizmo-*.tar.gz tst -vst/Makefile.mingw32 \ No newline at end of file +vst/Makefile.mingw32 +test/*.log +test/*.trs \ No newline at end of file -- cgit v1.2.3