From 1c3522be4a4694e4e13c84b6a13daffeaf3a7611 Mon Sep 17 00:00:00 2001 From: skylar taylor-barrick Date: Thu, 22 Oct 2026 18:55:49 -0600 Subject: [PATCH] #382: test that audio decoding at a zero mix rate does not divide by zero Resampling footage audio divided by the requested mix rate, so a rate of 0 panicked. The test decodes a 44.1 kHz WAV at rate 0 and expects a read of the requested length. Without the guard it panics with 'attempt to divide by zero'. --- crates/media/src/pool.rs | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/crates/media/src/pool.rs b/crates/media/src/pool.rs index 9a8ec5c2..d363143d 100644 --- a/crates/media/src/pool.rs +++ b/crates/media/src/pool.rs @@ -1030,6 +1030,48 @@ mod tests { let _ = std::fs::remove_dir_all(&dir); } + // Regression test for #382 (#274): resampling footage audio divided by the requested mix + // rate, so a rate of 0 panicked (division by zero); the read must come back instead. + #[test] + fn decoding_audio_at_a_zero_mix_rate_does_not_divide_by_zero() { + let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../target/test-zero-rate"); + let _ = std::fs::remove_dir_all(&dir); + std::fs::create_dir_all(&dir).unwrap(); + // One second of a 440 Hz tone, 44.1 kHz mono 16-bit PCM: not the 48 kHz mix rate. + let native = 44_100u32; + let pcm: Vec = + (0..native).flat_map(|n| (((n as f32 * 440.0 * std::f32::consts::TAU / native as f32).sin() * 8000.0) as i16).to_le_bytes()).collect(); + let mut wav = b"RIFF".to_vec(); + wav.extend((36 + pcm.len() as u32).to_le_bytes()); + wav.extend(b"WAVEfmt "); + wav.extend(16u32.to_le_bytes()); + wav.extend([1u16, 1].iter().flat_map(|v| v.to_le_bytes())); + wav.extend(native.to_le_bytes()); + wav.extend((native * 2).to_le_bytes()); + wav.extend([2u16, 16].iter().flat_map(|v| v.to_le_bytes())); + wav.extend(b"data"); + wav.extend((pcm.len() as u32).to_le_bytes()); + wav.extend(&pcm); + let path = dir.join("tone.wav"); + std::fs::write(&path, &wav).unwrap(); + let footage = Footage { + path: path.to_string_lossy().into(), + kind: FootageKind::Audio, + duration: Tick::from_seconds_f64(1.0), + has_audio: true, + ..Default::default() + }; + let pool = MediaPool::new(); + // The file really is read, and resampled, at a normal rate... + let tone = pool.decode_audio(&footage, Tick::from_seconds_f64(0.1), 1024, 48_000); + assert!(tone.iter().any(|v| v.abs() > 0.05), "the tone is audible at 48 kHz"); + // ...and a mix rate of 0 (which `audio_samples` itself turns away) returns a read of the + // requested length instead of dividing by it. + let degenerate = pool.decode_audio(&footage, Tick::from_seconds_f64(0.1), 64, 0); + assert_eq!(degenerate.len(), 128); + let _ = std::fs::remove_dir_all(&dir); + } + #[test] fn frame_index_loops_and_clamps() { let r = FrameRate::FPS_30;