/* -*- 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 Lesser General Public License as published by * the Free Software Foundation; either version 3 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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 #include #include "audiocachefile.h" #include "audiocache.h" #include "audiocacheidmanager.h" enum class EventType { LoadNext, Close, }; class CacheEvent { public: EventType event_type; // For close event: cacheid_t id; // For load next event: size_t pos; AudioCacheFile* afile; CacheChannels channels; }; AudioCacheEventHandler::AudioCacheEventHandler(AudioCacheIDManager& id_manager) : 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); } } 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) { this->threaded.store(threaded); } bool AudioCacheEventHandler::isThreaded() const { return threaded.load(); } 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.event_type = 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.event_type = 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, "setChunkSize 1\n"); // Remove all events from event queue. clearEvents(); DEBUG(cache, "setChunkSize 2\n"); // Skip all active cacheids and make their buffers point at nodata. id_manager.disableActive(); DEBUG(cache, "setChunkSize 3\n"); this->chunksize = chunksize; } size_t AudioCacheEventHandler::getChunkSize() const { return chunksize; } AudioCacheFile& AudioCacheEventHandler::openFile(const std::string& filename) { std::lock_guard 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.event_type == EventType::Close) { handleCloseCache(event.id); // This method does not lock. } } eventqueue.clear(); } void AudioCacheEventHandler::handleLoadNextEvent(CacheEvent& cache_event) { assert(cache_event.afile); // Assert that we have an audio file cache_event.afile->readChunk(cache_event.channels, cache_event.pos, chunksize); } void AudioCacheEventHandler::handleCloseEvent(CacheEvent& cache_event) { std::lock_guard lock(mutex); handleCloseCache(cache_event.id); } void AudioCacheEventHandler::handleCloseCache(cacheid_t id) { auto& cache = id_manager.getCache(id); // Only close the file if we have also opened it. if(cache.afile) { files.releaseFile(cache.afile->getFilename()); } delete[] cache.front; delete[] cache.back; id_manager.releaseID(id); } void AudioCacheEventHandler::handleEvent(CacheEvent& cache_event) { switch(cache_event.event_type) { 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(); handleEvent(cache_event); } } void AudioCacheEventHandler::pushEvent(CacheEvent& cache_event) { if(!threaded.load()) { handleEvent(cache_event); return; } { std::lock_guard lock(mutex); bool found = false; if(cache_event.event_type == EventType::LoadNext) { for(auto& queued_event : eventqueue) { if(queued_event.event_type == EventType::LoadNext) { assert(cache_event.afile); // Assert that we have an audio file assert(queued_event.afile); // Assert that we have an audio file if((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(); }