output/alsa: add option to enable DSD over USB

This commit is contained in:
Max Kellermann 2012-03-21 10:36:19 +01:00
parent 81208d78ac
commit 167242fec0
3 changed files with 72 additions and 1 deletions

1
NEWS
View File

@ -17,6 +17,7 @@ ver 0.17 (2011/??/??)
- oggflac: delete this obsolete plugin - oggflac: delete this obsolete plugin
- dsdiff: new decoder plugin - dsdiff: new decoder plugin
* output: * output:
- alsa: support DSD-over-USB (dCS suggested standard)
- httpd: support for streaming to a DLNA client - httpd: support for streaming to a DLNA client
- openal: improve buffer cancellation - openal: improve buffer cancellation
- osx: allow user to specify other audio devices - osx: allow user to specify other audio devices

View File

@ -1119,6 +1119,23 @@ systemctl start mpd.socket</programlisting>
bit, floating point, ...). bit, floating point, ...).
</entry> </entry>
</row> </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> </tbody>
</tgroup> </tgroup>
</informaltable> </informaltable>

View File

@ -55,6 +55,14 @@ struct alsa_data {
/** use memory mapped I/O? */ /** use memory mapped I/O? */
bool use_mmap; 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) */ /** libasound's buffer_time setting (in microseconds) */
unsigned int buffer_time; 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->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", ad->buffer_time = config_get_block_unsigned(param, "buffer_time",
MPD_ALSA_BUFFER_TIME_US); MPD_ALSA_BUFFER_TIME_US);
ad->period_time = config_get_block_unsigned(param, "period_time", 0); ad->period_time = config_get_block_unsigned(param, "period_time", 0);
@ -575,6 +585,49 @@ error:
return false; 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 static bool
alsa_open(struct audio_output *ao, struct audio_format *audio_format, GError **error) 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; return false;
} }
success = alsa_setup(ad, audio_format, error); success = alsa_setup_or_dsd(ad, audio_format, error);
if (!success) { if (!success) {
snd_pcm_close(ad->pcm); snd_pcm_close(ad->pcm);
return false; return false;