output/alsa: add option to enable DSD over USB
This commit is contained in:
		
							
								
								
									
										1
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								NEWS
									
									
									
									
									
								
							| @@ -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 | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								doc/user.xml
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								doc/user.xml
									
									
									
									
									
								
							| @@ -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> | ||||||
|   | |||||||
| @@ -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; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann