diff --git a/NEWS b/NEWS index 6349f16d3..f8b02baee 100644 --- a/NEWS +++ b/NEWS @@ -34,6 +34,7 @@ ver 0.16 (20??/??/??) - pulse: connect to server on MPD startup, implement pause - jack: don't disconnect during pause - jack: connect to server on MPD startup + - wildcards allowed in audio_format configuration * mixers: - removed support for legacy mixer configuration - reimplemented software volume as mixer+filter plugin diff --git a/doc/mpd.conf.5 b/doc/mpd.conf.5 index 0784ea393..6bbd7e784 100644 --- a/doc/mpd.conf.5 +++ b/doc/mpd.conf.5 @@ -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 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. +Any of the three attributes may be an asterisk to specify that this +attribute should not be enforced .TP .B samplerate_converter 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 \fBaudio_output_format\fP parameter for more details. The default is to use 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 .TP .B device diff --git a/doc/user.xml b/doc/user.xml index ca63eccd9..8111482d4 100644 --- a/doc/user.xml +++ b/doc/user.xml @@ -300,10 +300,19 @@ cd mpd-version format - Always open the audio output with the specified audio - format (samplerate:bits:channels), regardless of the - format of the input file. This is optional for most - plugins. + + Always open the audio output with the specified audio + format (samplerate:bits:channels), regardless of the + format of the input file. This is optional for most + plugins. + + + Any of the three attributes may be an asterisk to + specify that this attribute should not be enforced, + example: 48000:16:*. + *:*:* is equal to not having + a format specification. + diff --git a/src/audio.c b/src/audio.c index fb4712728..13f128eeb 100644 --- a/src/audio.c +++ b/src/audio.c @@ -35,9 +35,8 @@ static struct audio_format configured_audio_format; void getOutputAudioFormat(const struct audio_format *inAudioFormat, struct audio_format *outAudioFormat) { - *outAudioFormat = audio_format_defined(&configured_audio_format) - ? configured_audio_format - : *inAudioFormat; + *outAudioFormat = *inAudioFormat; + audio_format_mask_apply(outAudioFormat, &configured_audio_format); } void initAudioConfig(void) @@ -50,7 +49,7 @@ void initAudioConfig(void) return; ret = audio_format_parse(&configured_audio_format, param->value, - &error); + true, &error); if (!ret) g_error("error parsing \"%s\" at line %i: %s", CONF_AUDIO_OUTPUT_FORMAT, param->line, error->message); diff --git a/src/audio_format.h b/src/audio_format.h index a88fc3a4c..a4f5ba2e0 100644 --- a/src/audio_format.h +++ b/src/audio_format.h @@ -90,6 +90,27 @@ static inline bool audio_format_defined(const struct audio_format *af) 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. * @@ -132,6 +153,18 @@ static inline bool audio_format_valid(const struct audio_format *af) 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, 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; } +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. */ diff --git a/src/audio_parser.c b/src/audio_parser.c index 0b3474ab3..7c0d45ddc 100644 --- a/src/audio_parser.c +++ b/src/audio_parser.c @@ -37,12 +37,18 @@ audio_parser_quark(void) } 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) { unsigned long value; char *endptr; + if (mask && *src == '*') { + *sample_rate_r = 0; + *endptr_r = src + 1; + return true; + } + value = strtoul(src, &endptr, 10); if (endptr == src) { 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 -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) { unsigned long value; char *endptr; + if (mask && *src == '*') { + *bits_r = 0; + *endptr_r = src + 1; + return true; + } + value = strtoul(src, &endptr, 10); if (endptr == src) { 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 -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) { unsigned long value; char *endptr; + if (mask && *src == '*') { + *channels_r = 0; + *endptr_r = src + 1; + return true; + } + value = strtoul(src, &endptr, 10); if (endptr == src) { g_set_error(error_r, audio_parser_quark(), 0, @@ -107,7 +125,7 @@ parse_channel_count(const char *src, uint8_t *channels_r, bool audio_format_parse(struct audio_format *dest, const char *src, - GError **error_r) + bool mask, GError **error_r) { uint32_t rate; uint8_t bits, channels; @@ -116,7 +134,7 @@ audio_format_parse(struct audio_format *dest, const char *src, /* parse sample rate */ - if (!parse_sample_rate(src, &rate, &src, error_r)) + if (!parse_sample_rate(src, mask, &rate, &src, error_r)) return false; if (*src++ != ':') { @@ -127,7 +145,7 @@ audio_format_parse(struct audio_format *dest, const char *src, /* parse sample format */ - if (!parse_sample_format(src, &bits, &src, error_r)) + if (!parse_sample_format(src, mask, &bits, &src, error_r)) return false; if (*src++ != ':') { @@ -138,7 +156,7 @@ audio_format_parse(struct audio_format *dest, const char *src, /* parse channel count */ - if (!parse_channel_count(src, &channels, &src, error_r)) + if (!parse_channel_count(src, mask, &channels, &src, error_r)) return false; if (*src != 0) { diff --git a/src/audio_parser.h b/src/audio_parser.h index 1d821eaf5..d50c17489 100644 --- a/src/audio_parser.h +++ b/src/audio_parser.h @@ -37,12 +37,13 @@ struct audio_format; * * @param dest the destination #audio_format struct * @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 * ignore errors * @return true on success */ bool audio_format_parse(struct audio_format *dest, const char *src, - GError **error_r); + bool mask, GError **error_r); #endif diff --git a/src/output/shout_plugin.c b/src/output/shout_plugin.c index f1b21bb36..da90efd2d 100644 --- a/src/output/shout_plugin.c +++ b/src/output/shout_plugin.c @@ -126,6 +126,13 @@ my_shout_init_driver(const struct audio_format *audio_format, struct block_param *block_param; 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(); 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"); encoder_plugin = shout_encoder_plugin_get(encoding); if (encoder_plugin == NULL) { diff --git a/src/output_init.c b/src/output_init.c index f097f2c2d..745b63e30 100644 --- a/src/output_init.c +++ b/src/output_init.c @@ -157,7 +157,7 @@ audio_output_init(struct audio_output *ao, const struct config_param *param, if (p != NULL) { bool success = audio_format_parse(&ao->config_audio_format, - p, error_r); + p, true, error_r); if (!success) return false; } else @@ -195,8 +195,7 @@ audio_output_init(struct audio_output *ao, const struct config_param *param, ao->mutex = g_mutex_new(); ao->data = ao_plugin_init(plugin, - audio_format_defined(&ao->config_audio_format) - ? &ao->config_audio_format : NULL, + &ao->config_audio_format, param, error_r); if (ao->data == NULL) return false; diff --git a/src/output_thread.c b/src/output_thread.c index dd97d88b0..9eb2478b0 100644 --- a/src/output_thread.c +++ b/src/output_thread.c @@ -67,10 +67,9 @@ ao_open(struct audio_output *ao) 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, &ao->out_audio_format, @@ -166,7 +165,7 @@ ao_reopen_filter(struct audio_output *ao) static void 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) { const struct music_pipe *mp = ao->pipe; ao_close(ao); @@ -177,6 +176,8 @@ ao_reopen(struct audio_output *ao) the output's open() method determine the effective out_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) diff --git a/test/run_encoder.c b/test/run_encoder.c index 787fb03cf..b953bcd9a 100644 --- a/test/run_encoder.c +++ b/test/run_encoder.c @@ -86,7 +86,8 @@ int main(int argc, char **argv) /* open the encoder */ if (argc > 2) { - ret = audio_format_parse(&audio_format, argv[2], &error); + ret = audio_format_parse(&audio_format, argv[2], + false, &error); if (!ret) { g_printerr("Failed to parse audio format: %s\n", error->message); diff --git a/test/run_filter.c b/test/run_filter.c index ed2b904cf..3c4b76125 100644 --- a/test/run_filter.c +++ b/test/run_filter.c @@ -100,7 +100,8 @@ int main(int argc, char **argv) /* parse the audio format */ if (argc > 3) { - success = audio_format_parse(&audio_format, argv[3], &error); + success = audio_format_parse(&audio_format, argv[3], + false, &error); if (!success) { g_printerr("Failed to parse audio format: %s\n", error->message); diff --git a/test/run_output.c b/test/run_output.c index 3731b6c09..238e16e5a 100644 --- a/test/run_output.c +++ b/test/run_output.c @@ -143,7 +143,8 @@ int main(int argc, char **argv) /* parse the audio format */ if (argc > 3) { - success = audio_format_parse(&audio_format, argv[3], &error); + success = audio_format_parse(&audio_format, argv[3], + false, &error); if (!success) { g_printerr("Failed to parse audio format: %s\n", error->message); diff --git a/test/software_volume.c b/test/software_volume.c index 9e8c8e7d0..5d551b1f3 100644 --- a/test/software_volume.c +++ b/test/software_volume.c @@ -46,7 +46,8 @@ int main(int argc, char **argv) } if (argc > 1) { - ret = audio_format_parse(&audio_format, argv[1], &error); + ret = audio_format_parse(&audio_format, argv[1], + false, &error); if (!ret) { g_printerr("Failed to parse audio format: %s\n", error->message);