mod common;
use common::*;
use opus_pure::{Application, Bandwidth, Signal};
fn expected_celt_delay(rate: i32) -> usize {
(rate / 400 + rate / 250) as usize
}
fn measure_delay(decoded: &[f32], src: &[f32], max_lag: usize) -> (usize, f32) {
let (corr, lag) = aligned_correlation(decoded, src, max_lag);
(lag, corr)
}
#[test]
fn celt_only_delay_is_the_documented_algorithmic_delay() {
for &rate in &[8_000i32, 12_000, 16_000, 24_000, 48_000] {
let frame = (rate / 50) as usize;
let src = music_like(rate, frame * 100);
let r = Codec::new(rate, 1, Application::Audio)
.signal_type(Signal::Music)
.bitrate(96_000)
.roundtrip(&src);
assert!(
r.modes().iter().all(|m| *m == "celt"),
"{rate} Hz: this configuration must be CELT throughout for the \
delay below to be CELT's"
);
let skip = frame * 5;
let (lag, corr) = measure_delay(&r.decoded[skip..], &src[skip..], frame * 2);
assert!(
corr > 0.99,
"{rate} Hz: correlation {corr:.4} is too low to trust the lag it came \
from; the delay measurement below would be meaningless"
);
assert_eq!(
lag,
expected_celt_delay(rate),
"{rate} Hz: CELT-only output is {lag} samples behind its input, not \
the {} that Fs/400 + Fs/250 asks for. A pre-skip of {} would trim \
the wrong amount.",
expected_celt_delay(rate),
expected_celt_delay(rate)
);
}
}
#[test]
fn the_delay_does_not_move_when_the_mode_does() {
for &(rate, bitrate) in &[(16_000i32, 24_000i32), (24_000, 24_000), (48_000, 24_000)] {
let frame = (rate / 50) as usize;
let half = frame * 60;
let mut src = speech_like(rate, half);
src.extend(music_like(rate, half));
let r = Codec::new(rate, 1, Application::Audio)
.bitrate(bitrate)
.roundtrip(&src);
let modes = r.modes();
let sw = (1..modes.len())
.find(|&k| modes[k] != modes[k - 1])
.unwrap_or_else(|| {
panic!(
"{rate} Hz: this configuration never changes mode, so it \
cannot test what happens when it does"
)
});
let (before, after) = (modes[sw - 1], modes[sw]);
let a = (frame * 10, frame * (sw - 2));
let b = (frame * (sw + 3), frame * 115);
let (lag_a, corr_a) = measure_delay(&r.decoded[a.0..a.1], &src[a.0..a.1], frame * 2);
let (lag_b, corr_b) = measure_delay(&r.decoded[b.0..b.1], &src[b.0..b.1], frame * 2);
assert!(
corr_a > 0.95 && corr_b > 0.95,
"{rate} Hz: correlations {corr_a:.3}/{corr_b:.3} are too low to trust \
the lags they came from"
);
let jump = lag_a as i64 - lag_b as i64;
let allowed = 2;
assert!(
jump.abs() <= allowed,
"{rate} Hz {before}->{after} at packet {sw}: the output jumps {jump} \
samples across the switch ({lag_a} before, {lag_b} after). A listener \
hears that as a skip."
);
}
}
#[test]
fn silk_lands_where_celt_does_at_every_rate() {
for &rate in &[8_000i32, 12_000, 16_000, 24_000, 48_000] {
let frame = (rate / 50) as usize;
let src = speech_like(rate, frame * 150);
let r = Codec::new(rate, 1, Application::Voip)
.bitrate(12_000)
.bandwidth(Bandwidth::Wideband)
.signal_type(Signal::Voice)
.roundtrip(&src);
let modes = r.modes();
assert!(
modes.iter().all(|m| *m == "silk"),
"{rate} Hz: wanted a SILK-only stream to measure, got {modes:?}"
);
let (lag, corr) = measure_delay(&r.decoded, &src, frame * 2);
assert!(
corr > 0.8,
"{rate} Hz: correlation {corr:.4} is too low to trust the lag"
);
let expected = expected_celt_delay(rate) as i64;
let tolerance = 3 + (rate / 24_000) as i64;
let off = lag as i64 - expected;
assert!(
off.abs() <= tolerance,
"{rate} Hz: SILK comes out {lag} samples behind its input where CELT \
comes out {expected}, a difference of {off}. The two layers have to \
land in the same place or a stream that switches mode steps at the \
seam; `delay_matrix_enc` is what equalises them."
);
}
}