Commit b80a135c authored by Max Kellermann's avatar Max Kellermann

output/pulse: implement Interrupt()

parent 4ad525d9
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
......
......@@ -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
......@@ -705,6 +718,21 @@ PulseOutput::Close() noexcept
}
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()
{
while (true) {
......@@ -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 */
......
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