Commit b80a135c authored by Max Kellermann's avatar Max Kellermann

output/pulse: implement Interrupt()

parent 4ad525d9
ver 0.22.1 (not yet released) ver 0.22.1 (not yet released)
* output * output
- alsa: don't deadlock when the ALSA driver is buggy - 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) ver 0.22 (2020/09/23)
* protocol * protocol
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "lib/pulse/LogError.hxx" #include "lib/pulse/LogError.hxx"
#include "lib/pulse/LockGuard.hxx" #include "lib/pulse/LockGuard.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Error.hxx"
#include "mixer/MixerList.hxx" #include "mixer/MixerList.hxx"
#include "mixer/plugins/PulseMixerPlugin.hxx" #include "mixer/plugins/PulseMixerPlugin.hxx"
#include "util/ScopeExit.hxx" #include "util/ScopeExit.hxx"
...@@ -57,6 +58,15 @@ class PulseOutput final : AudioOutput { ...@@ -57,6 +58,15 @@ class PulseOutput final : AudioOutput {
bool pause; 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); explicit PulseOutput(const ConfigBlock &block);
public: public:
...@@ -99,6 +109,8 @@ public: ...@@ -99,6 +109,8 @@ public:
void Open(AudioFormat &audio_format) override; void Open(AudioFormat &audio_format) override;
void Close() noexcept override; void Close() noexcept override;
void Interrupt() noexcept override;
[[nodiscard]] std::chrono::steady_clock::duration Delay() const noexcept override; [[nodiscard]] std::chrono::steady_clock::duration Delay() const noexcept override;
size_t Play(const void *chunk, size_t size) override; size_t Play(const void *chunk, size_t size) override;
void Cancel() noexcept override; void Cancel() noexcept override;
...@@ -677,6 +689,7 @@ PulseOutput::Open(AudioFormat &audio_format) ...@@ -677,6 +689,7 @@ PulseOutput::Open(AudioFormat &audio_format)
} }
pause = false; pause = false;
interrupted = false;
} }
void void
...@@ -705,6 +718,21 @@ PulseOutput::Close() noexcept ...@@ -705,6 +718,21 @@ PulseOutput::Close() noexcept
} }
void 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() PulseOutput::WaitStream()
{ {
while (true) { while (true) {
...@@ -719,6 +747,9 @@ PulseOutput::WaitStream() ...@@ -719,6 +747,9 @@ PulseOutput::WaitStream()
"failed to connect the stream"); "failed to connect the stream");
case PA_STREAM_CREATING: case PA_STREAM_CREATING:
if (interrupted)
throw AudioOutputInterrupted{};
pa_threaded_mainloop_wait(mainloop); pa_threaded_mainloop_wait(mainloop);
break; break;
} }
...@@ -784,6 +815,9 @@ PulseOutput::Play(const void *chunk, size_t size) ...@@ -784,6 +815,9 @@ PulseOutput::Play(const void *chunk, size_t size)
if (pa_stream_is_suspended(stream)) if (pa_stream_is_suspended(stream))
throw std::runtime_error("suspended"); throw std::runtime_error("suspended");
if (interrupted)
throw AudioOutputInterrupted{};
pa_threaded_mainloop_wait(mainloop); pa_threaded_mainloop_wait(mainloop);
if (pa_stream_get_state(stream) != PA_STREAM_READY) if (pa_stream_get_state(stream) != PA_STREAM_READY)
...@@ -813,6 +847,7 @@ PulseOutput::Cancel() noexcept ...@@ -813,6 +847,7 @@ PulseOutput::Cancel() noexcept
assert(stream != nullptr); assert(stream != nullptr);
Pulse::LockGuard lock(mainloop); Pulse::LockGuard lock(mainloop);
interrupted = false;
if (pa_stream_get_state(stream) != PA_STREAM_READY) { if (pa_stream_get_state(stream) != PA_STREAM_READY) {
/* no need to flush when the stream isn't connected /* no need to flush when the stream isn't connected
...@@ -842,6 +877,7 @@ PulseOutput::Pause() ...@@ -842,6 +877,7 @@ PulseOutput::Pause()
Pulse::LockGuard lock(mainloop); Pulse::LockGuard lock(mainloop);
pause = true; pause = true;
interrupted = false;
/* check if the stream is (already/still) connected */ /* check if the stream is (already/still) connected */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment