diff --git a/NEWS b/NEWS index 19aa3b898..5de07ec3c 100644 --- a/NEWS +++ b/NEWS @@ -33,6 +33,7 @@ ver 0.24 (not yet released) * tags - new tags "TitleSort", "Mood" * output + - add option "always_off" - alsa: require alsa-lib 1.1 or later - pipewire: map tags "Date" and "Comment" * switch to C++20 diff --git a/doc/user.rst b/doc/user.rst index 44d4285f4..2206c3d1b 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;