mod common;
use common::*;
use opus_pure::{Application, Bandwidth, RateControl, Signal, packet};
fn pinned(rate: i32, channels: usize, mode: &str, frame: usize) -> Codec {
let c = match mode {
"silk" => Codec::new(rate, channels, Application::Voip)
.signal_type(Signal::Voice)
.bandwidth(Bandwidth::Wideband)
.bitrate(32_000),
"hybrid" => Codec::new(rate, channels, Application::Voip)
.signal_type(Signal::Voice)
.bandwidth(Bandwidth::Superwideband)
.bitrate(if channels == 2 { 24_000 } else { 32_000 }),
"celt" => Codec::new(rate, channels, Application::Audio)
.signal_type(Signal::Music)
.bitrate(96_000),
other => panic!("no configuration pins {other}"),
};
c.frame_samples(frame)
}
fn expected_frame_samples(rate: i32, tenth_ms: i32, mode: &str) -> usize {
let ms20 = (rate / 50) as usize;
let ms40 = (rate / 25) as usize;
let ms60 = (3 * rate / 50) as usize;
let whole = (rate as i64 * tenth_ms as i64 / 10_000) as usize;
match (tenth_ms, mode) {
(t, _) if t <= 200 => whole,
(400, "silk") | (800, "silk") => ms40,
(600, "silk") | (1200, "silk") => ms60,
_ => ms20,
}
}
#[test]
fn packet_framing_follows_the_mode() {
let rate = 48_000i32;
for &mode in &["silk", "hybrid", "celt"] {
for &channels in &[1usize, 2] {
for &tenth_ms in &[25i32, 50, 100, 200, 400, 600, 800, 1000, 1200] {
let frame = (rate as i64 * tenth_ms as i64 / 10_000) as usize;
let mono = music_like(rate, frame * 10);
let pcm = if channels == 2 {
interleave(&[mono.clone(), mono])
} else {
mono
};
let Ok(packets) = pinned(rate, channels, mode, frame).try_encode_all(&pcm) else {
continue;
};
for (i, p) in packets.iter().enumerate() {
let got_mode = packet_mode(p);
let want = expected_frame_samples(rate, tenth_ms, got_mode);
let per = packet::samples_per_frame(p, rate).unwrap();
let frames = packet::frame_count(p).unwrap();
let label =
format!("{mode}-pinned {channels}ch {}ms packet {i}", tenth_ms / 10);
assert_eq!(
per, want,
"{label}: coded as {got_mode}, so each frame should be \
{want} samples, not {per}"
);
assert_eq!(
frames * per,
frame,
"{label}: {frames} frames of {per} samples is not {frame}"
);
}
}
}
}
}
#[test]
fn long_music_frames_are_not_forced_to_silk() {
let rate = 48_000i32;
for &(tenth_ms, want_frames, want_code) in &[(400i32, 2usize, 1u8), (600, 3, 3)] {
let frame = (rate as i64 * tenth_ms as i64 / 10_000) as usize;
let src = music_like(rate, frame * 10);
let packets = Codec::new(rate, 1, Application::Audio)
.bitrate(64_000)
.frame_samples(frame)
.encode_all(&src);
for (i, p) in packets.iter().enumerate() {
let label = format!("{}ms packet {i}", tenth_ms / 10);
assert_eq!(packet_mode(p), "celt", "{label}: not CELT");
assert_eq!(
packet::frame_count(p).unwrap(),
want_frames,
"{label}: wrong frame count"
);
assert_eq!(p[0] & 0x03, want_code, "{label}: wrong packing code");
}
}
}
#[test]
fn every_frame_of_a_split_packet_carries_its_own_audio() {
let rate = 48_000i32;
let slice = (rate / 50) as usize; let tones = [500.0f32, 1000.0, 2000.0, 3000.0, 5000.0, 8000.0];
let frame = slice * tones.len(); let packets = 8;
let mut src = Vec::with_capacity(frame * (packets + 1));
for i in 0..frame * (packets + 1) {
let hz = tones[(i / slice) % tones.len()];
let t = i as f64 / rate as f64;
src.push(0.5 * sin_turns(hz as f64 * t) as f32);
}
let r = Codec::new(rate, 1, Application::Audio)
.signal_type(Signal::Music)
.bitrate(128_000)
.frame_samples(frame)
.roundtrip(&src);
for p in &r.packets {
assert_eq!(packet::frame_count(p).unwrap(), tones.len());
}
let bin = |x: &[f32], hz: f32| -> f32 {
let (mut re, mut im) = (0.0f32, 0.0f32);
for (n, &v) in x.iter().enumerate() {
let turns = hz as f64 * n as f64 / rate as f64;
re += v * sin_turns(turns + 0.25) as f32;
im += v * sin_turns(turns) as f32;
}
(re * re + im * im).sqrt() / x.len() as f32
};
let (_, lag) = aligned_correlation(&r.decoded, &src, slice);
let guard = (rate / 400) as usize;
for p in 2..packets {
for (k, &hz) in tones.iter().enumerate() {
let start = p * frame + k * slice + lag;
let seg = &r.decoded[start + guard..start + slice - guard];
let mine = bin(seg, hz);
for (j, &other) in tones.iter().enumerate() {
if j == k {
continue;
}
let theirs = bin(seg, other);
assert!(
mine > 4.0 * theirs,
"packet {p} frame {k}: {hz} Hz is {mine:.5} but {other} Hz is \
{theirs:.5} — this frame is not carrying its own audio"
);
}
}
}
}
#[test]
fn cbr_split_packets_are_exactly_the_requested_size() {
let rate = 48_000i32;
for &channels in &[1usize, 2] {
for &tenth_ms in &[400i32, 600, 800, 1000, 1200] {
for &bitrate in &[32_000i32, 64_000, 128_000] {
let frame = (rate as i64 * tenth_ms as i64 / 10_000) as usize;
let mono = music_like(rate, frame * 8);
let pcm = if channels == 2 {
interleave(&[mono.clone(), mono])
} else {
mono
};
let mut c = Codec::new(rate, channels, Application::Audio)
.bitrate(bitrate)
.frame_samples(frame);
c.enc.rate_control = RateControl::Cbr;
let packets = c.encode_all(&pcm);
let want = ((bitrate as i64 * frame as i64 / rate as i64) as i32 + 4) / 8;
for (i, p) in packets.iter().enumerate() {
assert_eq!(
p.len() as i32,
want,
"{channels}ch {}ms {bitrate} bps packet {i}: CBR asked for \
{want} bytes",
tenth_ms / 10
);
}
}
}
}
}
#[test]
fn dtx_collapses_a_split_packet_without_losing_its_duration() {
let rate = 48_000i32;
for &tenth_ms in &[600i32, 1200] {
let frame = (rate as i64 * tenth_ms as i64 / 10_000) as usize;
let expect_frames = if tenth_ms == 1200 { 2 } else { 1 };
let mut src = music_like(rate, frame * 12);
for s in src.iter_mut().skip(frame * 4).take(frame * 6) {
*s = 0.0;
}
for &cbr in &[false, true] {
let label = format!("{}ms cbr={cbr}", tenth_ms / 10);
let mut c = Codec::new(rate, 1, Application::Voip)
.signal_type(Signal::Voice)
.bandwidth(Bandwidth::Wideband)
.bitrate(24_000)
.frame_samples(frame);
c.enc.use_dtx = true;
c.enc.rate_control = if cbr {
RateControl::Cbr
} else {
RateControl::ConstrainedVbr
};
let r = c.roundtrip(&src);
let dtx: Vec<usize> = r
.packets
.iter()
.enumerate()
.filter(|(_, p)| p.len() == 1)
.map(|(i, _)| i)
.collect();
assert!(
!dtx.is_empty(),
"{label}: DTX never engaged, so nothing here was tested"
);
for (i, p) in r.packets.iter().enumerate() {
assert_eq!(
packet::frame_count(p).unwrap(),
expect_frames,
"{label}: packet {i} ({} bytes) announces the wrong frame count",
p.len()
);
assert_eq!(
packet::frame_count(p).unwrap() * packet::samples_per_frame(p, rate).unwrap(),
frame,
"{label}: packet {i} does not announce {frame} samples"
);
}
assert_eq!(
r.decoded.len(),
r.packets.len() * frame,
"{label}: decoded length does not match the packets"
);
}
}
}
#[test]
fn a_packet_too_small_to_code_still_announces_its_duration() {
let rate = 48_000i32;
for &(tenth_ms, want_len) in &[(1000i32, 6usize), (1200, 8)] {
let frame = (rate as i64 * tenth_ms as i64 / 10_000) as usize;
let src = music_like(rate, frame * 4);
let mut c = Codec::new(rate, 1, Application::Audio)
.bitrate(500)
.frame_samples(frame);
c.enc.rate_control = RateControl::Cbr;
let r = c.roundtrip(&src);
for (i, p) in r.packets.iter().enumerate() {
let label = format!("{}ms at 500 bps, packet {i}", tenth_ms / 10);
assert_eq!(p.len(), want_len, "{label}: wrong packet size");
assert_eq!(
packet::frame_count(p).unwrap() * packet::samples_per_frame(p, rate).unwrap(),
frame,
"{label}: does not announce {frame} samples"
);
}
assert_eq!(r.decoded.len(), r.packets.len() * frame);
assert!(r.decoded.iter().all(|s| s.is_finite()));
}
}