use std::path::PathBuf;
use audio_codec_bsd::{open, ContainerDecoder, WavDecoder};
use hound::{SampleFormat, WavSpec, WavWriter};
use proptest::prelude::*;
fn write_pcm_wav(channels: u16, sample_rate: u32, interleaved: &[i16], name: &str) -> PathBuf {
let spec = WavSpec {
channels,
sample_rate,
bits_per_sample: 16,
sample_format: SampleFormat::Int,
};
let mut path = std::env::temp_dir();
path.push(format!(
"audio_codec_bsd_prop_{id}_{name}.wav",
id = std::process::id(),
));
let mut writer = WavWriter::create(&path, spec).expect("create wav");
for &v in interleaved {
writer.write_sample(v).expect("write sample");
}
writer.finalize().expect("finalize wav");
path
}
fn drain_to_planar_i16(dec: &mut dyn ContainerDecoder) -> Vec<i16> {
let mut out: Vec<i16> = Vec::new();
while let Ok(Some(frame)) = dec.next_frame() {
for &s in &frame.samples {
let scaled = s * 32_768.0_f32;
let clamped = scaled.clamp(-32_768.0_f32, 32_767.0_f32);
out.push((clamped.round()) as i16);
}
}
out
}
proptest! {
#![proptest_config(ProptestConfig {
cases: 64,
..ProptestConfig::default()
})]
#[test]
fn wav_byte_exact_for_random_pcm(
samples in proptest::collection::vec(-32768i16..=32767i16, 1..1024),
channels in 1u16..=2u16,
sr in 8000u32..=96000u32,
) {
let ch = usize::from(channels);
let n = samples.len() - (samples.len() % ch);
let interleaved = &samples[..n];
let frames = n / ch;
let path = write_pcm_wav(channels, sr, interleaved, "pcm");
let mut dec = WavDecoder::open(&path).expect("open");
let _info = dec.open(&path).expect("trait open");
let recovered = drain_to_planar_i16(&mut dec);
let _ = std::fs::remove_file(&path);
prop_assert_eq!(recovered.len(), n);
for (i, &want) in interleaved.iter().enumerate() {
let c = i % ch;
let f = i / ch;
prop_assert_eq!(recovered[c * frames + f], want);
}
}
#[test]
fn malformed_bytes_never_panic(
bytes in proptest::collection::vec(any::<u8>(), 0..4096),
) {
let mut path = std::env::temp_dir();
path.push(format!(
"audio_codec_bsd_prop_malformed_{id}.bin",
id = std::process::id(),
));
let _ = std::fs::write(&path, &bytes);
if let Ok(mut dec) = open(&path) {
let _ = dec.open(&path);
while let Ok(Some(_)) = dec.next_frame() {
}
}
let _ = std::fs::remove_file(&path);
}
#[test]
fn planar_length_invariant(
channels in 1u16..=2u16,
n in 0usize..512,
) {
let ch = usize::from(channels);
let total = ch * n;
let samples: Vec<i16> = vec![0; total];
let path = write_pcm_wav(channels, 48_000, &samples, "planar_inv");
let mut dec = WavDecoder::open(&path).expect("open");
let _info = dec.open(&path).expect("trait open");
while let Ok(Some(frame)) = dec.next_frame() {
prop_assert_eq!(
frame.samples.len(),
usize::from(frame.channels) * frame.num_frames()
);
}
let _ = std::fs::remove_file(&path);
}
}