|
3 | 3 |
|
4 | 4 | use super::{MAX_CAPTURE_CHANNELS, MAX_CAPTURE_SAMPLE_RATE}; |
5 | 5 | use crate::dsp::{AudioFormat, ChannelPosition}; |
6 | | -use crate::util::{audio::DEFAULT_SAMPLE_RATE, unpoison}; |
| 6 | +use crate::util::{ |
| 7 | + audio::{DEFAULT_SAMPLE_RATE, flush_denormal_f32}, |
| 8 | + unpoison, |
| 9 | +}; |
7 | 10 | use rtrb::{Consumer, Producer, PushError, RingBuffer}; |
8 | 11 | use std::hash::{Hash, Hasher}; |
9 | 12 | use std::io::Read; |
@@ -197,14 +200,24 @@ pub(super) fn pcm_chunk( |
197 | 200 | Some([start..start + first, 0..len - first]) |
198 | 201 | } |
199 | 202 |
|
| 203 | +// Producer IIR tails can settle on subnormals, which *MIGHT* stall |
| 204 | +// f32 DSP on CPUs that handle subnormals in microcode (e.g. Intel). |
| 205 | +fn sanitize_pcm(mut sample: f32) -> f32 { |
| 206 | + if !sample.is_finite() { |
| 207 | + return 0.0; |
| 208 | + } |
| 209 | + flush_denormal_f32(&mut sample); |
| 210 | + sample |
| 211 | +} |
| 212 | + |
200 | 213 | fn pcm_has_signal(bytes: &[u8], chunk: &[Range<usize>; 2]) -> bool { |
201 | 214 | let mut source = Read::chain(&bytes[chunk[0].clone()], &bytes[chunk[1].clone()]); |
202 | 215 | let mut raw = [0; 4]; |
203 | 216 | std::iter::from_fn(|| { |
204 | 217 | source.read_exact(&mut raw).ok()?; |
205 | 218 | Some(f32::from_ne_bytes(raw)) |
206 | 219 | }) |
207 | | - .any(|sample| sample != 0.0 && sample.is_finite()) |
| 220 | + .any(|sample| sanitize_pcm(sample) != 0.0) |
208 | 221 | } |
209 | 222 |
|
210 | 223 | struct Packet { |
@@ -332,11 +345,8 @@ impl CaptureWriter { |
332 | 345 | .read_exact(bytemuck::cast_slice_mut(samples)) |
333 | 346 | .expect("valid PCM bounds"); |
334 | 347 | for sample in samples { |
335 | | - if sample.is_finite() { |
336 | | - signal |= *sample != 0.0; |
337 | | - } else { |
338 | | - *sample = 0.0; |
339 | | - } |
| 348 | + *sample = sanitize_pcm(*sample); |
| 349 | + signal |= *sample != 0.0; |
340 | 350 | } |
341 | 351 | }); |
342 | 352 | if accepted { |
@@ -611,7 +621,10 @@ impl AudioReader { |
611 | 621 | return; |
612 | 622 | } |
613 | 623 |
|
614 | | - while let Ok(packet) = self.consumer.pop() { |
| 624 | + for _ in 0..self.consumer.slots() { |
| 625 | + let Ok(packet) = self.consumer.pop() else { |
| 626 | + break; |
| 627 | + }; |
615 | 628 | self.accept(packet, &mut consume); |
616 | 629 | if self.scratch.len() >= PCM_FLUSH_SAMPLES { |
617 | 630 | self.flush(&mut consume); |
@@ -917,15 +930,15 @@ mod tests { |
917 | 930 | } |
918 | 931 |
|
919 | 932 | #[test] |
920 | | - fn pcm_copy_handles_wrapping_and_sanitizes_non_finite_samples() { |
| 933 | + fn pcm_copy_handles_wrapping_and_sanitizes_non_finite_and_tiny_samples() { |
921 | 934 | let (mut writer, mut reader, _) = mono(4, 1_000); |
922 | | - let mut bytes = bytemuck::cast_slice(&[1.0_f32, f32::NAN, 2.0]).to_vec(); |
| 935 | + let mut bytes = bytemuck::cast_slice(&[1.0_f32, f32::NAN, 2.0, 1.0e-40, -1.0e-30]).to_vec(); |
923 | 936 | bytes.rotate_right(3); |
924 | 937 | let chunk = pcm_chunk(bytes.len(), 3, bytes.len() as u32, size_of::<f32>()).unwrap(); |
925 | | - writer.push_pcm(&bytes, &chunk, 3); |
| 938 | + writer.push_pcm(&bytes, &chunk, 5); |
926 | 939 | assert!(writer.flush_pending()); |
927 | 940 | let samples = reader.consumer.pop().unwrap().samples.unwrap(); |
928 | | - assert_eq!(&samples[..3], &[1.0, 0.0, 2.0]); |
| 941 | + assert_eq!(&samples[..5], &[1.0, 0.0, 2.0, 0.0, 0.0]); |
929 | 942 | } |
930 | 943 |
|
931 | 944 | #[test] |
@@ -958,11 +971,13 @@ mod tests { |
958 | 971 | assert!(reader.sleep_until_signal()); |
959 | 972 |
|
960 | 973 | writer.push_silence(BLOCK_FRAMES as u64); |
961 | | - let zero = 0.0_f32.to_ne_bytes(); |
962 | | - let chunk = pcm_chunk(zero.len(), 0, zero.len() as u32, zero.len()).unwrap(); |
963 | | - writer.push_pcm(&zero, &chunk, 1); |
964 | | - assert!(!reader.is_active()); |
965 | | - assert!(audio_wake(&wake).try_recv().is_err()); |
| 974 | + for quiet in [0.0_f32, 1.0e-40] { |
| 975 | + let quiet = quiet.to_ne_bytes(); |
| 976 | + let chunk = pcm_chunk(quiet.len(), 0, quiet.len() as u32, quiet.len()).unwrap(); |
| 977 | + writer.push_pcm(&quiet, &chunk, 1); |
| 978 | + assert!(!reader.is_active()); |
| 979 | + assert!(audio_wake(&wake).try_recv().is_err()); |
| 980 | + } |
966 | 981 |
|
967 | 982 | let mut signal = 0.25_f32.to_ne_bytes(); |
968 | 983 | signal.rotate_right(1); |
|
0 commit comments