Merge pull request #79 from tomaka/alsa-robust

Make the ALSA implementation more robust by recovering from underruns
This commit is contained in:
tomaka 2015-09-22 21:07:29 +02:00
commit 6c72a7c08b
1 changed files with 43 additions and 15 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) };
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 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
} }
} }
@ -313,17 +333,25 @@ impl<'a, T> Buffer<'a, T> {
} }
pub fn finish(self) { pub fn finish(self) {
let written = (self.buffer.len() / self.channel.num_channels as usize) let to_write = (self.buffer.len() / self.channel.num_channels as usize)
as alsa::snd_pcm_uframes_t; as alsa::snd_pcm_uframes_t;
let channel = self.channel.channel.lock().unwrap(); let channel = self.channel.channel.lock().unwrap();
unsafe { unsafe {
let result = alsa::snd_pcm_writei(*channel, loop {
self.buffer.as_ptr() as *const libc::c_void, let result = alsa::snd_pcm_writei(*channel,
written); self.buffer.as_ptr() as *const libc::c_void,
to_write);
if result < 0 { if result == -32 {
check_errors(result as libc::c_int).unwrap(); // buffer underrun
alsa::snd_pcm_prepare(*channel);
} 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;
}
} }
} }
} }