audio_format: wildcards allowed in audio_format configuration
An asterisk means that this attribute should not be enforced, and stays whatever it used to be. This way, some configuration values work like masks.
This commit is contained in:
parent
a5c4566fa1
commit
9526fdbe73
1
NEWS
1
NEWS
@ -34,6 +34,7 @@ ver 0.16 (20??/??/??)
|
|||||||
- pulse: connect to server on MPD startup, implement pause
|
- pulse: connect to server on MPD startup, implement pause
|
||||||
- jack: don't disconnect during pause
|
- jack: don't disconnect during pause
|
||||||
- jack: connect to server on MPD startup
|
- jack: connect to server on MPD startup
|
||||||
|
- wildcards allowed in audio_format configuration
|
||||||
* mixers:
|
* mixers:
|
||||||
- removed support for legacy mixer configuration
|
- removed support for legacy mixer configuration
|
||||||
- reimplemented software volume as mixer+filter plugin
|
- reimplemented software volume as mixer+filter plugin
|
||||||
|
@ -129,6 +129,8 @@ audio that is sent to each audio output. Note that audio outputs may specify
|
|||||||
their own audio format which will be used for actual output to the audio
|
their own audio format which will be used for actual output to the audio
|
||||||
device. An example is "44100:16:2" for 44100Hz, 16 bits, and 2 channels. The
|
device. An example is "44100:16:2" for 44100Hz, 16 bits, and 2 channels. The
|
||||||
default is to use the audio format of the input file.
|
default is to use the audio format of the input file.
|
||||||
|
Any of the three attributes may be an asterisk to specify that this
|
||||||
|
attribute should not be enforced
|
||||||
.TP
|
.TP
|
||||||
.B samplerate_converter <integer or prefix>
|
.B samplerate_converter <integer or prefix>
|
||||||
This specifies the libsamplerate converter to use. The supplied value should
|
This specifies the libsamplerate converter to use. The supplied value should
|
||||||
@ -261,6 +263,8 @@ This specifies the sample rate, bits per sample, and number of channels of
|
|||||||
audio that is sent to the audio output device. See documentation for the
|
audio that is sent to the audio output device. See documentation for the
|
||||||
\fBaudio_output_format\fP parameter for more details. The default is to use
|
\fBaudio_output_format\fP parameter for more details. The default is to use
|
||||||
whatever audio format is passed to the audio output.
|
whatever audio format is passed to the audio output.
|
||||||
|
Any of the three attributes may be an asterisk to specify that this
|
||||||
|
attribute should not be enforced
|
||||||
.SH OPTIONAL ALSA OUTPUT PARAMETERS
|
.SH OPTIONAL ALSA OUTPUT PARAMETERS
|
||||||
.TP
|
.TP
|
||||||
.B device <dev>
|
.B device <dev>
|
||||||
|
@ -300,10 +300,19 @@ cd mpd-version</programlisting>
|
|||||||
<varname>format</varname>
|
<varname>format</varname>
|
||||||
</entry>
|
</entry>
|
||||||
<entry>
|
<entry>
|
||||||
|
<para>
|
||||||
Always open the audio output with the specified audio
|
Always open the audio output with the specified audio
|
||||||
format (samplerate:bits:channels), regardless of the
|
format (samplerate:bits:channels), regardless of the
|
||||||
format of the input file. This is optional for most
|
format of the input file. This is optional for most
|
||||||
plugins.
|
plugins.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Any of the three attributes may be an asterisk to
|
||||||
|
specify that this attribute should not be enforced,
|
||||||
|
example: <parameter>48000:16:*</parameter>.
|
||||||
|
<parameter>*:*:*</parameter> is equal to not having
|
||||||
|
a <varname>format</varname> specification.
|
||||||
|
</para>
|
||||||
</entry>
|
</entry>
|
||||||
</row>
|
</row>
|
||||||
<row>
|
<row>
|
||||||
|
@ -35,9 +35,8 @@ static struct audio_format configured_audio_format;
|
|||||||
void getOutputAudioFormat(const struct audio_format *inAudioFormat,
|
void getOutputAudioFormat(const struct audio_format *inAudioFormat,
|
||||||
struct audio_format *outAudioFormat)
|
struct audio_format *outAudioFormat)
|
||||||
{
|
{
|
||||||
*outAudioFormat = audio_format_defined(&configured_audio_format)
|
*outAudioFormat = *inAudioFormat;
|
||||||
? configured_audio_format
|
audio_format_mask_apply(outAudioFormat, &configured_audio_format);
|
||||||
: *inAudioFormat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void initAudioConfig(void)
|
void initAudioConfig(void)
|
||||||
@ -50,7 +49,7 @@ void initAudioConfig(void)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
ret = audio_format_parse(&configured_audio_format, param->value,
|
ret = audio_format_parse(&configured_audio_format, param->value,
|
||||||
&error);
|
true, &error);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
g_error("error parsing \"%s\" at line %i: %s",
|
g_error("error parsing \"%s\" at line %i: %s",
|
||||||
CONF_AUDIO_OUTPUT_FORMAT, param->line, error->message);
|
CONF_AUDIO_OUTPUT_FORMAT, param->line, error->message);
|
||||||
|
@ -90,6 +90,27 @@ static inline bool audio_format_defined(const struct audio_format *af)
|
|||||||
return af->sample_rate != 0;
|
return af->sample_rate != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the specified #audio_format object is full, i.e. all
|
||||||
|
* attributes are defined. This is more complete than
|
||||||
|
* audio_format_defined(), but slower.
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
audio_format_fully_defined(const struct audio_format *af)
|
||||||
|
{
|
||||||
|
return af->sample_rate != 0 && af->bits != 0 && af->channels != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the specified #audio_format object has at least one
|
||||||
|
* defined value.
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
audio_format_mask_defined(const struct audio_format *af)
|
||||||
|
{
|
||||||
|
return af->sample_rate != 0 || af->bits != 0 || af->channels != 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the sample rate is valid.
|
* Checks whether the sample rate is valid.
|
||||||
*
|
*
|
||||||
@ -132,6 +153,18 @@ static inline bool audio_format_valid(const struct audio_format *af)
|
|||||||
audio_valid_channel_count(af->channels);
|
audio_valid_channel_count(af->channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns false if the format mask is not valid for playback with
|
||||||
|
* MPD. This function performs some basic validity checks.
|
||||||
|
*/
|
||||||
|
static inline bool audio_format_mask_valid(const struct audio_format *af)
|
||||||
|
{
|
||||||
|
return (af->sample_rate == 0 ||
|
||||||
|
audio_valid_sample_rate(af->sample_rate)) &&
|
||||||
|
(af->bits == 0 || audio_valid_sample_format(af->bits)) &&
|
||||||
|
(af->channels == 0 || audio_valid_channel_count(af->channels));
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool audio_format_equals(const struct audio_format *a,
|
static inline bool audio_format_equals(const struct audio_format *a,
|
||||||
const struct audio_format *b)
|
const struct audio_format *b)
|
||||||
{
|
{
|
||||||
@ -141,6 +174,20 @@ static inline bool audio_format_equals(const struct audio_format *a,
|
|||||||
a->reverse_endian == b->reverse_endian;
|
a->reverse_endian == b->reverse_endian;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
audio_format_mask_apply(struct audio_format *af,
|
||||||
|
const struct audio_format *mask)
|
||||||
|
{
|
||||||
|
if (mask->sample_rate != 0)
|
||||||
|
af->sample_rate = mask->sample_rate;
|
||||||
|
|
||||||
|
if (mask->bits != 0)
|
||||||
|
af->bits = mask->bits;
|
||||||
|
|
||||||
|
if (mask->channels != 0)
|
||||||
|
af->channels = mask->channels;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the size of each (mono) sample in bytes.
|
* Returns the size of each (mono) sample in bytes.
|
||||||
*/
|
*/
|
||||||
|
@ -37,12 +37,18 @@ audio_parser_quark(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
parse_sample_rate(const char *src, uint32_t *sample_rate_r,
|
parse_sample_rate(const char *src, bool mask, uint32_t *sample_rate_r,
|
||||||
const char **endptr_r, GError **error_r)
|
const char **endptr_r, GError **error_r)
|
||||||
{
|
{
|
||||||
unsigned long value;
|
unsigned long value;
|
||||||
char *endptr;
|
char *endptr;
|
||||||
|
|
||||||
|
if (mask && *src == '*') {
|
||||||
|
*sample_rate_r = 0;
|
||||||
|
*endptr_r = src + 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
value = strtoul(src, &endptr, 10);
|
value = strtoul(src, &endptr, 10);
|
||||||
if (endptr == src) {
|
if (endptr == src) {
|
||||||
g_set_error(error_r, audio_parser_quark(), 0,
|
g_set_error(error_r, audio_parser_quark(), 0,
|
||||||
@ -60,12 +66,18 @@ parse_sample_rate(const char *src, uint32_t *sample_rate_r,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
parse_sample_format(const char *src, uint8_t *bits_r,
|
parse_sample_format(const char *src, bool mask, uint8_t *bits_r,
|
||||||
const char **endptr_r, GError **error_r)
|
const char **endptr_r, GError **error_r)
|
||||||
{
|
{
|
||||||
unsigned long value;
|
unsigned long value;
|
||||||
char *endptr;
|
char *endptr;
|
||||||
|
|
||||||
|
if (mask && *src == '*') {
|
||||||
|
*bits_r = 0;
|
||||||
|
*endptr_r = src + 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
value = strtoul(src, &endptr, 10);
|
value = strtoul(src, &endptr, 10);
|
||||||
if (endptr == src) {
|
if (endptr == src) {
|
||||||
g_set_error(error_r, audio_parser_quark(), 0,
|
g_set_error(error_r, audio_parser_quark(), 0,
|
||||||
@ -83,12 +95,18 @@ parse_sample_format(const char *src, uint8_t *bits_r,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
parse_channel_count(const char *src, uint8_t *channels_r,
|
parse_channel_count(const char *src, bool mask, uint8_t *channels_r,
|
||||||
const char **endptr_r, GError **error_r)
|
const char **endptr_r, GError **error_r)
|
||||||
{
|
{
|
||||||
unsigned long value;
|
unsigned long value;
|
||||||
char *endptr;
|
char *endptr;
|
||||||
|
|
||||||
|
if (mask && *src == '*') {
|
||||||
|
*channels_r = 0;
|
||||||
|
*endptr_r = src + 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
value = strtoul(src, &endptr, 10);
|
value = strtoul(src, &endptr, 10);
|
||||||
if (endptr == src) {
|
if (endptr == src) {
|
||||||
g_set_error(error_r, audio_parser_quark(), 0,
|
g_set_error(error_r, audio_parser_quark(), 0,
|
||||||
@ -107,7 +125,7 @@ parse_channel_count(const char *src, uint8_t *channels_r,
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
audio_format_parse(struct audio_format *dest, const char *src,
|
audio_format_parse(struct audio_format *dest, const char *src,
|
||||||
GError **error_r)
|
bool mask, GError **error_r)
|
||||||
{
|
{
|
||||||
uint32_t rate;
|
uint32_t rate;
|
||||||
uint8_t bits, channels;
|
uint8_t bits, channels;
|
||||||
@ -116,7 +134,7 @@ audio_format_parse(struct audio_format *dest, const char *src,
|
|||||||
|
|
||||||
/* parse sample rate */
|
/* parse sample rate */
|
||||||
|
|
||||||
if (!parse_sample_rate(src, &rate, &src, error_r))
|
if (!parse_sample_rate(src, mask, &rate, &src, error_r))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (*src++ != ':') {
|
if (*src++ != ':') {
|
||||||
@ -127,7 +145,7 @@ audio_format_parse(struct audio_format *dest, const char *src,
|
|||||||
|
|
||||||
/* parse sample format */
|
/* parse sample format */
|
||||||
|
|
||||||
if (!parse_sample_format(src, &bits, &src, error_r))
|
if (!parse_sample_format(src, mask, &bits, &src, error_r))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (*src++ != ':') {
|
if (*src++ != ':') {
|
||||||
@ -138,7 +156,7 @@ audio_format_parse(struct audio_format *dest, const char *src,
|
|||||||
|
|
||||||
/* parse channel count */
|
/* parse channel count */
|
||||||
|
|
||||||
if (!parse_channel_count(src, &channels, &src, error_r))
|
if (!parse_channel_count(src, mask, &channels, &src, error_r))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (*src != 0) {
|
if (*src != 0) {
|
||||||
|
@ -37,12 +37,13 @@ struct audio_format;
|
|||||||
*
|
*
|
||||||
* @param dest the destination #audio_format struct
|
* @param dest the destination #audio_format struct
|
||||||
* @param src the input string
|
* @param src the input string
|
||||||
|
* @param mask if true, then "*" is allowed for any number of items
|
||||||
* @param error_r location to store the error occuring, or NULL to
|
* @param error_r location to store the error occuring, or NULL to
|
||||||
* ignore errors
|
* ignore errors
|
||||||
* @return true on success
|
* @return true on success
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
audio_format_parse(struct audio_format *dest, const char *src,
|
audio_format_parse(struct audio_format *dest, const char *src,
|
||||||
GError **error_r);
|
bool mask, GError **error_r);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -126,6 +126,13 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
|||||||
struct block_param *block_param;
|
struct block_param *block_param;
|
||||||
int public;
|
int public;
|
||||||
|
|
||||||
|
if (audio_format == NULL ||
|
||||||
|
!audio_format_fully_defined(audio_format)) {
|
||||||
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
|
"Need full audio format specification");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
sd = new_shout_data();
|
sd = new_shout_data();
|
||||||
|
|
||||||
if (shout_init_count == 0)
|
if (shout_init_count == 0)
|
||||||
@ -191,8 +198,6 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
check_block_param("format");
|
|
||||||
|
|
||||||
encoding = config_get_block_string(param, "encoding", "ogg");
|
encoding = config_get_block_string(param, "encoding", "ogg");
|
||||||
encoder_plugin = shout_encoder_plugin_get(encoding);
|
encoder_plugin = shout_encoder_plugin_get(encoding);
|
||||||
if (encoder_plugin == NULL) {
|
if (encoder_plugin == NULL) {
|
||||||
|
@ -157,7 +157,7 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
|
|||||||
if (p != NULL) {
|
if (p != NULL) {
|
||||||
bool success =
|
bool success =
|
||||||
audio_format_parse(&ao->config_audio_format,
|
audio_format_parse(&ao->config_audio_format,
|
||||||
p, error_r);
|
p, true, error_r);
|
||||||
if (!success)
|
if (!success)
|
||||||
return false;
|
return false;
|
||||||
} else
|
} else
|
||||||
@ -195,8 +195,7 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
|
|||||||
ao->mutex = g_mutex_new();
|
ao->mutex = g_mutex_new();
|
||||||
|
|
||||||
ao->data = ao_plugin_init(plugin,
|
ao->data = ao_plugin_init(plugin,
|
||||||
audio_format_defined(&ao->config_audio_format)
|
&ao->config_audio_format,
|
||||||
? &ao->config_audio_format : NULL,
|
|
||||||
param, error_r);
|
param, error_r);
|
||||||
if (ao->data == NULL)
|
if (ao->data == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
@ -67,10 +67,9 @@ ao_open(struct audio_output *ao)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio_format_defined(&ao->config_audio_format))
|
|
||||||
ao->out_audio_format = ao->config_audio_format;
|
|
||||||
else
|
|
||||||
ao->out_audio_format = *filter_audio_format;
|
ao->out_audio_format = *filter_audio_format;
|
||||||
|
audio_format_mask_apply(&ao->out_audio_format,
|
||||||
|
&ao->config_audio_format);
|
||||||
|
|
||||||
success = ao_plugin_open(ao->plugin, ao->data,
|
success = ao_plugin_open(ao->plugin, ao->data,
|
||||||
&ao->out_audio_format,
|
&ao->out_audio_format,
|
||||||
@ -166,7 +165,7 @@ ao_reopen_filter(struct audio_output *ao)
|
|||||||
static void
|
static void
|
||||||
ao_reopen(struct audio_output *ao)
|
ao_reopen(struct audio_output *ao)
|
||||||
{
|
{
|
||||||
if (!audio_format_defined(&ao->config_audio_format)) {
|
if (!audio_format_fully_defined(&ao->config_audio_format)) {
|
||||||
if (ao->open) {
|
if (ao->open) {
|
||||||
const struct music_pipe *mp = ao->pipe;
|
const struct music_pipe *mp = ao->pipe;
|
||||||
ao_close(ao);
|
ao_close(ao);
|
||||||
@ -177,6 +176,8 @@ ao_reopen(struct audio_output *ao)
|
|||||||
the output's open() method determine the effective
|
the output's open() method determine the effective
|
||||||
out_audio_format */
|
out_audio_format */
|
||||||
ao->out_audio_format = ao->in_audio_format;
|
ao->out_audio_format = ao->in_audio_format;
|
||||||
|
audio_format_mask_apply(&ao->out_audio_format,
|
||||||
|
&ao->config_audio_format);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ao->open)
|
if (ao->open)
|
||||||
|
@ -86,7 +86,8 @@ int main(int argc, char **argv)
|
|||||||
/* open the encoder */
|
/* open the encoder */
|
||||||
|
|
||||||
if (argc > 2) {
|
if (argc > 2) {
|
||||||
ret = audio_format_parse(&audio_format, argv[2], &error);
|
ret = audio_format_parse(&audio_format, argv[2],
|
||||||
|
false, &error);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
g_printerr("Failed to parse audio format: %s\n",
|
g_printerr("Failed to parse audio format: %s\n",
|
||||||
error->message);
|
error->message);
|
||||||
|
@ -100,7 +100,8 @@ int main(int argc, char **argv)
|
|||||||
/* parse the audio format */
|
/* parse the audio format */
|
||||||
|
|
||||||
if (argc > 3) {
|
if (argc > 3) {
|
||||||
success = audio_format_parse(&audio_format, argv[3], &error);
|
success = audio_format_parse(&audio_format, argv[3],
|
||||||
|
false, &error);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
g_printerr("Failed to parse audio format: %s\n",
|
g_printerr("Failed to parse audio format: %s\n",
|
||||||
error->message);
|
error->message);
|
||||||
|
@ -143,7 +143,8 @@ int main(int argc, char **argv)
|
|||||||
/* parse the audio format */
|
/* parse the audio format */
|
||||||
|
|
||||||
if (argc > 3) {
|
if (argc > 3) {
|
||||||
success = audio_format_parse(&audio_format, argv[3], &error);
|
success = audio_format_parse(&audio_format, argv[3],
|
||||||
|
false, &error);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
g_printerr("Failed to parse audio format: %s\n",
|
g_printerr("Failed to parse audio format: %s\n",
|
||||||
error->message);
|
error->message);
|
||||||
|
@ -46,7 +46,8 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
ret = audio_format_parse(&audio_format, argv[1], &error);
|
ret = audio_format_parse(&audio_format, argv[1],
|
||||||
|
false, &error);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
g_printerr("Failed to parse audio format: %s\n",
|
g_printerr("Failed to parse audio format: %s\n",
|
||||||
error->message);
|
error->message);
|
||||||
|
Loading…
Reference in New Issue
Block a user