diff --git a/bark/src/audio/output.rs b/bark/src/audio/output.rs index dfe169d..11450e3 100644 --- a/bark/src/audio/output.rs +++ b/bark/src/audio/output.rs @@ -1,6 +1,8 @@ use alsa::{ValueOr, Direction}; use alsa::pcm::{PCM, HwParams, Format, Access}; +use bark_protocol::CHANNELS; use bark_protocol::time::SampleDuration; +use nix::errno::Errno; use thiserror::Error; pub struct Output { @@ -57,11 +59,46 @@ impl Output { Ok(Output { pcm }) } - pub fn write(&self, audio: &[f32]) -> Result<(), WriteAudioError> { - self.pcm.io_f32()?.writei(audio)?; + pub fn write(&self, mut audio: &[f32]) -> Result<(), WriteAudioError> { + while audio.len() > 0 { + let n = self.write_partial(audio)?; + audio = &audio[n..]; + } + Ok(()) } + fn write_partial(&self, audio: &[f32]) -> Result { + let io = unsafe { + // the checked versions of this function call + // snd_pcm_hw_params_current which mallocs under the hood + self.pcm.io_unchecked::() + }; + + loop { + // try to write audio + let err = match io.writei(audio) { + Ok(n) => { + return Ok(n * CHANNELS.0 as usize); + } + Err(e) => e, + }; + + // handle recoverable errors + match err.errno() { + | Errno::EPIPE // underrun + | Errno::ESTRPIPE // stream suspended + | Errno::EINTR // interrupted syscall + => { + eprintln!("recovering from error: {}", err.errno()); + // try to recover + self.pcm.recover(err.errno() as i32, false)?; + } + _ => { return Err(err.into()); } + } + } + } + pub fn delay(&self) -> Result { let frames = self.pcm.delay()?; Ok(SampleDuration::from_frame_count(frames.try_into().unwrap()))