Changes in v2:
* DSD support
* Clean-up
At some point the comment was probably true, but recent
versions of pulseaudio have no trouble with any reasonable
selection of sample formats.There are some exceptions: * at present DSD is not (yet) supported in pulseaudio, so in the meantime DSD is supported by downsampling to a decent PCM spec * pulseaudio supports U8, but not S8, vis-versa for mpd * pulseaudio supports both packed and unpacked variants of S24 for both endian, but mpd apparently doesn't support non-packed 24bit samples, which is a little odd since the ALSA output plugin assumes it does and so seems to always fall back to packed for 24bit formats!?! (bug?) I originally started this patch from the multi-format support code in the ALSA plugin, and as such it still contains the logic for fall-back formats, but whereas the ALSA plugin attempts to open a stream, this isn't necessary in this case since there's a pulse API for testing the validity of a sample_spec. Signed-off-by: Steven Newbury <[email protected]> --- src/output/plugins/PulseOutputPlugin.cxx | 131 +++++++++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 6 deletions(-) diff --git a/src/output/plugins/PulseOutputPlugin.cxx b/src/output/plugins/PulseOutputPlugin.cxx index 0d79af7..23d2c8f 100644 --- a/src/output/plugins/PulseOutputPlugin.cxx +++ b/src/output/plugins/PulseOutputPlugin.cxx @@ -544,6 +544,94 @@ pulse_output_setup_stream(PulseOutput *po, const pa_sample_spec *ss, return true; } +static pa_sample_format_t +get_bitformat(SampleFormat sample_format) +{ + /* Pulse supports U8 and S24LE/BE formats but mpd + currently does not. Pulse does not support S8. */ + switch (sample_format) { + case SampleFormat::UNDEFINED: + case SampleFormat::DSD: + case SampleFormat::S8: + return PA_SAMPLE_INVALID; + + case SampleFormat::S16: + return PA_SAMPLE_S16NE; + + case SampleFormat::S24_P32: + return PA_SAMPLE_S24_32NE; + + case SampleFormat::S32: + return PA_SAMPLE_S32NE; + + case SampleFormat::FLOAT: + return PA_SAMPLE_FLOAT32NE; + } + + assert(false); + gcc_unreachable(); +} + +static pa_sample_format_t +byteswap_bitformat(pa_sample_format_t fmt) +{ + switch(fmt) { + case PA_SAMPLE_S16LE: return PA_SAMPLE_S16BE; + case PA_SAMPLE_S24LE: return PA_SAMPLE_S24BE; + case PA_SAMPLE_S32LE: return PA_SAMPLE_S32BE; + case PA_SAMPLE_S16BE: return PA_SAMPLE_S16LE; + case PA_SAMPLE_S24BE: return PA_SAMPLE_S24LE; + + case PA_SAMPLE_S24_32BE: + return PA_SAMPLE_S24_32LE; + + case PA_SAMPLE_S24_32LE: + return PA_SAMPLE_S24_32BE; + + case PA_SAMPLE_S32BE: return PA_SAMPLE_S32LE; + + case PA_SAMPLE_FLOAT32BE: + return PA_SAMPLE_FLOAT32LE; + + case PA_SAMPLE_FLOAT32LE: + return PA_SAMPLE_FLOAT32BE; + + default: return PA_SAMPLE_INVALID; + } +} + +/** + * Attempts to configure the specified sample format, and tries the + * reversed host byte order if was not supported. + */ +static pa_sample_spec +pulse_get_valid_spec(uint32_t sample_rate, SampleFormat sample_format, + uint8_t channels) +{ + pa_sample_spec test_spec; + int ret; + + test_spec.format = get_bitformat(sample_format); + test_spec.rate = sample_rate; + test_spec.channels = channels; + if (test_spec.format == PA_SAMPLE_INVALID) + return test_spec; + + ret = pa_sample_spec_valid(&test_spec); + if (ret != 0) + return test_spec; + + test_spec.format = byteswap_bitformat(test_spec.format); + if (test_spec.format == PA_SAMPLE_INVALID) + return test_spec; + + ret = pa_sample_spec_valid(&test_spec); + if (ret == 0) + test_spec.format = PA_SAMPLE_INVALID; + + return test_spec; +} + static bool pulse_output_open(AudioOutput *ao, AudioFormat &audio_format, Error &error) @@ -579,13 +667,44 @@ pulse_output_open(AudioOutput *ao, AudioFormat &audio_format, return false; } - /* MPD doesn't support the other pulseaudio sample formats, so - we just force MPD to send us everything as 16 bit */ - audio_format.format = SampleFormat::S16; + /* pulseaudio can validate a sample_spec before opening the stream */ + + ss = pulse_get_valid_spec(audio_format.sample_rate, + audio_format.format, audio_format.channels); + + /* if unsupported, try other formats */ - ss.format = PA_SAMPLE_S16NE; - ss.rate = audio_format.sample_rate; - ss.channels = audio_format.channels; + if (ss.format == PA_SAMPLE_INVALID) { + /* Pulse doesn't yet support DSD so attempt to resample + to high quality PCM - maybe higher would be better, + but that would then typically need to be resampled + twice! :-/ */ + if (audio_format.format == SampleFormat::DSD) + { + audio_format.format = SampleFormat::FLOAT; + audio_format.sample_rate = 48000; + } + + static const SampleFormat probe_formats[] = { + SampleFormat::S32, + SampleFormat::S24_P32, + SampleFormat::S16, + SampleFormat::UNDEFINED, + }; + + for (unsigned i = 0; + ss.format == PA_SAMPLE_INVALID && probe_formats[i] != SampleFormat::UNDEFINED; + ++i) { + const SampleFormat mpd_format = probe_formats[i]; + if (mpd_format == audio_format.format) + continue; + + ss = pulse_get_valid_spec(audio_format.sample_rate, + mpd_format, audio_format.channels); + if (ss.format != PA_SAMPLE_INVALID) + audio_format.format = mpd_format; + } + } /* create a stream .. */ -- 2.1.0
signature.asc
Description: This is a digitally signed message part
_______________________________________________ mpd-devel mailing list [email protected] http://mailman.blarg.de/listinfo/mpd-devel
