summaryrefslogtreecommitdiff
path: root/src/audiocacheeventhandler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/audiocacheeventhandler.cc')
-rw-r--r--src/audiocacheeventhandler.cc315
1 files changed, 315 insertions, 0 deletions
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<CacheEvent>())
+ , 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<std::mutex> l(mutex);
+ return files.getFile(filename);
+}
+
+void AudioCacheEventHandler::clearEvents()
+{
+ std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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();
+}