diff --git a/src/output/Control.hxx b/src/output/Control.hxx index c4dd2b901..9ec66a2e1 100644 --- a/src/output/Control.hxx +++ b/src/output/Control.hxx @@ -404,8 +404,29 @@ public: gcc_pure bool LockIsChunkConsumed(const MusicChunk &chunk) const noexcept; + /** + * There's only one chunk left in the pipe (#pipe), and all + * audio outputs have consumed it already. Clear the + * reference. + * + * This stalls playback to give the caller a chance to shift + * the #MusicPipe without getting disturbed; after this, + * LockAllowPlay() must be called to resume playback. + */ void ClearTailChunk(const MusicChunk &chunk) noexcept { + if (!IsOpen()) + return; + source.ClearTailChunk(chunk); + allow_play = false; + } + + /** + * Locking wrapper for ClearTailChunk(). + */ + void LockClearTailChunk(const MusicChunk &chunk) noexcept { + const std::lock_guard lock(mutex); + ClearTailChunk(chunk); } void LockPlay() noexcept; diff --git a/src/output/MultipleOutputs.cxx b/src/output/MultipleOutputs.cxx index d24aa1eac..d8a73adf8 100644 --- a/src/output/MultipleOutputs.cxx +++ b/src/output/MultipleOutputs.cxx @@ -271,35 +271,10 @@ MultipleOutputs::IsChunkConsumed(const MusicChunk *chunk) const noexcept return true; } -inline void -MultipleOutputs::ClearTailChunk(const MusicChunk *chunk, - bool *locked) noexcept -{ - assert(chunk->next == nullptr); - assert(pipe->Contains(chunk)); - - for (unsigned i = 0, n = outputs.size(); i != n; ++i) { - auto &ao = *outputs[i]; - - /* this mutex will be unlocked by the caller when it's - ready */ - ao.mutex.lock(); - locked[i] = ao.IsOpen(); - - if (!locked[i]) { - ao.mutex.unlock(); - continue; - } - - ao.ClearTailChunk(*chunk); - } -} - unsigned MultipleOutputs::CheckPipe() noexcept { const MusicChunk *chunk; - bool locked[outputs.size()]; assert(pipe != nullptr); @@ -320,18 +295,18 @@ MultipleOutputs::CheckPipe() noexcept if (is_tail) /* this is the tail of the pipe - clear the chunk reference in all outputs */ - ClearTailChunk(chunk, locked); + for (const auto &ao : outputs) + ao->LockClearTailChunk(*chunk); /* remove the chunk from the pipe */ const auto shifted = pipe->Shift(); assert(shifted.get() == chunk); if (is_tail) - /* unlock all audio outputs which were locked - by clear_tail_chunk() */ - for (unsigned i = 0, n = outputs.size(); i != n; ++i) - if (locked[i]) - outputs[i]->mutex.unlock(); + /* resume playback which has been suspended by + LockClearTailChunk() */ + for (const auto &ao : outputs) + ao->LockAllowPlay(); /* chunk is automatically returned to the buffer by ~MusicChunkPtr() */ diff --git a/src/output/MultipleOutputs.hxx b/src/output/MultipleOutputs.hxx index a332c9558..6e76a432a 100644 --- a/src/output/MultipleOutputs.hxx +++ b/src/output/MultipleOutputs.hxx @@ -171,13 +171,6 @@ private: */ bool IsChunkConsumed(const MusicChunk *chunk) const noexcept; - /** - * There's only one chunk left in the pipe (#pipe), and all - * audio outputs have consumed it already. Clear the - * reference. - */ - void ClearTailChunk(const MusicChunk *chunk, bool *locked) noexcept; - /* virtual methods from class PlayerOutputs */ void EnableDisable() override; void Open(const AudioFormat audio_format) override;