From b80a135cf3cab19dfaef2ddc95a72e67d6c55f2a Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Fri, 2 Oct 2020 10:39:55 +0200 Subject: [PATCH] output/pulse: implement Interrupt() --- NEWS | 1 + src/output/plugins/PulseOutputPlugin.cxx | 36 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/NEWS b/NEWS index 8e438ec1a..2e8dc14a6 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,7 @@ ver 0.22.1 (not yet released) * output - alsa: don't deadlock when the ALSA driver is buggy + - pulse: reduce the delay when stopping or pausing playback ver 0.22 (2020/09/23) * protocol diff --git a/src/output/plugins/PulseOutputPlugin.cxx b/src/output/plugins/PulseOutputPlugin.cxx index 6558232d0..c2e691b76 100644 --- a/src/output/plugins/PulseOutputPlugin.cxx +++ b/src/output/plugins/PulseOutputPlugin.cxx @@ -22,6 +22,7 @@ #include "lib/pulse/LogError.hxx" #include "lib/pulse/LockGuard.hxx" #include "../OutputAPI.hxx" +#include "../Error.hxx" #include "mixer/MixerList.hxx" #include "mixer/plugins/PulseMixerPlugin.hxx" #include "util/ScopeExit.hxx" @@ -57,6 +58,15 @@ class PulseOutput final : AudioOutput { bool pause; + /** + * Was Interrupt() called? This will unblock Play(). It will + * be reset by Cancel() and Pause(), as documented by the + * #AudioOutput interface. + * + * Only initialized while the output is open. + */ + bool interrupted; + explicit PulseOutput(const ConfigBlock &block); public: @@ -99,6 +109,8 @@ public: void Open(AudioFormat &audio_format) override; void Close() noexcept override; + void Interrupt() noexcept override; + [[nodiscard]] std::chrono::steady_clock::duration Delay() const noexcept override; size_t Play(const void *chunk, size_t size) override; void Cancel() noexcept override; @@ -677,6 +689,7 @@ PulseOutput::Open(AudioFormat &audio_format) } pause = false; + interrupted = false; } void @@ -704,6 +717,21 @@ PulseOutput::Close() noexcept DeleteContext(); } +void +PulseOutput::Interrupt() noexcept +{ + if (mainloop == nullptr) + return; + + const Pulse::LockGuard lock(mainloop); + + /* the "interrupted" flag will prevent Play() from blocking, + and will instead throw AudioOutputInterrupted */ + interrupted = true; + + Signal(); +} + void PulseOutput::WaitStream() { @@ -719,6 +747,9 @@ PulseOutput::WaitStream() "failed to connect the stream"); case PA_STREAM_CREATING: + if (interrupted) + throw AudioOutputInterrupted{}; + pa_threaded_mainloop_wait(mainloop); break; } @@ -784,6 +815,9 @@ PulseOutput::Play(const void *chunk, size_t size) if (pa_stream_is_suspended(stream)) throw std::runtime_error("suspended"); + if (interrupted) + throw AudioOutputInterrupted{}; + pa_threaded_mainloop_wait(mainloop); if (pa_stream_get_state(stream) != PA_STREAM_READY) @@ -813,6 +847,7 @@ PulseOutput::Cancel() noexcept assert(stream != nullptr); Pulse::LockGuard lock(mainloop); + interrupted = false; if (pa_stream_get_state(stream) != PA_STREAM_READY) { /* no need to flush when the stream isn't connected @@ -842,6 +877,7 @@ PulseOutput::Pause() Pulse::LockGuard lock(mainloop); pause = true; + interrupted = false; /* check if the stream is (already/still) connected */