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(),