output/alsa: add attributes "dop" and "allowed_formats"

This commit is contained in:
Max Kellermann 2017-12-19 10:44:29 +01:00
parent 86a06a7acc
commit 8ac73a9eba
5 changed files with 128 additions and 6 deletions

1
NEWS
View File

@ -12,6 +12,7 @@ ver 0.21 (not yet released)
- pcm: support audio/L24 (RFC 3190) - pcm: support audio/L24 (RFC 3190)
* output * output
- alsa: non-blocking mode - alsa: non-blocking mode
- alsa: change "dop" and "allowed_formats" settings at runtime
- shout: support the Shine encoder plugin - shout: support the Shine encoder plugin
- sndio: remove support for the broken RoarAudio sndio emulation - sndio: remove support for the broken RoarAudio sndio emulation
* mixer * mixer

View File

@ -3539,6 +3539,52 @@ run</programlisting>
</tbody> </tbody>
</tgroup> </tgroup>
</informaltable> </informaltable>
<para>
The following attributes can be configured at runtime using
the <command>outputset</command> command:
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>dop</varname>
<parameter>1|0</parameter>
</entry>
<entry>
<para>
Allows changing the <varname>dop</varname>
configuration setting at runtime. This takes
effect the next time the output is opened.
</para>
</entry>
</row>
<row>
<entry>
<varname>allowed_formats</varname>
<parameter>F1 F2 ...</parameter>
</entry>
<entry>
<para>
Allows changing the
<varname>allowed_formats</varname> configuration
setting at runtime. This takes effect the next
time the output is opened.
</para>
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section> </section>
<section> <section>

View File

@ -21,6 +21,7 @@
#include "AllowedFormat.hxx" #include "AllowedFormat.hxx"
#include "AudioParser.hxx" #include "AudioParser.hxx"
#include "util/IterableSplitString.hxx" #include "util/IterableSplitString.hxx"
#include "util/StringBuffer.hxx"
#include <stdexcept> #include <stdexcept>
@ -65,4 +66,24 @@ AllowedFormat::ParseList(StringView s)
return list; return list;
} }
std::string
ToString(const std::forward_list<AllowedFormat> &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 } // namespace Alsa

View File

@ -24,6 +24,7 @@
#include "AudioFormat.hxx" #include "AudioFormat.hxx"
#include <forward_list> #include <forward_list>
#include <string>
struct StringView; struct StringView;
@ -54,6 +55,9 @@ struct AllowedFormat {
static std::forward_list<AllowedFormat> ParseList(StringView s); static std::forward_list<AllowedFormat> ParseList(StringView s);
}; };
std::string
ToString(const std::forward_list<AllowedFormat> &allowed_formats) noexcept;
} // namespace Alsa } // namespace Alsa
#endif #endif

View File

@ -69,7 +69,7 @@ class AlsaOutput final
* *
* @see http://dsd-guide.com/dop-open-standard * @see http://dsd-guide.com/dop-open-standard
*/ */
const bool dop_setting; bool dop_setting;
#endif #endif
/** libasound's buffer_time setting (in microseconds) */ /** libasound's buffer_time setting (in microseconds) */
@ -83,6 +83,11 @@ class AlsaOutput final
std::forward_list<Alsa::AllowedFormat> allowed_formats; std::forward_list<Alsa::AllowedFormat> allowed_formats;
/**
* Protects #dop_setting and #allowed_formats.
*/
mutable Mutex attributes_mutex;
/** the libasound PCM device handle */ /** the libasound PCM device handle */
snd_pcm_t *pcm; snd_pcm_t *pcm;
@ -186,6 +191,9 @@ public:
} }
private: private:
const std::map<std::string, std::string> GetAttributes() const noexcept override;
void SetAttribute(std::string &&name, std::string &&value) override;
void Enable() override; void Enable() override;
void Disable() noexcept override; void Disable() noexcept override;
@ -348,6 +356,40 @@ AlsaOutput::AlsaOutput(EventLoop &_loop, const ConfigBlock &block)
allowed_formats = Alsa::AllowedFormat::ParseList(allowed_formats_string); allowed_formats = Alsa::AllowedFormat::ParseList(allowed_formats_string);
} }
const std::map<std::string, std::string>
AlsaOutput::GetAttributes() const noexcept
{
const std::lock_guard<Mutex> 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<Mutex> lock(attributes_mutex);
allowed_formats = Alsa::AllowedFormat::ParseList({value.data(), value.length()});
#ifdef ENABLE_DSD
} else if (name == "dop") {
const std::lock_guard<Mutex> 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 void
AlsaOutput::Enable() AlsaOutput::Enable()
{ {
@ -548,16 +590,24 @@ void
AlsaOutput::Open(AudioFormat &audio_format) AlsaOutput::Open(AudioFormat &audio_format)
{ {
#ifdef ENABLE_DSD #ifdef ENABLE_DSD
bool dop = dop_setting; bool dop;
#endif
{
const std::lock_guard<Mutex> lock(attributes_mutex);
#ifdef ENABLE_DSD
dop = dop_setting;
#endif #endif
if (!allowed_formats.empty()) { if (!allowed_formats.empty()) {
const auto &a = BestMatch(allowed_formats, audio_format); const auto &a = BestMatch(allowed_formats,
audio_format);
audio_format.ApplyMask(a.format); audio_format.ApplyMask(a.format);
#ifdef ENABLE_DSD #ifdef ENABLE_DSD
dop = a.dop; dop = a.dop;
#endif #endif
} }
}
int err = snd_pcm_open(&pcm, GetDevice(), int err = snd_pcm_open(&pcm, GetDevice(),
SND_PCM_STREAM_PLAYBACK, mode); SND_PCM_STREAM_PLAYBACK, mode);