diff options
Diffstat (limited to 'audio/out')
-rw-r--r-- | audio/out/ao_alsa.c | 77 |
1 files changed, 56 insertions, 21 deletions
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c index b0099011dc..900cbafd03 100644 --- a/audio/out/ao_alsa.c +++ b/audio/out/ao_alsa.c @@ -91,6 +91,8 @@ struct priv { snd_output_t *output; + struct ao_convert_fmt convert; + struct ao_alsa_opts *opts; }; @@ -246,28 +248,36 @@ alsa_error: return CONTROL_ERROR; } -static const int mp_to_alsa_format[][2] = { +struct alsa_fmt { + int mp_format; + int alsa_format; + int bits; // alsa format full sample size (optional) + int pad_msb; // how many MSB bits are 0 (optional) +}; + +// Entries that have the same mp_format must be: +// 1. consecutive +// 2. sorted by preferred format (worst comes last) +static const struct alsa_fmt mp_alsa_formats[] = { {AF_FORMAT_U8, SND_PCM_FORMAT_U8}, {AF_FORMAT_S16, SND_PCM_FORMAT_S16}, {AF_FORMAT_S32, SND_PCM_FORMAT_S32}, - {AF_FORMAT_S24, - MP_SELECT_LE_BE(SND_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_S24_3BE)}, + {AF_FORMAT_S32, SND_PCM_FORMAT_S24, .bits = 32, .pad_msb = 8}, + {AF_FORMAT_S32, + MP_SELECT_LE_BE(SND_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_S24_3BE), + .bits = 24, .pad_msb = 0}, {AF_FORMAT_FLOAT, SND_PCM_FORMAT_FLOAT}, {AF_FORMAT_DOUBLE, SND_PCM_FORMAT_FLOAT64}, - {AF_FORMAT_S_MP3, SND_PCM_FORMAT_MPEG}, - {AF_FORMAT_UNKNOWN, SND_PCM_FORMAT_UNKNOWN}, + {0}, }; -static int find_alsa_format(int af_format) +static const struct alsa_fmt *find_alsa_format(int mp_format) { - af_format = af_fmt_from_planar(af_format); - for (int n = 0; mp_to_alsa_format[n][0] != AF_FORMAT_UNKNOWN; n++) { - if (mp_to_alsa_format[n][0] == af_format) - return mp_to_alsa_format[n][1]; + for (int n = 0; mp_alsa_formats[n].mp_format; n++) { + if (mp_alsa_formats[n].mp_format == mp_format) + return &mp_alsa_formats[n]; } - if (af_fmt_is_spdif(af_format)) - return SND_PCM_FORMAT_S16; - return SND_PCM_FORMAT_UNKNOWN; + return NULL; } #if HAVE_CHMAP_API @@ -648,6 +658,8 @@ static int init_device(struct ao *ao, int mode) size_t tmp_s; int err; + p->alsa_fmt = SND_PCM_FORMAT_UNKNOWN; + err = snd_output_buffer_open(&p->output); CHECK_ALSA_ERROR("Unable to create output buffer"); @@ -699,15 +711,34 @@ static int init_device(struct ao *ao, int mode) bool found_format = false; int try_formats[AF_FORMAT_COUNT]; af_get_best_sample_formats(ao->format, try_formats); - for (int n = 0; try_formats[n]; n++) { - if (af_fmt_is_planar(ao->format) != af_fmt_is_planar(try_formats[n])) + for (int n = 0; try_formats[n] && !found_format; n++) { + int mp_format = try_formats[n]; + if (af_fmt_is_planar(ao->format) != af_fmt_is_planar(mp_format)) continue; // implied SND_PCM_ACCESS mismatches - p->alsa_fmt = find_alsa_format(try_formats[n]); - MP_VERBOSE(ao, "trying format %s\n", af_fmt_to_str(try_formats[n])); - if (snd_pcm_hw_params_test_format(p->alsa, alsa_hwparams, p->alsa_fmt) >= 0) { - ao->format = try_formats[n]; - found_format = true; - break; + int mp_pformat = af_fmt_from_planar(mp_format); + if (af_fmt_is_spdif(mp_pformat)) + mp_pformat = AF_FORMAT_S16; + const struct alsa_fmt *fmt = find_alsa_format(mp_pformat); + if (!fmt) + continue; + for (; fmt->mp_format == mp_pformat; fmt++) { + p->alsa_fmt = fmt->alsa_format; + p->convert = (struct ao_convert_fmt){ + .src_fmt = mp_format, + .dst_bits = fmt->bits ? fmt->bits : af_fmt_to_bytes(mp_format) * 8, + .pad_msb = fmt->pad_msb, + }; + if (!ao_can_convert_inplace(&p->convert)) + continue; + MP_VERBOSE(ao, "trying format %s/%d\n", af_fmt_to_str(mp_pformat), + p->alsa_fmt); + if (snd_pcm_hw_params_test_format(p->alsa, alsa_hwparams, + p->alsa_fmt) >= 0) + { + ao->format = mp_format; + found_format = true; + break; + } } } @@ -842,6 +873,8 @@ static int init_device(struct ao *ao, int mode) ao->device_buffer = p->buffersize; ao->period_size = p->outburst; + p->convert.channels = ao->channels.num; + return 0; alsa_error: @@ -1067,6 +1100,8 @@ static int play(struct ao *ao, void **data, int samples, int flags) return 0; do { + ao_convert_inplace(&p->convert, data, samples); + if (af_fmt_is_planar(ao->format)) { res = snd_pcm_writen(p->alsa, data, samples); } else { |