diff options
Diffstat (limited to 'src/audiocacheeventhandler.cc')
-rw-r--r-- | src/audiocacheeventhandler.cc | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/src/audiocacheeventhandler.cc b/src/audiocacheeventhandler.cc new file mode 100644 index 0000000..7322785 --- /dev/null +++ b/src/audiocacheeventhandler.cc @@ -0,0 +1,326 @@ +/* -*- 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 <assert.h> + +#include <hugin.hpp> + +#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& id_manager) + // Hack to be able to forward declare CacheEvent: + : eventqueue(new std::list<CacheEvent>()) + , id_manager(id_manager) +{ +} + +AudioCacheEventHandler::~AudioCacheEventHandler() +{ + // Close all ids already enqueued to be closed. + clearEvents(); + + auto active_ids = id_manager.getActiveIDs(); + for(auto id : active_ids) + { + 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; + + 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 cache_event; + cache_event.eventType = EventType::LoadNext; + cache_event.pos = pos; + cache_event.afile = afile; + + CacheChannel c; + c.channel = channel; + c.samples = buffer; + + *ready = false; + c.ready = ready; + + cache_event.channels.insert(cache_event.channels.end(), c); + + pushEvent(cache_event); +} + +void AudioCacheEventHandler::pushCloseEvent(cacheid_t id) +{ + CacheEvent cache_event; + cache_event.eventType = EventType::Close; + cache_event.id = id; + + pushEvent(cache_event); +} + +void AudioCacheEventHandler::setChunkSize(size_t chunksize) +{ + DEBUG(cache, "%s\n", __PRETTY_FUNCTION__); + + // We should already locked when this method is called. + //assert(!mutex.try_lock()); + + if(this->chunksize == chunksize) + { + return; + } + + DEBUG(cache, "1)\n"); + + // Remove all events from event queue. + clearEvents(); + + DEBUG(cache, "2)\n"); + + // Skip all active cacheids and make their buffers point at nodata. + id_manager.disableActive(); + + DEBUG(cache, "3)\n"); + + this->chunksize = chunksize; +} + +size_t AudioCacheEventHandler::chunkSize() +{ + return chunksize; +} + +AudioCacheFile& AudioCacheEventHandler::openFile(const std::string& filename) +{ + std::lock_guard<std::mutex> lock(mutex); + return files.getFile(filename); +} + +void AudioCacheEventHandler::clearEvents() +{ + // Iterate all events ignoring load events and handling close events. + for(auto& event : *eventqueue) + { + if(event.eventType == EventType::Close) + { + handleCloseCache(event.id); // This method does not lock. + } + } + + eventqueue->clear(); +} + +void AudioCacheEventHandler::handleLoadNextEvent(CacheEvent& cache_event) +{ + cache_event.afile->readChunk(cache_event.channels, cache_event.pos, + chunksize); +} + +void AudioCacheEventHandler::handleCloseEvent(CacheEvent& cache_event) +{ + std::lock_guard<std::mutex> lock(mutex); + handleCloseCache(cache_event.id); +} + +void AudioCacheEventHandler::handleCloseCache(cacheid_t cacheid) +{ + auto& cache = id_manager.getCache(cacheid); + + files.releaseFile(cache.afile->getFilename()); + + delete[] cache.front; + delete[] cache.back; + + id_manager.releaseID(cacheid); +} + +void AudioCacheEventHandler::handleEvent(CacheEvent& cache_event) +{ + switch(cache_event.eventType) + { + case EventType::LoadNext: + handleLoadNextEvent(cache_event); + break; + case EventType::Close: + handleCloseEvent(cache_event); + 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 cache_event = eventqueue->front(); + eventqueue->pop_front(); + mutex.unlock(); + + // TODO: Skip event if cache_event.pos < cache.pos + //if(!cache_event.active) + //{ + // continue; + //} + + handleEvent(cache_event); + } +} + +void AudioCacheEventHandler::pushEvent(CacheEvent& cache_event) +{ + if(!threaded) + { + handleEvent(cache_event); + return; + } + + { + std::lock_guard<std::mutex> lock(mutex); + + bool found = false; + + if(cache_event.eventType == EventType::LoadNext) + { + for(auto& queued_event : *eventqueue) + { + if((queued_event.eventType == EventType::LoadNext) && + (cache_event.afile->getFilename() == + queued_event.afile->getFilename()) && + (cache_event.pos == queued_event.pos)) + { + // Append channel and buffer to the existing event. + queued_event.channels.insert(queued_event.channels.end(), + cache_event.channels.begin(), + cache_event.channels.end()); + found = true; + break; + } + } + } + + if(!found) + { + // The event was not already on the list, create a new one. + eventqueue->push_back(cache_event); + } + } + + sem.post(); +} |