From 167242fec045a104e91ca142fb3bd38af8cb78bb Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 21 Mar 2012 10:36:19 +0100 Subject: [PATCH] output/alsa: add option to enable DSD over USB --- NEWS | 1 + doc/user.xml | 17 ++++++++++ src/output/alsa_output_plugin.c | 55 ++++++++++++++++++++++++++++++++- 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 9343f3f77..d576787df 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,7 @@ ver 0.17 (2011/??/??) - oggflac: delete this obsolete plugin - dsdiff: new decoder plugin * output: + - alsa: support DSD-over-USB (dCS suggested standard) - httpd: support for streaming to a DLNA client - openal: improve buffer cancellation - osx: allow user to specify other audio devices diff --git a/doc/user.xml b/doc/user.xml index 427e561c5..6c0547f0c 100644 --- a/doc/user.xml +++ b/doc/user.xml @@ -1119,6 +1119,23 @@ systemctl start mpd.socket bit, floating point, ...). + + + dsd_usb + yes|no + + + If set to yes, then DSD over + USB according to the dCS + suggested standard is enabled. This wrapsa + DSD samples in fake 24 bit PCM, and is understood by + some DSD capable products, but may be harmful to + other hardware. Therefore, the default is + no and you can enable the + option at your own risk. + + diff --git a/src/output/alsa_output_plugin.c b/src/output/alsa_output_plugin.c index 49b11c9b2..d0b0c4a5e 100644 --- a/src/output/alsa_output_plugin.c +++ b/src/output/alsa_output_plugin.c @@ -55,6 +55,14 @@ struct alsa_data { /** use memory mapped I/O? */ bool use_mmap; + /** + * Enable DSD over USB according to the dCS suggested + * standard? + * + * @see http://www.dcsltd.co.uk/page/assets/DSDoverUSB.pdf + */ + bool dsd_usb; + /** libasound's buffer_time setting (in microseconds) */ unsigned int buffer_time; @@ -128,6 +136,8 @@ alsa_configure(struct alsa_data *ad, const struct config_param *param) ad->use_mmap = config_get_block_bool(param, "use_mmap", false); + ad->dsd_usb = config_get_block_bool(param, "dsd_usb", false); + ad->buffer_time = config_get_block_unsigned(param, "buffer_time", MPD_ALSA_BUFFER_TIME_US); ad->period_time = config_get_block_unsigned(param, "period_time", 0); @@ -575,6 +585,49 @@ error: return false; } +static bool +alsa_setup_dsd(struct alsa_data *ad, struct audio_format *audio_format, + GError **error_r) +{ + assert(ad->dsd_usb); + assert(audio_format->format == SAMPLE_FORMAT_DSD); + + /* pass 24 bit to alsa_setup() */ + + audio_format->format = SAMPLE_FORMAT_S24_P32; + audio_format->sample_rate /= 2; + + const struct audio_format check = *audio_format; + + if (!alsa_setup(ad, audio_format, error_r)) + return false; + + if (!audio_format_equals(audio_format, &check)) { + /* no bit-perfect playback, which is required + for DSD over USB */ + g_set_error(error_r, alsa_output_quark(), 0, + "Failed to configure DSD-over-USB on ALSA device \"%s\"", + alsa_device(ad)); + return false; + } + + /* the ALSA device has accepted 24 bit playback, + return DSD_OVER_USB to the caller */ + + audio_format->format = SAMPLE_FORMAT_DSD_OVER_USB; + return true; +} + +static bool +alsa_setup_or_dsd(struct alsa_data *ad, struct audio_format *audio_format, + GError **error_r) +{ + if (ad->dsd_usb && audio_format->format == SAMPLE_FORMAT_DSD) + return alsa_setup_dsd(ad, audio_format, error_r); + + return alsa_setup(ad, audio_format, error_r); +} + static bool alsa_open(struct audio_output *ao, struct audio_format *audio_format, GError **error) { @@ -591,7 +644,7 @@ alsa_open(struct audio_output *ao, struct audio_format *audio_format, GError **e return false; } - success = alsa_setup(ad, audio_format, error); + success = alsa_setup_or_dsd(ad, audio_format, error); if (!success) { snd_pcm_close(ad->pcm); return false;