From 5fc346b34bbc9dd971f0f669870ca96c25719096 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 22 Sep 2015 20:20:17 +0200 Subject: [PATCH 1/2] Make the ALSA implementation more robust by recovering from underruns --- src/alsa/mod.rs | 53 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/src/alsa/mod.rs b/src/alsa/mod.rs index bf79262..c527f6b 100644 --- a/src/alsa/mod.rs +++ b/src/alsa/mod.rs @@ -233,11 +233,11 @@ impl Voice { check_errors(alsa::snd_pcm_prepare(playback_handle)).unwrap(); let buffer_len = { - let obtained_params = HwParams::alloc(); - check_errors(alsa::snd_pcm_hw_params_current(playback_handle, hw_params.0)).unwrap(); + let mut dummy = mem::uninitialized(); let mut val = mem::uninitialized(); - check_errors(alsa::snd_pcm_hw_params_get_buffer_size(obtained_params.0, &mut val)).unwrap(); - val as usize / format.data_type.get_sample_size() + check_errors(alsa::snd_pcm_get_params(playback_handle, &mut val, &mut dummy)).unwrap(); + assert!(val != 0); + val as usize * format.channels.len() }; Ok(Voice { @@ -252,10 +252,19 @@ impl Voice { let available = { let channel = self.channel.lock().unwrap(); let available = unsafe { alsa::snd_pcm_avail(*channel) }; - available * self.num_channels as alsa::snd_pcm_sframes_t + + if available == -32 { + // buffer underrun + self.buffer_len + } else if available < 0 { + check_errors(available as libc::c_int).unwrap(); + unreachable!() + } else { + (available * self.num_channels as alsa::snd_pcm_sframes_t) as usize + } }; - let elements = ::std::cmp::min(available as usize, max_elements); + let elements = cmp::min(available, max_elements); Buffer { channel: self, @@ -278,14 +287,25 @@ impl Voice { let available = { let channel = self.channel.lock().unwrap(); let available = unsafe { alsa::snd_pcm_avail(*channel) }; - available * self.num_channels as alsa::snd_pcm_sframes_t + + if available == -32 { + 0 // buffer underrun + } else if available < 0 { + check_errors(available as libc::c_int).unwrap(); + unreachable!() + } else { + available * self.num_channels as alsa::snd_pcm_sframes_t + } }; self.buffer_len - available as usize } pub fn underflowed(&self) -> bool { - false // TODO: + let channel = self.channel.lock().unwrap(); + + let state = unsafe { alsa::snd_pcm_state(*channel) }; + state == alsa::SND_PCM_STATE_XRUN } } @@ -318,12 +338,19 @@ impl<'a, T> Buffer<'a, T> { let channel = self.channel.channel.lock().unwrap(); unsafe { - let result = alsa::snd_pcm_writei(*channel, - self.buffer.as_ptr() as *const libc::c_void, - written); + loop { + let result = alsa::snd_pcm_writei(*channel, + self.buffer.as_ptr() as *const libc::c_void, + written); - if result < 0 { - check_errors(result as libc::c_int).unwrap(); + if result == -32 { + // buffer underrun + alsa::snd_pcm_prepare(*channel); + } else if result < 0 { + check_errors(result as libc::c_int).unwrap(); + } else { + break; + } } } } From 6bd28231ec697be5807740bebbe34fee4a709932 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 22 Sep 2015 21:01:26 +0200 Subject: [PATCH 2/2] Make sure that all writes succeed --- src/alsa/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/alsa/mod.rs b/src/alsa/mod.rs index c527f6b..dd1b8a5 100644 --- a/src/alsa/mod.rs +++ b/src/alsa/mod.rs @@ -333,15 +333,15 @@ impl<'a, T> Buffer<'a, T> { } pub fn finish(self) { - let written = (self.buffer.len() / self.channel.num_channels as usize) - as alsa::snd_pcm_uframes_t; + let to_write = (self.buffer.len() / self.channel.num_channels as usize) + as alsa::snd_pcm_uframes_t; let channel = self.channel.channel.lock().unwrap(); unsafe { loop { let result = alsa::snd_pcm_writei(*channel, self.buffer.as_ptr() as *const libc::c_void, - written); + to_write); if result == -32 { // buffer underrun @@ -349,6 +349,7 @@ impl<'a, T> Buffer<'a, T> { } else if result < 0 { check_errors(result as libc::c_int).unwrap(); } else { + assert_eq!(result as alsa::snd_pcm_uframes_t, to_write); break; } }