From ab196f7afc9ab7cb757c53b4d587875adc7e4f57 Mon Sep 17 00:00:00 2001 From: Simon Arlott Date: Thu, 20 Apr 2023 21:18:17 +0100 Subject: [PATCH] output/Control: Support `always_off` outputs that are never used for playback Add an `always_off` option to outputs that causes them to never start playback even if they're enabled. This allows placeholder `null` outputs to be defined for the purpose of having an external client react to the enabled state without the side effects of real outputs. Like an external mixer, the client can perform some action when an output is enabled. Normally `null` outputs can be used for playback so it's possible for MPD to continue playback silently if a problem occurs with all the real outputs (or there are none enabled). --- doc/user.rst | 5 +++++ src/output/Control.cxx | 7 ++++++- src/output/Control.hxx | 9 +++++++++ src/output/MultipleOutputs.cxx | 4 ++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/doc/user.rst b/doc/user.rst index 7eb891229..3f11daf28 100644 --- a/doc/user.rst +++ b/doc/user.rst @@ -517,6 +517,11 @@ The following table lists the audio_output options valid for all plugins: - If set to no, then :program:`MPD` will not send tags to this output. This is only useful for output plugins that can receive tags, for example the httpd output plugin. * - **always_on yes|no** - If set to yes, then :program:`MPD` attempts to keep this audio output always open. This may be useful for streaming servers, when you don't want to disconnect all listeners even when playback is accidentally stopped. + * - **always_off yes|no** + - If set to yes, then :program:`MPD` never uses this audio output for + playback even if it's enabled. This can be used with the null output + plugin to create placeholder outputs for other software to react to + the enabled state without affecting playback. * - **mixer_type hardware|software|null|none** - Specifies which mixer should be used for this audio output: the hardware mixer (available for ALSA :ref:`alsa_plugin`, OSS diff --git a/src/output/Control.cxx b/src/output/Control.cxx index 620b9c177..cf10aeb2a 100644 --- a/src/output/Control.cxx +++ b/src/output/Control.cxx @@ -25,6 +25,7 @@ AudioOutputControl::AudioOutputControl(std::unique_ptr _out thread(BIND_THIS_METHOD(Task)), tags(block.GetBlockValue("tags", true)), always_on(block.GetBlockValue("always_on", false)), + always_off(block.GetBlockValue("always_off", false)), enabled(block.GetBlockValue("enabled", true)) { } @@ -36,7 +37,8 @@ AudioOutputControl::AudioOutputControl(AudioOutputControl &&src, client(_client), thread(BIND_THIS_METHOD(Task)), tags(src.tags), - always_on(src.always_on) + always_on(src.always_on), + always_off(src.always_off) { } @@ -176,6 +178,9 @@ AudioOutputControl::EnableAsync() if (!output) return; + if (always_off) + return; + if (!thread.IsDefined()) { if (!output->SupportsEnableDisable()) { /* don't bother to start the thread now if the diff --git a/src/output/Control.hxx b/src/output/Control.hxx index b28a32ae9..1a4747fac 100644 --- a/src/output/Control.hxx +++ b/src/output/Control.hxx @@ -143,6 +143,11 @@ class AudioOutputControl { */ const bool always_on; + /** + * Should this output never play anything, even when enabled? + */ + const bool always_off; + /** * Has the user enabled this device? */ @@ -274,6 +279,10 @@ public: return !output; } + bool AlwaysOff() const noexcept { + return always_off; + } + /** * Caller must lock the mutex. */ diff --git a/src/output/MultipleOutputs.cxx b/src/output/MultipleOutputs.cxx index eb95bcda8..18464d3a6 100644 --- a/src/output/MultipleOutputs.cxx +++ b/src/output/MultipleOutputs.cxx @@ -215,6 +215,10 @@ MultipleOutputs::Open(const AudioFormat audio_format) for (const auto &ao : outputs) { const std::scoped_lock lock(ao->mutex); + /* can't play on this device even if it's enabled */ + if (ao->AlwaysOff()) + continue; + if (ao->IsEnabled()) enabled = true;