Make the ALSA implementation more robust by recovering from underruns

This commit is contained in:
Pierre Krieger 2015-09-22 20:20:17 +02:00
parent 73be6abe6c
commit 5fc346b34b
1 changed files with 40 additions and 13 deletions

View File

@ -233,11 +233,11 @@ impl Voice {
check_errors(alsa::snd_pcm_prepare(playback_handle)).unwrap(); check_errors(alsa::snd_pcm_prepare(playback_handle)).unwrap();
let buffer_len = { let buffer_len = {
let obtained_params = HwParams::alloc(); let mut dummy = mem::uninitialized();
check_errors(alsa::snd_pcm_hw_params_current(playback_handle, hw_params.0)).unwrap();
let mut val = mem::uninitialized(); let mut val = mem::uninitialized();
check_errors(alsa::snd_pcm_hw_params_get_buffer_size(obtained_params.0, &mut val)).unwrap(); check_errors(alsa::snd_pcm_get_params(playback_handle, &mut val, &mut dummy)).unwrap();
val as usize / format.data_type.get_sample_size() assert!(val != 0);
val as usize * format.channels.len()
}; };
Ok(Voice { Ok(Voice {
@ -252,10 +252,19 @@ impl Voice {
let available = { let available = {
let channel = self.channel.lock().unwrap(); let channel = self.channel.lock().unwrap();
let available = unsafe { alsa::snd_pcm_avail(*channel) }; 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 { Buffer {
channel: self, channel: self,
@ -278,14 +287,25 @@ impl Voice {
let available = { let available = {
let channel = self.channel.lock().unwrap(); let channel = self.channel.lock().unwrap();
let available = unsafe { alsa::snd_pcm_avail(*channel) }; let available = unsafe { alsa::snd_pcm_avail(*channel) };
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 available * self.num_channels as alsa::snd_pcm_sframes_t
}
}; };
self.buffer_len - available as usize self.buffer_len - available as usize
} }
pub fn underflowed(&self) -> bool { 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(); let channel = self.channel.channel.lock().unwrap();
unsafe { unsafe {
loop {
let result = alsa::snd_pcm_writei(*channel, let result = alsa::snd_pcm_writei(*channel,
self.buffer.as_ptr() as *const libc::c_void, self.buffer.as_ptr() as *const libc::c_void,
written); written);
if result < 0 { if result == -32 {
// buffer underrun
alsa::snd_pcm_prepare(*channel);
} else if result < 0 {
check_errors(result as libc::c_int).unwrap(); check_errors(result as libc::c_int).unwrap();
} else {
break;
}
} }
} }
} }