From 88a906395ba7d33ae563e70d8b94c855e3b5a573 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 6 Oct 2013 13:10:00 +0200 Subject: Refactoring of AudioFile loading and message passing from engine to gui and vice versa. --- plugingui/plugingui.cc | 2 +- src/audiofile.cc | 174 +++++++++++++++++++++-------------------- src/audiofile.h | 9 +-- src/drumgizmo.cc | 25 ++---- src/drumkitloader.cc | 208 ++++++++++++++++++------------------------------- src/drumkitloader.h | 51 ++++++++---- src/messagehandler.cc | 7 +- src/messagereceiver.cc | 3 +- src/messagereceiver.h | 1 + src/semaphore.cc | 11 +-- src/semaphore.h | 3 +- 11 files changed, 228 insertions(+), 266 deletions(-) diff --git a/plugingui/plugingui.cc b/plugingui/plugingui.cc index 4f16b34..e452965 100644 --- a/plugingui/plugingui.cc +++ b/plugingui/plugingui.cc @@ -153,7 +153,7 @@ void closeClick(void *ptr) */ PluginGUI::PluginGUI() - : MessageReceiver(MSGRCV_UI) + : MessageReceiver(MSGRCV_UI), sem("plugingui") { windowClosedHandler = NULL; changeMidimapHandler = NULL; diff --git a/src/audiofile.cc b/src/audiofile.cc index 4ac1f94..858edd7 100644 --- a/src/audiofile.cc +++ b/src/audiofile.cc @@ -37,8 +37,6 @@ #include "configuration.h" -#define LAZYLOAD - AudioFile::AudioFile(std::string filename) { is_loaded = false; @@ -46,8 +44,11 @@ AudioFile::AudioFile(std::string filename) data = NULL; size = 0; + +#ifdef LAZYLOAD preloaded_data = NULL; - ref_count = 0; +#endif/*LAZYLOAD*/ + magic = this; } @@ -64,21 +65,104 @@ bool AudioFile::isValid() void AudioFile::unload() { + // Make sure we don't unload the object while loading it... MutexAutolock l(mutex); + + is_loaded = false; + +#ifdef LAZYLOAD if(data == preloaded_data) { delete[] data; data = NULL; size = 0; - } - else { + } else { size = 0; delete[] data; data = NULL; delete preloaded_data; preloaded_data = NULL; } +#else + delete[] data; + data = NULL; + size = 0; +#endif/*LAZYLOAD*/ +} + +void AudioFile::load(int num_samples) +{ + // Make sure we don't unload the object while loading it... + MutexAutolock l(mutex); + + /* + Lazy load of drum kits + init(); + return; + */ + + if(data) return; + + 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; + } + + size = 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; + } + + sample_t* data = new sample_t[size]; + size = sf_read_float(fh, data, size); + + DEBUG(audiofile,"Loaded %d samples %p\n", size, this); + + sf_close(fh); + + if(Conf::samplerate != sf_info.samplerate) { + // Resample data... + size_t osize = size * ratio; + sample_t *odata = new sample_t[osize]; + + SRC_DATA src; + src.data_in = data; + src.input_frames = size; + + src.data_out = odata; + src.output_frames = osize; + + src.src_ratio = ratio; + + // Do the conversion + src_simple(&src, SRC_SINC_BEST_QUALITY, 1); + + delete[] data; + data = odata; + size = src.output_frames; + + DEBUG(audiofile,"Converted into %d samples %p\n", size, this); + } + + this->data = data; + is_loaded = true; + + //DEBUG(audiofile, "Loading of %s completed.\n", filename.c_str()); +} + +bool AudioFile::isLoaded() +{ + return is_loaded; } +#ifdef LAZYLOAD #define SIZE 512*4 void AudioFile::init() { @@ -173,82 +257,4 @@ void AudioFile::reset() delete old_data; mutex.unlock(); } - -void AudioFile::load(int num_samples) -{ - /* - Lazy load of drum kits - init(); - return; - */ - - if(data) return; - - 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; - } - - size = 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; - } - - sample_t* data = new sample_t[size]; - size = sf_read_float(fh, data, size); - - DEBUG(audiofile,"Loaded %d samples %p\n", size, this); - - sf_close(fh); - - if(Conf::samplerate != sf_info.samplerate) { - // Resample data... - size_t osize = size * ratio; - sample_t *odata = new sample_t[osize]; - - SRC_DATA src; - src.data_in = data; - src.input_frames = size; - - src.data_out = odata; - src.output_frames = osize; - - src.src_ratio = ratio; - - // Do the conversion - src_simple(&src, SRC_SINC_BEST_QUALITY, 1); - - delete[] data; - data = odata; - size = src.output_frames; - - DEBUG(audiofile,"Converted into %d samples %p\n", size, this); - } - - - mutex.lock(); - this->data = data; - is_loaded = true; - mutex.unlock(); - - //DEBUG(audiofile, "Loading of %s completed.\n", filename.c_str()); -} - -bool AudioFile::isLoaded() -{ - bool l; - - mutex.lock(); - l = is_loaded; - mutex.unlock(); - - return l; -} +#endif diff --git a/src/audiofile.h b/src/audiofile.h index 20ffdf3..8a190c1 100644 --- a/src/audiofile.h +++ b/src/audiofile.h @@ -36,7 +36,7 @@ #include "mutex.h" #include "audio.h" -#if 0 +/* Plan for lazy loading of audio (Brainstorming) * Encapsulate data array? - Speed issues? @@ -63,9 +63,9 @@ | --------- | | Wave Into --> | SndFile | <----- Read data (directly from array) --------- -#endif/*0*/ +*/ -#define LAZYLOAD +//#define LAZYLOAD #define ALL_SAMPLES -1 @@ -95,13 +95,12 @@ public: #endif/*LAZYLOAD*/ bool isValid(); - int ref_count; Mutex mutex; private: void *magic; - bool is_loaded; + volatile bool is_loaded; }; #endif/*__DRUMGIZMO_AUDIOFILE_H__*/ diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index b99687d..c4ef194 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -43,21 +43,13 @@ DrumGizmo::DrumGizmo(AudioOutputEngine *o, AudioInputEngine *i) : MessageReceiver(MSGRCV_ENGINE), - loader(this), oe(o), ie(i) + loader(), oe(o), ie(i) { - loader.run(); // Start drumkit loader thread. } DrumGizmo::~DrumGizmo() { - /* - AudioFiles::iterator i = audiofiles.begin(); - while(i != audiofiles.end()) { - AudioFile *audiofile = i->second; - delete audiofile; - i++; - } - */ + DEBUG(drumgizmo, "!"); loader.stop(); } @@ -75,8 +67,10 @@ bool DrumGizmo::loadkit(std::string file) DEBUG(drumgizmo, "loadkit(%s)\n", kitfile.c_str()); + // Remove all queue AudioFiles from loader before we actually delete them. loader.skip(); + // Delete all Channels, Instruments, Samples and AudioFiles. kit.clear(); DrumKitParser parser(kitfile, kit); @@ -94,16 +88,7 @@ bool DrumGizmo::loadkit(std::string file) bool DrumGizmo::init(bool preload) { - if(preload) { - /* - AudioFiles::iterator i = audiofiles.begin(); - while(i != audiofiles.end()) { - AudioFile *audiofile = i->second; - audiofile->load(); - i++; - } - */ - } + (void)preload; if(!ie->init(kit.instruments)) return false; if(!oe->init(kit.channels)) return false; diff --git a/src/drumkitloader.cc b/src/drumkitloader.cc index a7a8989..69f656b 100644 --- a/src/drumkitloader.cc +++ b/src/drumkitloader.cc @@ -31,185 +31,129 @@ #include "drumkitparser.h" #include "drumgizmo.h" -DrumKitLoader::DrumKitLoader(DrumGizmo *dg) +DrumKitLoader::DrumKitLoader() + : semaphore("drumkitloader") { - drumgizmo = dg; - is_done = false; - quitit = false; - skipit = false; + running = true; + run(); } DrumKitLoader::~DrumKitLoader() { - if(!quitit) { + if(running) { stop(); } } void DrumKitLoader::stop() { - quitit = true; + { + MutexAutolock l(mutex); + load_queue.clear(); + } + + running = false; semaphore.post(); wait_stop(); } void DrumKitLoader::skip() { - skipit = true; - semaphore.post(); - skip_semaphore.wait(); + MutexAutolock l(mutex); + load_queue.clear(); } bool DrumKitLoader::isDone() { - bool done; - - mutex.lock(); - done = is_done; - mutex.unlock(); - - return done; + MutexAutolock l(mutex); + return load_queue.size() == 0; } void DrumKitLoader::loadKit(DrumKit *kit) { - this->kit = kit; - - mutex.lock(); - is_done = false; - mutex.unlock(); - - semaphore.post(); -} + MutexAutolock l(mutex); -void DrumKitLoader::prepare(AudioFile* af) -{ - DEBUG(loader, "Preparing audiofile %p (%d in queue)\n", - af, load_queue.size()); - mutex.lock(); - af->ref_count++; - load_queue.push_back(af); -// if(ref_count.find(af) == ref_count.end()) { -// ref_count[af]++; -// } -// else { -// ref_count[af] = 0; -// } - mutex.unlock(); - semaphore.post(); -} + DEBUG(loader, "Create AudioFile queue from DrumKit\n"); -void DrumKitLoader::reset(AudioFile* af) -{ - mutex.lock(); - af->ref_count--; - reset_queue.push_back(af); - mutex.unlock(); - semaphore.post(); -} + total_num_audiofiles = 0;// For UI Progress Messages -void DrumKitLoader::thread_main() -{ - while(1) { - DEBUG(loader, "before sem\n"); + { // Count total number of files that need loading: + Instruments::iterator i = kit->instruments.begin(); + while(i != kit->instruments.end()) { + Instrument *instr = *i; + total_num_audiofiles += instr->audiofiles.size(); + i++; + } + } - semaphore.wait(); + { // Now actually queue them for loading: + Instruments::iterator i = kit->instruments.begin(); + while(i != kit->instruments.end()) { + Instrument *instr = *i; + + std::vector::iterator af = instr->audiofiles.begin(); + while(af != instr->audiofiles.end()) { + AudioFile *audiofile = *af; + load_queue.push_back(audiofile); + af++; + } + + i++; + } + } - DEBUG(loader, "after sem\n"); - fflush(stdout); + loaded = 0; // For UI Progress Messages + DEBUG(loader, "Queued %d (size: %d) AudioFiles for loading.\n", + total_num_audiofiles, load_queue.size()); - if(quitit) return; + semaphore.post(); // Start loader loop. +} - if(skipit) { - skip_semaphore.post(); - skipit = false; - continue; +void DrumKitLoader::thread_main() +{ + while(running) { + size_t size; + { + MutexAutolock l(mutex); + size = load_queue.size(); } - if(!load_queue.empty()) { - DEBUG(loader, "Loading remaining of audio file\n"); - AudioFile* af = load_queue.front(); - mutex.lock(); - load_queue.pop_front(); - mutex.unlock(); - af->loadNext(); - } - else if(!reset_queue.empty()) { - AudioFile* af = reset_queue.front(); - mutex.lock(); - if(af->ref_count <= 0) { - af->reset(); - af->ref_count = 0; - } - reset_queue.pop_front(); - mutex.unlock(); + // Only sleep if queue is empty. + if(size == 0) { + //DEBUG(loader, "Wait for sem"); + semaphore.wait(); + //DEBUG(loader, "Sem enter"); } - else { // Initialize drum kit - DEBUG(loader, "Initializing drum kit\n"); - unsigned int count = 0; - - if(kit && !kit->isValid()) goto finish; - { // Count total number of files that need loading: - Instruments::iterator i = kit->instruments.begin(); - while(i != kit->instruments.end()) { - Instrument *instr = *i; - if(instr && !instr->isValid()) goto finish; + AudioFile *audiofile = NULL; - count += instr->audiofiles.size(); - i++; - } - } - - { // Now actually load them: - unsigned int loaded = 0; - Instruments::iterator i = kit->instruments.begin(); - while(i != kit->instruments.end()) { - Instrument *instr = *i; - - if(instr && !instr->isValid()) goto finish; + { + MutexAutolock l(mutex); + if(load_queue.size() == 0) continue; + audiofile = load_queue.front(); + load_queue.pop_front(); + } - std::vector::iterator a = instr->audiofiles.begin(); - while(a != instr->audiofiles.end()) { #if 0 #ifdef WIN32 - SleepEx(5000, FALSE); + SleepEx(5000, FALSE); #else - usleep(5000); + usleep(5000); #endif/*WIN32*/ #endif - AudioFile *af = *a; - - if(af && !af->isValid()) goto finish; - - af->load(); - - loaded++; - - LoadStatusMessage *ls = new LoadStatusMessage(); - ls->number_of_files = count; - ls->numer_of_files_loaded = loaded; - ls->current_file = af->filename; - msghandler.sendMessage(MSGRCV_UI, ls); - - a++; - - if(skipit) goto finish; - } - - i++; - } - } - mutex.lock(); - is_done = true; - mutex.unlock(); + audiofile->load(); + loaded++; - finish: - continue; - } + LoadStatusMessage *ls = new LoadStatusMessage(); + ls->number_of_files = total_num_audiofiles; + ls->numer_of_files_loaded = loaded; + ls->current_file = audiofile->filename; + msghandler.sendMessage(MSGRCV_UI, ls); } + + DEBUG(loader, "Loader thread finished."); } #ifdef TEST_DRUMKITLOADER diff --git a/src/drumkitloader.h b/src/drumkitloader.h index 9d14638..be24812 100644 --- a/src/drumkitloader.h +++ b/src/drumkitloader.h @@ -36,37 +36,62 @@ #include "drumkit.h" -class DrumGizmo; - +/** + * This class is responsible for loading the drumkits in its own thread. + * All interaction calls are simply modifying queues and not doing any + * work in-sync with the caller. + * This means that if loadKit(...) is called, one cannot assume that the + * drumkit has actually been loaded when the call returns. + */ class DrumKitLoader : public Thread { public: - DrumKitLoader(DrumGizmo *drumgizmo); + /** + * The constrcutor starts the loader thread. + */ + DrumKitLoader(); + + /** + * The destructor signals the thread to stop and waits to merge before + * returning (ie. deleting the object will garantuee that the thread has + * been stopped). + */ ~DrumKitLoader(); + /** + * Signal the loader to start loading all audio files contained in kit. + * All other AudioFiles in queue will be removed before the new ones are + * scheduled. + */ void loadKit(DrumKit *kit); - void prepare(AudioFile* af); - void reset(AudioFile* af); + // I have no idea what this does.. + //void reset(AudioFile* af); void thread_main(); + /** + * Simply reports if the load queue is empty (i.e. all AudioFiles has been + * loaded). + */ bool isDone(); + /** + * Signal the loader to stop and wait until it has. + */ void stop(); + + /** + * Skip all queued AudioFiles. + */ void skip(); private: - DrumGizmo *drumgizmo; Semaphore semaphore; - Semaphore skip_semaphore; - DrumKit *kit; - bool is_done; Mutex mutex; - volatile bool quitit; - volatile bool skipit; + volatile bool running; std::list load_queue; - std::list reset_queue; - std::map ref_count; + size_t total_num_audiofiles; + size_t loaded; }; #endif/*__DRUMGIZMO_DRUMKITLOADER_H__*/ diff --git a/src/messagehandler.cc b/src/messagehandler.cc index dcaf5f1..52a89a5 100644 --- a/src/messagehandler.cc +++ b/src/messagehandler.cc @@ -73,16 +73,17 @@ bool MessageHandler::sendMessage(message_receiver_id_t id, Message* msg) //DEBUG(msghandler, "Sending message to id %d\n", id); MessageReceiver *receiver = receivers[id]; - + /* // This code causes sporadic segfaults on windows. if(msg->processing_mode() == Message::FilterMultiple) { Message *pmsg; + MutexAutolock lock(receiver->message_mutex); // Make peek/receive atomic. while( (pmsg = receiver->peekMessage()) != NULL) { - if(pmsg->type() != Message::LoadStatus) break; + if(pmsg->type() != msg->type()) break; // Remove all old messages with same type. delete receiver->receiveMessage(); } } - + */ receiver->sendMessage(msg); return true; } diff --git a/src/messagereceiver.cc b/src/messagereceiver.cc index f391aad..a24482b 100644 --- a/src/messagereceiver.cc +++ b/src/messagereceiver.cc @@ -47,7 +47,6 @@ void MessageReceiver::sendMessage(Message *msg) Message *MessageReceiver::receiveMessage() { - MutexAutolock l(message_mutex); Message *msg = NULL; if(message_queue.size()) { msg = message_queue.front(); @@ -58,7 +57,6 @@ Message *MessageReceiver::receiveMessage() Message *MessageReceiver::peekMessage() { - MutexAutolock l(message_mutex); Message *msg = NULL; if(message_queue.size()) { msg = message_queue.front(); @@ -68,6 +66,7 @@ Message *MessageReceiver::peekMessage() void MessageReceiver::handleMessages(size_t max) { + MutexAutolock l(message_mutex); bool process_all = false; if(max == 0) process_all = true; diff --git a/src/messagereceiver.h b/src/messagereceiver.h index 4185f29..2794091 100644 --- a/src/messagereceiver.h +++ b/src/messagereceiver.h @@ -34,6 +34,7 @@ #include "messagehandler.h" class MessageReceiver { + friend class MessageHandler; public: MessageReceiver(message_receiver_id_t id); ~MessageReceiver(); diff --git a/src/semaphore.cc b/src/semaphore.cc index d43835a..5d90c55 100644 --- a/src/semaphore.cc +++ b/src/semaphore.cc @@ -42,9 +42,10 @@ struct semaphore_private_t { #endif }; -Semaphore::Semaphore() +Semaphore::Semaphore(const char *name) { - DEBUG(semaphore, "Create\n"); + this->name = name; + DEBUG(semaphore, "Create [%s]\n", name); prv = new struct semaphore_private_t(); @@ -60,7 +61,7 @@ Semaphore::Semaphore() Semaphore::~Semaphore() { - DEBUG(semaphore, "Delete\n"); + DEBUG(semaphore, "Delete [%s]\n", name); #ifdef WIN32 CloseHandle(prv->semaphore); @@ -73,7 +74,7 @@ Semaphore::~Semaphore() void Semaphore::post() { - DEBUG(semaphore, "Post\n"); + DEBUG(semaphore, "Post [%s]\n", name); #ifdef WIN32 ReleaseSemaphore(prv->semaphore, 1, NULL); @@ -84,7 +85,7 @@ void Semaphore::post() void Semaphore::wait() { - DEBUG(semaphore, "Wait\n"); + DEBUG(semaphore, "Wait [%s]\n", name); #ifdef WIN32 WaitForSingleObject(prv->semaphore, INFINITE); diff --git a/src/semaphore.h b/src/semaphore.h index 7f8a6ff..7e39f5a 100644 --- a/src/semaphore.h +++ b/src/semaphore.h @@ -31,7 +31,7 @@ struct semaphore_private_t; class Semaphore { public: - Semaphore(); + Semaphore(const char *name = ""); ~Semaphore(); void post(); @@ -39,6 +39,7 @@ public: private: struct semaphore_private_t *prv; + const char *name; }; #endif/*__PRACRO_SEMAPHORE_H__*/ -- cgit v1.2.3