diff --git a/src/codec/opus_impl.rs b/src/codec/opus_impl.rs index b701f3e..039b857 100644 --- a/src/codec/opus_impl.rs +++ b/src/codec/opus_impl.rs @@ -80,3 +80,80 @@ impl AudioDecoder for OpusDecoder { Ok(pcm) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_round_trip() { + let mut encoder = OpusEncoder::new(48000, Channels::Mono, Application::Voip).unwrap(); + let mut decoder = OpusDecoder::new(48000, Channels::Mono, 960).unwrap(); + + // 1. Round-trip shape: generate a 960-sample mono PCM frame (440Hz sine wave) + let mut pcm = vec![0i16; 960]; + for (i, sample) in pcm.iter_mut().enumerate() { + let t = (i as f32) / 48000.0; + let val = (t * 440.0 * 2.0 * std::f32::consts::PI).sin(); + *sample = (val * 10000.0) as i16; + } + + // encode it + let compressed = encoder.encode(&pcm).unwrap(); + assert!(!compressed.is_empty(), "Compressed buffer should not be empty"); + assert!( + compressed.len() < pcm.len() * std::mem::size_of::(), + "Compressed size ({}) should be smaller than raw PCM size ({})", + compressed.len(), + pcm.len() * std::mem::size_of::() + ); + + // decode it + let decoded = decoder.decode(Some(&compressed)).unwrap(); + assert_eq!(decoded.len(), 960, "Decoded sample count should be exactly 960"); + + // 2. Round-trip carries signal energy (not silence) + let sum_sq: f64 = decoded.iter().map(|&x| (x as f64).powi(2)).sum(); + let rms = (sum_sq / decoded.len() as f64).sqrt(); + // Since input had amplitude ~10000, let's verify RMS is significantly above 0 (e.g. > 100.0) + assert!(rms > 100.0, "Decoded signal should carry energy (RMS was {})", rms); + } + + #[test] + fn test_plc_sizing() { + let mut decoder = OpusDecoder::new(48000, Channels::Mono, 960).unwrap(); + + // decode(None) returns exactly frame_samples (960) samples + let plc_none = decoder.decode(None).unwrap(); + assert_eq!(plc_none.len(), 960, "decode(None) should yield exactly 960 samples"); + + // decode(Some(&[])) (empty slice) does the same + let plc_empty = decoder.decode(Some(&[])).unwrap(); + assert_eq!(plc_empty.len(), 960, "decode(Some(&[])) should yield exactly 960 samples"); + } + + #[test] + fn test_decoded_duration_follows_packet() { + let mut encoder = OpusEncoder::new(48000, Channels::Mono, Application::Voip).unwrap(); + let mut decoder = OpusDecoder::new(48000, Channels::Mono, 960).unwrap(); + + let pcm = vec![0i16; 960]; + let compressed = encoder.encode(&pcm).unwrap(); + let decoded = decoder.decode(Some(&compressed)).unwrap(); + assert_eq!(decoded.len(), 960, "Decoded sample count should match packet duration"); + } + + #[test] + fn test_stereo_plc_sizing() { + let mut decoder = OpusDecoder::new(48000, Channels::Stereo, 960).unwrap(); + + // decode(None) returns exactly frame_samples * 2 (1920) samples + let plc_none = decoder.decode(None).unwrap(); + assert_eq!(plc_none.len(), 960 * 2, "Stereo decode(None) should yield exactly 1920 samples"); + + // decode(Some(&[])) (empty slice) does the same + let plc_empty = decoder.decode(Some(&[])).unwrap(); + assert_eq!(plc_empty.len(), 960 * 2, "Stereo decode(Some(&[])) should yield exactly 1920 samples"); + } +} +