diff --git a/crates/opusenc/src/silk/mod.rs b/crates/opusenc/src/silk/mod.rs index 0a1dedad..c7a4b869 100644 --- a/crates/opusenc/src/silk/mod.rs +++ b/crates/opusenc/src/silk/mod.rs @@ -4,9 +4,10 @@ //! search), its NLSFs are quantised, and the excitation is chosen sample by sample by running //! the decoder's exact fixed-point reconstruction (LTP and LPC synthesis with the dequantised //! Q12/Q14 coefficients, quantisation offsets and the pseudo-random sign sequence), so the -//! encoder always holds the decoder's state and there is no drift. A per-frame search over a -//! gain offset hits the bit budget. Stereo input is coded as mid/side with the decoder's -//! stereo prediction (§4.2.8). No LBRR, no NLSF interpolation, no noise shaping. +//! encoder always holds the decoder's state and there is no drift. A noise-feedback loop +//! shapes the coding noise with a bandwidth-expanded LPC envelope, and a per-frame search over +//! a gain offset hits the bit budget. Stereo input is coded as mid/side with the decoder's +//! stereo prediction (§4.2.8). No LBRR, no NLSF interpolation. pub(crate) mod analysis; pub(crate) mod lpc; diff --git a/crates/opusenc/tests/modes.rs b/crates/opusenc/tests/modes.rs index 1ae4ead4..843cf3d9 100644 --- a/crates/opusenc/tests/modes.rs +++ b/crates/opusenc/tests/modes.rs @@ -223,6 +223,38 @@ fn invalid_mode_bandwidth_panics() { let _ = OpusEncoder::with_mode(1, 16_000, Mode::Silk, Bandwidth::Fullband); } +#[test] +fn extreme_inputs_stay_decodable() { + let len = 24_000; + let mut s = 1u32; + let noise: Vec = (0..len) + .map(|_| { + s = s.wrapping_mul(1_664_525).wrapping_add(1_013_904_223); + (s >> 8) as f32 / (1u32 << 23) as f32 - 1.0 + }) + .collect(); + let square: Vec = (0..len).map(|i| if (i / 60) % 2 == 0 { 0.999 } else { -0.999 }).collect(); + let clicks: Vec = (0..len).map(|i| if i % 4_801 == 0 { 1.0 } else { 0.0 }).collect(); + let dc = vec![0.7f32; len]; + let nan: Vec = (0..len).map(|i| if i % 1000 == 0 { f32::NAN } else { 0.1 * (i as f32 * 0.01).sin() }).collect(); + for (name, x) in [("noise", &noise), ("square", &square), ("clicks", &clicks), ("dc", &dc), ("nan", &nan)] { + for (mode, bw, rate) in + [(Mode::Silk, Bandwidth::Narrowband, 6_000u32), (Mode::Silk, Bandwidth::Wideband, 40_000), (Mode::Hybrid, Bandwidth::Fullband, 24_000)] + { + for c in [1usize, 2] { + let planar: Vec> = if c == 1 { vec![x.clone()] } else { vec![x.clone(), x.iter().map(|v| -0.5 * v).collect()] }; + let (enc, packets) = encode(OpusEncoder::with_mode(c as u8, rate * c as u32, mode, bw), &planar); + assert!(packets.iter().all(|p| p.len() >= 3 && p.len() <= 1276), "{name} {mode:?}: packet sizes"); + let dec = decode(&enc.opus_head(), &packets); + let peak = dec.iter().flatten().fold(0f32, |m, v| m.max(v.abs())); + let snr = if name == "nan" { f64::NAN } else { snr_search(&planar[0], &dec[0], 2).0 }; + eprintln!("{name} {mode:?}/{bw:?} {c} ch: peak {peak:.2}, SNR {snr:.1} dB, {:.1} kb/s", bitrate(&packets) / 1000.0); + assert!(dec.iter().all(|ch| ch.iter().all(|v| v.is_finite() && v.abs() <= 4.0)), "{name} {mode:?}/{bw:?} {c} ch: bad output"); + } + } + } +} + #[test] fn silk_and_hybrid_silence() { for (mode, bw, rate) in [(Mode::Silk, Bandwidth::Wideband, 16_000u32), (Mode::Hybrid, Bandwidth::Fullband, 32_000)] {