/* * Xournal++ * * Queue to connect an audio producer and an audio consumer * * @author Xournal++ Team * https://github.com/xournalpp/xournalpp * * @license GNU GPLv2 or later */ #pragma once #include #include #include #include #include #include #include template class AudioQueue { public: void reset() { std::lock_guard lock(internalLock); this->popNotified = false; this->pushNotified = false; this->streamEnd = false; internalQueue.clear(); this->sampleRate = -1; this->channels = 0; } bool empty() { std::lock_guard lock(internalLock); return internalQueue.empty(); } size_t size() { std::lock_guard lock(internalLock); return internalQueue.size(); } template void emplace(Iter begI, Iter endI) { std::lock_guard lock(internalLock); std::move(begI, endI, std::front_inserter(internalQueue)); this->pushNotified = true; this->pushLockCondition.notify_one(); } template InsertIter pop(InsertIter insertIter, size_t nSamples) { std::lock_guard lock(internalLock); if (this->channels == 0) { this->popNotified = true; this->popLockCondition.notify_one(); return insertIter; } auto queueSize = internalQueue.size(); auto returnBufferLength = std::min(nSamples, queueSize - queueSize % this->channels); auto begI = rbegin(internalQueue); auto endI = std::next(begI, returnBufferLength); auto ret = std::move(begI, endI, insertIter); internalQueue.erase(endI.base(), begI.base()); this->popNotified = true; this->popLockCondition.notify_one(); return ret; } void signalEndOfStream() { std::lock_guard lock(internalLock); this->streamEnd = true; this->pushNotified = true; this->popNotified = true; this->pushLockCondition.notify_one(); this->popLockCondition.notify_one(); } void waitForProducer(std::unique_lock& lock) { // static_assert(lock.mutex() == &this->queueLock); assert(lock.mutex() == &this->queueLock); while (!this->pushNotified && !hasStreamEnded()) { this->pushLockCondition.wait(lock); } this->pushNotified = false; } void waitForConsumer(std::unique_lock& lock) { // static_assert(lock.mutex() == &this->queueLock); assert(lock.mutex() == &this->queueLock); while (!this->popNotified && !hasStreamEnded()) { this->popLockCondition.wait(lock); } this->popNotified = false; } bool hasStreamEnded() { std::lock_guard lock(internalLock); return this->streamEnd; } [[nodiscard]] std::unique_lock acquire_lock() { std::unique_lock retLock{this->queueLock, std::defer_lock}; std::lock(retLock, this->internalLock); std::lock_guard{this->internalLock, std::adopt_lock}; return retLock; } void setAudioAttributes(double lSampleRate, unsigned int lChannels) { std::lock_guard lock(internalLock); this->sampleRate = lSampleRate; this->channels = lChannels; } /** * @return std::pair, * std::pair::first is the sample rate and std::pair::second the channel count. */ [[nodiscard]] std::pair getAudioAttributes() { std::lock_guard lock(internalLock); return {this->sampleRate, static_cast(this->channels)}; } private: std::mutex queueLock; std::mutex internalLock; std::deque internalQueue; std::condition_variable pushLockCondition; std::condition_variable popLockCondition; double sampleRate{std::numeric_limits::quiet_NaN()}; unsigned int channels{0}; bool streamEnd{false}; bool pushNotified{false}; bool popNotified{false}; };