diff options
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | doc/user.xml | 17 | ||||
-rw-r--r-- | src/output/alsa_output_plugin.c | 55 |
3 files changed, 72 insertions, 1 deletions
@@ -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</programlisting> bit, floating point, ...). </entry> </row> + <row> + <entry> + <varname>dsd_usb</varname> + <parameter>yes|no</parameter> + </entry> + <entry> + If set to <parameter>yes</parameter>, then DSD over + USB according to the <ulink + url="http://www.dcsltd.co.uk/page/assets/DSDoverUSB.pdf">dCS + suggested standard</ulink> 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 + <parameter>no</parameter> and you can enable the + option at your own risk. + </entry> + </row> </tbody> </tgroup> </informaltable> 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); @@ -576,6 +586,49 @@ error: } 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) { struct alsa_data *ad = (struct alsa_data *)ao; @@ -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; |