diff options
Diffstat (limited to 'src/output/alsa_plugin.c')
-rw-r--r-- | src/output/alsa_plugin.c | 152 |
1 files changed, 80 insertions, 72 deletions
diff --git a/src/output/alsa_plugin.c b/src/output/alsa_plugin.c index cc6986d29..ff591230c 100644 --- a/src/output/alsa_plugin.c +++ b/src/output/alsa_plugin.c @@ -216,89 +216,54 @@ byteswap_bitformat(snd_pcm_format_t fmt) default: return SND_PCM_FORMAT_UNKNOWN; } } + /** - * Set up the snd_pcm_t object which was opened by the caller. Set up - * the configured settings and the audio format. + * Configure a sample format, and probe other formats if that fails. */ -static bool -alsa_setup(struct alsa_data *ad, struct audio_format *audio_format, - snd_pcm_format_t bitformat, - GError **error) +static int +alsa_output_setup_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams, + struct audio_format *audio_format) { - snd_pcm_hw_params_t *hwparams; - snd_pcm_sw_params_t *swparams; - unsigned int sample_rate = audio_format->sample_rate; - unsigned int channels = audio_format->channels; - snd_pcm_uframes_t alsa_buffer_size; - snd_pcm_uframes_t alsa_period_size; - int err; - const char *cmd = NULL; - int retry = MPD_ALSA_RETRY_NR; - unsigned int period_time, period_time_ro; - unsigned int buffer_time; - - period_time_ro = period_time = ad->period_time; -configure_hw: - /* configure HW params */ - snd_pcm_hw_params_alloca(&hwparams); - cmd = "snd_pcm_hw_params_any"; - err = snd_pcm_hw_params_any(ad->pcm, hwparams); - if (err < 0) - goto error; - - if (ad->use_mmap) { - err = snd_pcm_hw_params_set_access(ad->pcm, hwparams, - SND_PCM_ACCESS_MMAP_INTERLEAVED); - if (err < 0) { - g_warning("Cannot set mmap'ed mode on ALSA device \"%s\": %s\n", - alsa_device(ad), snd_strerror(-err)); - g_warning("Falling back to direct write mode\n"); - ad->use_mmap = false; - } else - ad->writei = snd_pcm_mmap_writei; - } + snd_pcm_format_t bitformat = get_bitformat(audio_format); + if (bitformat == SND_PCM_FORMAT_UNKNOWN) { + /* sample format is not supported by this plugin - + fall back to 16 bit samples */ - if (!ad->use_mmap) { - cmd = "snd_pcm_hw_params_set_access"; - err = snd_pcm_hw_params_set_access(ad->pcm, hwparams, - SND_PCM_ACCESS_RW_INTERLEAVED); - if (err < 0) - goto error; - ad->writei = snd_pcm_writei; + audio_format->format = SAMPLE_FORMAT_S16; + bitformat = SND_PCM_FORMAT_S16; } - err = snd_pcm_hw_params_set_format(ad->pcm, hwparams, bitformat); + int err = snd_pcm_hw_params_set_format(pcm, hwparams, bitformat); if (err == -EINVAL && byteswap_bitformat(bitformat) != SND_PCM_FORMAT_UNKNOWN) { - err = snd_pcm_hw_params_set_format(ad->pcm, hwparams, + err = snd_pcm_hw_params_set_format(pcm, hwparams, byteswap_bitformat(bitformat)); if (err == 0) { - g_debug("ALSA device \"%s\": converting format %s to reverse-endian", - alsa_device(ad), + g_debug("converting format %s to reverse-endian", sample_format_to_string(audio_format->format)); audio_format->reverse_endian = 1; } } + if (err == -EINVAL && (audio_format->format == SAMPLE_FORMAT_S24_P32 || audio_format->format == SAMPLE_FORMAT_S16)) { /* fall back to 32 bit, let pcm_convert.c do the conversion */ - err = snd_pcm_hw_params_set_format(ad->pcm, hwparams, + err = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S32); if (err == 0) { - g_debug("ALSA device \"%s\": converting format %s to 32 bit\n", - alsa_device(ad), + g_debug("converting format %s to 32 bit\n", sample_format_to_string(audio_format->format)); audio_format->format = SAMPLE_FORMAT_S32; } } + if (err == -EINVAL && (audio_format->format == SAMPLE_FORMAT_S24_P32 || audio_format->format == SAMPLE_FORMAT_S16)) { /* fall back to 32 bit, let pcm_convert.c do the conversion */ - err = snd_pcm_hw_params_set_format(ad->pcm, hwparams, + err = snd_pcm_hw_params_set_format(pcm, hwparams, byteswap_bitformat(SND_PCM_FORMAT_S32)); if (err == 0) { - g_debug("ALSA device \"%s\": converting format %s to 32 bit backward-endian\n", - alsa_device(ad), + g_debug("converting format %s to 32 bit backward-endian\n", sample_format_to_string(audio_format->format)); audio_format->format = SAMPLE_FORMAT_S32; audio_format->reverse_endian = 1; @@ -307,28 +272,81 @@ configure_hw: if (err == -EINVAL && audio_format->format != SAMPLE_FORMAT_S16) { /* fall back to 16 bit, let pcm_convert.c do the conversion */ - err = snd_pcm_hw_params_set_format(ad->pcm, hwparams, + err = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S16); if (err == 0) { - g_debug("ALSA device \"%s\": converting format %s to 16 bit\n", - alsa_device(ad), + g_debug("converting format %s to 16 bit\n", sample_format_to_string(audio_format->format)); audio_format->format = SAMPLE_FORMAT_S16; } } + if (err == -EINVAL && audio_format->format != SAMPLE_FORMAT_S16) { /* fall back to 16 bit, let pcm_convert.c do the conversion */ - err = snd_pcm_hw_params_set_format(ad->pcm, hwparams, + err = snd_pcm_hw_params_set_format(pcm, hwparams, byteswap_bitformat(SND_PCM_FORMAT_S16)); if (err == 0) { - g_debug("ALSA device \"%s\": converting format %s to 16 bit backward-endian\n", - alsa_device(ad), + g_debug("converting format %s to 16 bit backward-endian\n", sample_format_to_string(audio_format->format)); audio_format->format = SAMPLE_FORMAT_S16; audio_format->reverse_endian = 1; } } + return err; +} + +/** + * Set up the snd_pcm_t object which was opened by the caller. Set up + * the configured settings and the audio format. + */ +static bool +alsa_setup(struct alsa_data *ad, struct audio_format *audio_format, + GError **error) +{ + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + unsigned int sample_rate = audio_format->sample_rate; + unsigned int channels = audio_format->channels; + snd_pcm_uframes_t alsa_buffer_size; + snd_pcm_uframes_t alsa_period_size; + int err; + const char *cmd = NULL; + int retry = MPD_ALSA_RETRY_NR; + unsigned int period_time, period_time_ro; + unsigned int buffer_time; + + period_time_ro = period_time = ad->period_time; +configure_hw: + /* configure HW params */ + snd_pcm_hw_params_alloca(&hwparams); + cmd = "snd_pcm_hw_params_any"; + err = snd_pcm_hw_params_any(ad->pcm, hwparams); + if (err < 0) + goto error; + + if (ad->use_mmap) { + err = snd_pcm_hw_params_set_access(ad->pcm, hwparams, + SND_PCM_ACCESS_MMAP_INTERLEAVED); + if (err < 0) { + g_warning("Cannot set mmap'ed mode on ALSA device \"%s\": %s\n", + alsa_device(ad), snd_strerror(-err)); + g_warning("Falling back to direct write mode\n"); + ad->use_mmap = false; + } else + ad->writei = snd_pcm_mmap_writei; + } + + if (!ad->use_mmap) { + cmd = "snd_pcm_hw_params_set_access"; + err = snd_pcm_hw_params_set_access(ad->pcm, hwparams, + SND_PCM_ACCESS_RW_INTERLEAVED); + if (err < 0) + goto error; + ad->writei = snd_pcm_writei; + } + + err = alsa_output_setup_format(ad->pcm, hwparams, audio_format); if (err < 0) { g_set_error(error, alsa_output_quark(), err, "ALSA device \"%s\" does not support format %s: %s", @@ -455,19 +473,9 @@ static bool alsa_open(void *data, struct audio_format *audio_format, GError **error) { struct alsa_data *ad = data; - snd_pcm_format_t bitformat; int err; bool success; - bitformat = get_bitformat(audio_format); - if (bitformat == SND_PCM_FORMAT_UNKNOWN) { - /* sample format is not supported by this plugin - - fall back to 16 bit samples */ - - audio_format->format = SAMPLE_FORMAT_S16; - bitformat = SND_PCM_FORMAT_S16; - } - err = snd_pcm_open(&ad->pcm, alsa_device(ad), SND_PCM_STREAM_PLAYBACK, ad->mode); if (err < 0) { @@ -477,7 +485,7 @@ alsa_open(void *data, struct audio_format *audio_format, GError **error) return false; } - success = alsa_setup(ad, audio_format, bitformat, error); + success = alsa_setup(ad, audio_format, error); if (!success) { snd_pcm_close(ad->pcm); return false; |