output/alsa: don't generate silence if ALSA-PCM buffer has enough data
If our `ring_buffer` is smaller than the ALSA-PCM buffer (if the
latter has more than the 4 periods we allocate), it can happen that
the start threshold is crossed and ALSA switches to
`SND_PCM_STATE_RUNNING`, but the `ring_buffer` is empty.  In this
case, MPDD will generate silence, even though the ALSA-PCM buffer has
enough data.  This causes stuttering (#420).
This commit amends an older workaround for a similar problem (commit
e08598e7e2) by adding a snd_pcm_avail()
check, and only generate silence if there is less than one period of
data in the ALSA-PCM buffer.
Fixes #420
			
			
This commit is contained in:
		
							
								
								
									
										1
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								NEWS
									
									
									
									
									
								
							| @@ -1,6 +1,7 @@ | |||||||
| ver 0.21.3 (not yet released) | ver 0.21.3 (not yet released) | ||||||
| * output | * output | ||||||
|   - alsa: fix crash bug |   - alsa: fix crash bug | ||||||
|  |   - alsa: fix stuttering at start of playback | ||||||
|  |  | ||||||
| ver 0.21.2 (2018/11/12) | ver 0.21.2 (2018/11/12) | ||||||
| * protocol | * protocol | ||||||
|   | |||||||
| @@ -109,6 +109,13 @@ class AlsaOutput final | |||||||
| 	 */ | 	 */ | ||||||
| 	snd_pcm_uframes_t period_frames; | 	snd_pcm_uframes_t period_frames; | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * If snd_pcm_avail() goes above this value and no more data | ||||||
|  | 	 * is available in the #ring_buffer, we need to play some | ||||||
|  | 	 * silence. | ||||||
|  | 	 */ | ||||||
|  | 	snd_pcm_sframes_t max_avail_frames; | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Is this a buggy alsa-lib version, which needs a workaround | 	 * Is this a buggy alsa-lib version, which needs a workaround | ||||||
| 	 * for the snd_pcm_drain() bug always returning -EAGAIN?  See | 	 * for the snd_pcm_drain() bug always returning -EAGAIN?  See | ||||||
| @@ -482,6 +489,10 @@ AlsaOutput::Setup(AudioFormat &audio_format, | |||||||
|  |  | ||||||
| 	period_frames = alsa_period_size; | 	period_frames = alsa_period_size; | ||||||
|  |  | ||||||
|  | 	/* generate silence if there's less than once period of data | ||||||
|  | 	   in the ALSA-PCM buffer */ | ||||||
|  | 	max_avail_frames = hw_result.buffer_size - hw_result.period_size; | ||||||
|  |  | ||||||
| 	silence = new uint8_t[snd_pcm_frames_to_bytes(pcm, alsa_period_size)]; | 	silence = new uint8_t[snd_pcm_frames_to_bytes(pcm, alsa_period_size)]; | ||||||
| 	snd_pcm_format_set_silence(hw_result.format, silence, | 	snd_pcm_format_set_silence(hw_result.format, silence, | ||||||
| 				   alsa_period_size * audio_format.channels); | 				   alsa_period_size * audio_format.channels); | ||||||
| @@ -933,7 +944,8 @@ try { | |||||||
| 	CopyRingToPeriodBuffer(); | 	CopyRingToPeriodBuffer(); | ||||||
|  |  | ||||||
| 	if (period_buffer.IsEmpty()) { | 	if (period_buffer.IsEmpty()) { | ||||||
| 		if (snd_pcm_state(pcm) == SND_PCM_STATE_PREPARED) { | 		if (snd_pcm_state(pcm) == SND_PCM_STATE_PREPARED || | ||||||
|  | 		    snd_pcm_avail(pcm) <= max_avail_frames) { | ||||||
| 			/* at SND_PCM_STATE_PREPARED (not yet switched | 			/* at SND_PCM_STATE_PREPARED (not yet switched | ||||||
| 			   to SND_PCM_STATE_RUNNING), we have no | 			   to SND_PCM_STATE_RUNNING), we have no | ||||||
| 			   pressure to fill the ALSA buffer, because | 			   pressure to fill the ALSA buffer, because | ||||||
| @@ -943,6 +955,11 @@ try { | |||||||
| 			   monitoring the ALSA file descriptor, and | 			   monitoring the ALSA file descriptor, and | ||||||
| 			   let it be reactivated by Play()/Activate() | 			   let it be reactivated by Play()/Activate() | ||||||
| 			   whenever more data arrives */ | 			   whenever more data arrives */ | ||||||
|  | 			/* the same applies when there is still enough | ||||||
|  | 			   data in the ALSA-PCM buffer (determined by | ||||||
|  | 			   snd_pcm_avail()); this can happend at the | ||||||
|  | 			   start of playback, when our ring_buffer is | ||||||
|  | 			   smaller than the ALSA-PCM buffer */ | ||||||
|  |  | ||||||
| 			{ | 			{ | ||||||
| 				const std::lock_guard<Mutex> lock(mutex); | 				const std::lock_guard<Mutex> lock(mutex); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann