mod common;
use common::*;
use opus_pure::Application;
fn fnv1a(bytes: &[u8]) -> u64 {
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
for &b in bytes {
h ^= b as u64;
h = h.wrapping_mul(0x100_0000_01b3);
}
h
}
fn pcm_hash(pcm: &[f32]) -> u64 {
let mut bytes = Vec::with_capacity(pcm.len() * 2);
for &s in pcm {
bytes.extend_from_slice(&((s * 32768.0).round() as i32 as i16).to_le_bytes());
}
fnv1a(&bytes)
}
struct Frozen {
rate: i32,
channels: usize,
bitrate: i32,
label: &'static str,
packets: u64,
pcm: u64,
}
#[rustfmt::skip]
const FROZEN: &[Frozen] = &[
Frozen { rate: 8_000, channels: 1, bitrate: 12_000, label: "nb mono",
packets: 0xea00_5aeb_1f63_7db5, pcm: 0x1350_2545_9e9d_0151 },
Frozen { rate: 8_000, channels: 2, bitrate: 16_000, label: "nb stereo",
packets: 0x4aa2_1e97_2cf4_fc25, pcm: 0xd56e_cb68_4b29_8593 },
Frozen { rate: 12_000, channels: 1, bitrate: 14_000, label: "mb mono",
packets: 0x84d7_97b4_fc7a_029f, pcm: 0xe50d_400b_e8fc_1cec },
Frozen { rate: 12_000, channels: 2, bitrate: 20_000, label: "mb stereo",
packets: 0xe948_3f31_f5ea_890d, pcm: 0x3f9b_ce6a_4a67_1ed5 },
Frozen { rate: 16_000, channels: 1, bitrate: 24_000, label: "wb mono",
packets: 0x0663_41f0_d7c9_3719, pcm: 0x6a32_f1e4_bbe9_8e61 },
Frozen { rate: 16_000, channels: 2, bitrate: 32_000, label: "wb stereo",
packets: 0x39a0_a6eb_a30e_c576, pcm: 0x1036_f681_cc2e_0462 },
];
const FRAMES: usize = 40;
struct FrozenPlc {
rate: i32,
channels: usize,
bitrate: i32,
label: &'static str,
lost: &'static [usize],
pcm: u64,
}
#[rustfmt::skip]
const FROZEN_PLC: &[FrozenPlc] = &[
FrozenPlc { rate: 8_000, channels: 1, bitrate: 12_000, label: "nb mono", lost: &[20],
pcm: 0x7179_2013_1e5d_523b },
FrozenPlc { rate: 8_000, channels: 2, bitrate: 16_000, label: "nb stereo", lost: &[20],
pcm: 0x4e9d_3008_9ac3_efce },
FrozenPlc { rate: 12_000, channels: 1, bitrate: 14_000, label: "mb mono", lost: &[20],
pcm: 0x18f0_14d3_9fdf_ee4d },
FrozenPlc { rate: 12_000, channels: 2, bitrate: 20_000, label: "mb stereo", lost: &[20],
pcm: 0xfde5_411d_19d3_d9c5 },
FrozenPlc { rate: 16_000, channels: 1, bitrate: 24_000, label: "wb mono", lost: &[20],
pcm: 0x6692_fb09_20a4_e7a5 },
FrozenPlc { rate: 16_000, channels: 2, bitrate: 32_000, label: "wb stereo", lost: &[20],
pcm: 0x7697_99df_c6d6_f77b },
FrozenPlc { rate: 16_000, channels: 1, bitrate: 24_000, label: "wb mono burst",
lost: &[10, 11, 12], pcm: 0x3983_73b2_6b79_723b },
FrozenPlc { rate: 16_000, channels: 2, bitrate: 32_000, label: "wb stereo burst",
lost: &[10, 11, 12], pcm: 0xbdc0_0191_c638_1536 },
FrozenPlc { rate: 16_000, channels: 1, bitrate: 24_000, label: "wb mono first packet lost",
lost: &[0], pcm: 0xe569_4339_993b_c653 },
];
struct FrozenDownmix {
label: &'static str,
lost: &'static [usize],
pcm: u64,
}
#[rustfmt::skip]
const FROZEN_DOWNMIX: &[FrozenDownmix] = &[
FrozenDownmix { label: "nb stereo", lost: &[], pcm: 0x5d9b_8420_f779_8143 },
FrozenDownmix { label: "mb stereo", lost: &[], pcm: 0x79fb_ee68_3183_271d },
FrozenDownmix { label: "wb stereo", lost: &[], pcm: 0x6c2f_e6b3_0ad1_df7d },
FrozenDownmix { label: "nb stereo", lost: &[20], pcm: 0x9f0d_7bb7_1b2a_4df5 },
FrozenDownmix { label: "mb stereo", lost: &[20], pcm: 0x3a1f_7f58_a5a2_7860 },
FrozenDownmix { label: "wb stereo", lost: &[20], pcm: 0x5ed3_018e_7580_c73e },
];
fn encode(rate: i32, channels: usize, bitrate: i32) -> Vec<Vec<u8>> {
let frame = (rate / 50) as usize;
let mono = speech_like(rate, frame * FRAMES);
let pcm = if channels == 2 {
interleave(&[mono, music_like(rate, frame * FRAMES)])
} else {
mono
};
Codec::new(rate, channels, Application::Voip)
.bitrate(bitrate)
.roundtrip(&pcm)
.packets
}
#[test]
fn silk_concealment_matches_libopus() {
use opus_pure::OpusDecoder;
for f in FROZEN_PLC {
let frame = (f.rate / 50) as usize;
let packets = encode(f.rate, f.channels, f.bitrate);
let mut dec = OpusDecoder::new(f.rate, f.channels).expect("decoder");
let mut out = vec![0.0f32; frame * f.channels];
let mut pcm = Vec::with_capacity(frame * f.channels * packets.len());
for (i, p) in packets.iter().enumerate() {
let n = if f.lost.contains(&i) {
dec.decode(&[], frame, &mut out).expect("conceal")
} else {
dec.decode(p, frame, &mut out).expect("decode")
};
pcm.extend_from_slice(&out[..n * f.channels]);
}
assert_eq!(
pcm_hash(&pcm),
f.pcm,
"{}: concealing {:?} differs from libopus 1.6.1 concealing the same packets. SILK is fixed point on both sides, so this is exact or it is wrong.",
f.label,
f.lost
);
}
}
#[test]
fn silk_decode_matches_libopus() {
for f in FROZEN {
let frame = (f.rate / 50) as usize;
let mono = speech_like(f.rate, frame * FRAMES);
let pcm = if f.channels == 2 {
interleave(&[mono, music_like(f.rate, frame * FRAMES)])
} else {
mono
};
let r = Codec::new(f.rate, f.channels, Application::Voip)
.bitrate(f.bitrate)
.roundtrip(&pcm);
assert!(
r.modes().iter().all(|m| *m == "silk"),
"{}: every packet must be SILK for this comparison to mean anything",
f.label
);
let mut blob = Vec::new();
for p in &r.packets {
blob.extend_from_slice(&(p.len() as u32).to_le_bytes());
blob.extend_from_slice(p);
}
assert_eq!(
fnv1a(&blob),
f.packets,
"{}: the encoder no longer produces the packets these reference \
values were generated from — regenerate them (see the module docs) \
rather than adjusting the decoder to match",
f.label
);
assert_eq!(
pcm_hash(&r.decoded),
f.pcm,
"{}: decoded output differs from libopus 1.6.1's decode of the same \
packets",
f.label
);
}
}
#[test]
fn silk_stereo_decoded_to_mono_matches_libopus() {
use opus_pure::OpusDecoder;
for d in FROZEN_DOWNMIX {
let f = FROZEN
.iter()
.find(|f| f.label == d.label)
.unwrap_or_else(|| panic!("{}: no such configuration in FROZEN", d.label));
assert_eq!(f.channels, 2, "{}: the stream has to be stereo", d.label);
let frame = (f.rate / 50) as usize;
let packets = encode(f.rate, f.channels, f.bitrate);
let mut dec = OpusDecoder::new(f.rate, 1).expect("decoder");
let mut out = vec![0.0f32; frame];
let mut pcm = Vec::with_capacity(frame * packets.len());
for (i, p) in packets.iter().enumerate() {
let n = if d.lost.contains(&i) {
dec.decode(&[], frame, &mut out).expect("conceal")
} else {
dec.decode(p, frame, &mut out).expect("decode")
};
pcm.extend_from_slice(&out[..n]);
}
assert_eq!(
pcm_hash(&pcm),
d.pcm,
"{} losing {:?}: the mono downmix differs from libopus 1.6.1's. \
SILK is fixed point on both sides, so this is exact or it is wrong.",
d.label,
d.lost
);
}
}
#[test]
fn silk_output_carries_the_resampler_input_delay() {
for &(rate, delay) in &[(8_000i32, 4usize), (12_000, 9), (16_000, 12)] {
let frame = (rate / 50) as usize;
let mut pcm = vec![0.0f32; frame * 8];
pcm[frame] = 0.8;
let r = Codec::new(rate, 1, Application::Voip)
.bitrate(24_000)
.roundtrip(&pcm);
assert!(
r.modes().iter().all(|m| *m == "silk"),
"{rate} Hz: not SILK"
);
assert!(
r.decoded[..delay].iter().all(|&s| s == 0.0),
"{rate} Hz: expected {delay} samples of resampler delay, got a \
non-zero sample inside it"
);
assert!(
r.decoded[delay..frame].iter().any(|&s| s != 0.0),
"{rate} Hz: output is silent past the {delay}-sample delay"
);
}
}
#[test]
#[ignore]
fn dump_reference_packets() {
use std::io::Write;
let dir = "reference/work/plc";
std::fs::create_dir_all(dir).expect("create reference/work/plc");
for f in FROZEN {
let packets = encode(f.rate, f.channels, f.bitrate);
let stem = f.label.replace(' ', "_");
let path = format!("{dir}/{stem}.pkt");
let mut out = std::io::BufWriter::new(std::fs::File::create(&path).expect("create"));
for p in &packets {
out.write_all(&(p.len() as u32).to_le_bytes())
.expect("write");
out.write_all(p).expect("write");
}
out.flush().expect("flush");
println!(
"{path} {} packets {} Hz {} ch frame={}",
packets.len(),
f.rate,
f.channels,
f.rate / 50
);
}
println!("\nlosses to pass as cplc's last argument:");
for f in FROZEN_PLC {
let stem = f.label.replace(' ', "_");
let lost: Vec<String> = f.lost.iter().map(|i| i.to_string()).collect();
println!(" {:<28} {}", stem, lost.join(","));
}
}