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 <st...@snewbury.org.uk>
---
 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

Attachment: signature.asc
Description: This is a digitally signed message part

_______________________________________________
mpd-devel mailing list
mpd-devel@musicpd.org
http://mailman.blarg.de/listinfo/mpd-devel

Reply via email to