From 7e76c88a4bea4096baec106042550c001611910d Mon Sep 17 00:00:00 2001 From: HEnquist Date: Sun, 14 Mar 2021 18:38:45 +0100 Subject: [PATCH 1/4] Alsa, wait until frames are available --- src/alsadevice.rs | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/alsadevice.rs b/src/alsadevice.rs index 6906467e..6ff39dd8 100644 --- a/src/alsadevice.rs +++ b/src/alsadevice.rs @@ -131,18 +131,43 @@ fn play_buffer( Ok(()) } -/// Play a buffer. +/// Capture a buffer. fn capture_buffer( buffer: &mut [u8], pcmdevice: &alsa::PCM, io: &alsa::pcm::IO, retry: bool, + samplerate: usize, + frames_to_read: usize, ) -> Res { let capture_state = pcmdevice.state(); if capture_state == State::XRun { warn!("prepare capture"); pcmdevice.prepare()?; } + let available = pcmdevice.avail(); + match available { + Ok(frames) => { + if (frames as usize) < frames_to_read { + // Let's wait for more frames, with 20% plus 1 ms of margin + thread::sleep(Duration::from_millis((1 + (1200*(frames_to_read - frames as usize))/samplerate) as u64)); + if (pcmdevice.avail().unwrap_or(0) as usize) < frames_to_read { + // Still not enough, + warn!("Capture timed out"); + return Ok(CaptureResult::RecoverableError); + } + } + } + Err(err) => { + if retry { + warn!("Capture failed while reading available frames, error: {}, will try again.", err); + return Ok(CaptureResult::RecoverableError); + } else { + warn!("Capture failed while reading available frames, error: {}", err); + return Err(Box::new(err)); + } + } + } let _frames = match io.readi(buffer) { Ok(frames) => frames, Err(err) => match err.nix_error() { @@ -397,6 +422,8 @@ fn capture_loop_bytes( pcmdevice, &io, params.retry_on_error, + params.capture_samplerate, + capture_bytes / (params.channels * params.store_bytes_per_sample) ); match capture_res { Ok(CaptureResult::Normal) => { From d5a75ae4853246098dacfb2098122afaf3fb22e7 Mon Sep 17 00:00:00 2001 From: Henrik Enquist Date: Sun, 14 Mar 2021 21:34:12 +0100 Subject: [PATCH 2/4] Check if capture needs start --- src/alsadevice.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/alsadevice.rs b/src/alsadevice.rs index 6ff39dd8..08c8c947 100644 --- a/src/alsadevice.rs +++ b/src/alsadevice.rs @@ -142,28 +142,34 @@ fn capture_buffer( ) -> Res { let capture_state = pcmdevice.state(); if capture_state == State::XRun { - warn!("prepare capture"); + warn!("Prepare capture device"); pcmdevice.prepare()?; + //pcmdevice.start()?; + } + else if capture_state != State::Running { + debug!("Starting capture"); + pcmdevice.start()?; } let available = pcmdevice.avail(); match available { Ok(frames) => { if (frames as usize) < frames_to_read { + trace!("Not enough frames available: {}, need: {}, waiting...", frames, frames_to_read); // Let's wait for more frames, with 20% plus 1 ms of margin thread::sleep(Duration::from_millis((1 + (1200*(frames_to_read - frames as usize))/samplerate) as u64)); if (pcmdevice.avail().unwrap_or(0) as usize) < frames_to_read { // Still not enough, - warn!("Capture timed out"); + warn!("Capture timed out, will try again"); return Ok(CaptureResult::RecoverableError); } } } Err(err) => { if retry { - warn!("Capture failed while reading available frames, error: {}, will try again.", err); + warn!("Capture failed while querying for available frames, error: {}, will try again.", err); return Ok(CaptureResult::RecoverableError); } else { - warn!("Capture failed while reading available frames, error: {}", err); + warn!("Capture failed while querying for available frames, error: {}", err); return Err(Box::new(err)); } } @@ -176,7 +182,7 @@ fn capture_buffer( warn!("Capture failed with error: {}, will try again.", err); return Ok(CaptureResult::RecoverableError); } else { - warn!("Capture failed, error: {}", err); + warn!("Capture failed with error: {}", err); return Err(Box::new(err)); } } From 7d91934d4c4ba00798eb8f565e911d241a2b942f Mon Sep 17 00:00:00 2001 From: HEnquist Date: Sun, 14 Mar 2021 22:11:46 +0100 Subject: [PATCH 3/4] Format and cleanup --- src/alsadevice.rs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/alsadevice.rs b/src/alsadevice.rs index 08c8c947..c970cd1b 100644 --- a/src/alsadevice.rs +++ b/src/alsadevice.rs @@ -144,21 +144,25 @@ fn capture_buffer( if capture_state == State::XRun { warn!("Prepare capture device"); pcmdevice.prepare()?; - //pcmdevice.start()?; - } - else if capture_state != State::Running { - debug!("Starting capture"); + } else if capture_state != State::Running { + debug!("Starting capture"); pcmdevice.start()?; } let available = pcmdevice.avail(); match available { Ok(frames) => { if (frames as usize) < frames_to_read { - trace!("Not enough frames available: {}, need: {}, waiting...", frames, frames_to_read); + trace!( + "Not enough frames available: {}, need: {}, waiting...", + frames, + frames_to_read + ); // Let's wait for more frames, with 20% plus 1 ms of margin - thread::sleep(Duration::from_millis((1 + (1200*(frames_to_read - frames as usize))/samplerate) as u64)); + thread::sleep(Duration::from_millis( + (1 + (1200 * (frames_to_read - frames as usize)) / samplerate) as u64, + )); if (pcmdevice.avail().unwrap_or(0) as usize) < frames_to_read { - // Still not enough, + // Still not enough, warn!("Capture timed out, will try again"); return Ok(CaptureResult::RecoverableError); } @@ -167,9 +171,15 @@ fn capture_buffer( Err(err) => { if retry { warn!("Capture failed while querying for available frames, error: {}, will try again.", err); + thread::sleep(Duration::from_millis( + (1000 * frames_to_read as u64) / samplerate as u64, + )); return Ok(CaptureResult::RecoverableError); } else { - warn!("Capture failed while querying for available frames, error: {}", err); + warn!( + "Capture failed while querying for available frames, error: {}", + err + ); return Err(Box::new(err)); } } @@ -429,7 +439,7 @@ fn capture_loop_bytes( &io, params.retry_on_error, params.capture_samplerate, - capture_bytes / (params.channels * params.store_bytes_per_sample) + capture_bytes / (params.channels * params.store_bytes_per_sample), ); match capture_res { Ok(CaptureResult::Normal) => { From bc14de6f484ab6c5afc812d4bb7ddc33f5e3c633 Mon Sep 17 00:00:00 2001 From: HEnquist Date: Thu, 8 Apr 2021 21:01:07 +0200 Subject: [PATCH 4/4] Reduce margin, update changelog --- CHANGELOG.md | 1 + Cargo.toml | 2 +- src/alsadevice.rs | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a84f09c6..8eb98a56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ New features: - Add `debug` feature for extra logging. - Improve validation of filters. - Setting to enable retry on reads from Alsa capture devices. +- Avoid blocking reads on Alsa capture devices (helps avoiding driver bugs for some devices). Bugfixes: - Don't block playback for CoreAudio/Wasapi if there is no data in time. diff --git a/Cargo.toml b/Cargo.toml index 4e0aba58..75d16351 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "camilladsp" -version = "0.5.0" +version = "0.5.0-beta5" authors = ["Henrik Enquist "] description = "A flexible tool for processing audio" diff --git a/src/alsadevice.rs b/src/alsadevice.rs index c970cd1b..0335c24e 100644 --- a/src/alsadevice.rs +++ b/src/alsadevice.rs @@ -157,9 +157,9 @@ fn capture_buffer( frames, frames_to_read ); - // Let's wait for more frames, with 20% plus 1 ms of margin + // Let's wait for more frames, with 10% plus 1 ms of margin thread::sleep(Duration::from_millis( - (1 + (1200 * (frames_to_read - frames as usize)) / samplerate) as u64, + (1 + (1100 * (frames_to_read - frames as usize)) / samplerate) as u64, )); if (pcmdevice.avail().unwrap_or(0) as usize) < frames_to_read { // Still not enough,