output/alsa: add attributes "dop" and "allowed_formats"
This commit is contained in:
parent
86a06a7acc
commit
8ac73a9eba
1
NEWS
1
NEWS
|
@ -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
|
||||||
|
|
46
doc/user.xml
46
doc/user.xml
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,15 +590,23 @@ void
|
||||||
AlsaOutput::Open(AudioFormat &audio_format)
|
AlsaOutput::Open(AudioFormat &audio_format)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_DSD
|
#ifdef ENABLE_DSD
|
||||||
bool dop = dop_setting;
|
bool dop;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!allowed_formats.empty()) {
|
{
|
||||||
const auto &a = BestMatch(allowed_formats, audio_format);
|
const std::lock_guard<Mutex> lock(attributes_mutex);
|
||||||
audio_format.ApplyMask(a.format);
|
|
||||||
#ifdef ENABLE_DSD
|
#ifdef ENABLE_DSD
|
||||||
dop = a.dop;
|
dop = dop_setting;
|
||||||
#endif
|
#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(),
|
int err = snd_pcm_open(&pcm, GetDevice(),
|
||||||
|
|
Loading…
Reference in New Issue