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/audiocacheeventhandler.cc | 315 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 src/audiocacheeventhandler.cc (limited to 'src/audiocacheeventhandler.cc') 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(); +} -- cgit v1.2.3