diff --git a/NEWS b/NEWS index d2d414a7e..174dce950 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,7 @@ ver 0.21 (not yet released) - pcm: support audio/L24 (RFC 3190) * output - alsa: non-blocking mode + - alsa: change "dop" and "allowed_formats" settings at runtime - shout: support the Shine encoder plugin - sndio: remove support for the broken RoarAudio sndio emulation * mixer diff --git a/doc/user.xml b/doc/user.xml index a739046bf..616f137df 100644 --- a/doc/user.xml +++ b/doc/user.xml @@ -3539,6 +3539,52 @@ run + + + The following attributes can be configured at runtime using + the outputset command: + + + + + + + Setting + Description + + + + + + dop + 1|0 + + + + Allows changing the dop + configuration setting at runtime. This takes + effect the next time the output is opened. + + + + + + + allowed_formats + F1 F2 ... + + + + Allows changing the + allowed_formats configuration + setting at runtime. This takes effect the next + time the output is opened. + + + + + +
diff --git a/src/lib/alsa/AllowedFormat.cxx b/src/lib/alsa/AllowedFormat.cxx index e4c69388c..46d0cd3a9 100644 --- a/src/lib/alsa/AllowedFormat.cxx +++ b/src/lib/alsa/AllowedFormat.cxx @@ -21,6 +21,7 @@ #include "AllowedFormat.hxx" #include "AudioParser.hxx" #include "util/IterableSplitString.hxx" +#include "util/StringBuffer.hxx" #include @@ -65,4 +66,24 @@ AllowedFormat::ParseList(StringView s) return list; } +std::string +ToString(const std::forward_list &allowed_formats) noexcept +{ + std::string result; + + for (const auto &i : allowed_formats) { + if (!result.empty()) + result.push_back(' '); + + result += ::ToString(i.format); + +#ifdef ENABLE_DSD + if (i.dop) + result += "=dop"; +#endif + } + + return result; +} + } // namespace Alsa diff --git a/src/lib/alsa/AllowedFormat.hxx b/src/lib/alsa/AllowedFormat.hxx index 12bc5a21f..35658f48b 100644 --- a/src/lib/alsa/AllowedFormat.hxx +++ b/src/lib/alsa/AllowedFormat.hxx @@ -24,6 +24,7 @@ #include "AudioFormat.hxx" #include +#include struct StringView; @@ -54,6 +55,9 @@ struct AllowedFormat { static std::forward_list ParseList(StringView s); }; +std::string +ToString(const std::forward_list &allowed_formats) noexcept; + } // namespace Alsa #endif diff --git a/src/output/plugins/AlsaOutputPlugin.cxx b/src/output/plugins/AlsaOutputPlugin.cxx index de3e11933..fab0a85d1 100644 --- a/src/output/plugins/AlsaOutputPlugin.cxx +++ b/src/output/plugins/AlsaOutputPlugin.cxx @@ -69,7 +69,7 @@ class AlsaOutput final * * @see http://dsd-guide.com/dop-open-standard */ - const bool dop_setting; + bool dop_setting; #endif /** libasound's buffer_time setting (in microseconds) */ @@ -83,6 +83,11 @@ class AlsaOutput final std::forward_list allowed_formats; + /** + * Protects #dop_setting and #allowed_formats. + */ + mutable Mutex attributes_mutex; + /** the libasound PCM device handle */ snd_pcm_t *pcm; @@ -186,6 +191,9 @@ public: } private: + const std::map GetAttributes() const noexcept override; + void SetAttribute(std::string &&name, std::string &&value) override; + void Enable() override; void Disable() noexcept override; @@ -348,6 +356,40 @@ AlsaOutput::AlsaOutput(EventLoop &_loop, const ConfigBlock &block) allowed_formats = Alsa::AllowedFormat::ParseList(allowed_formats_string); } +const std::map +AlsaOutput::GetAttributes() const noexcept +{ + const std::lock_guard lock(attributes_mutex); + + return { + std::make_pair("allowed_formats", + Alsa::ToString(allowed_formats)), +#ifdef ENABLE_DSD + std::make_pair("dop", dop_setting ? "1" : "0"), +#endif + }; +} + +void +AlsaOutput::SetAttribute(std::string &&name, std::string &&value) +{ + if (name == "allowed_formats") { + const std::lock_guard lock(attributes_mutex); + allowed_formats = Alsa::AllowedFormat::ParseList({value.data(), value.length()}); +#ifdef ENABLE_DSD + } else if (name == "dop") { + const std::lock_guard lock(attributes_mutex); + if (value == "0") + dop_setting = false; + else if (value == "1") + dop_setting = true; + else + throw std::invalid_argument("Bad 'dop' value"); +#endif + } else + AudioOutput::SetAttribute(std::move(name), std::move(value)); +} + void AlsaOutput::Enable() { @@ -548,15 +590,23 @@ void AlsaOutput::Open(AudioFormat &audio_format) { #ifdef ENABLE_DSD - bool dop = dop_setting; + bool dop; #endif - if (!allowed_formats.empty()) { - const auto &a = BestMatch(allowed_formats, audio_format); - audio_format.ApplyMask(a.format); + { + const std::lock_guard lock(attributes_mutex); #ifdef ENABLE_DSD - dop = a.dop; + dop = dop_setting; #endif + + if (!allowed_formats.empty()) { + const auto &a = BestMatch(allowed_formats, + audio_format); + audio_format.ApplyMask(a.format); +#ifdef ENABLE_DSD + dop = a.dop; +#endif + } } int err = snd_pcm_open(&pcm, GetDevice(),