use crate::range_decoder::RangeDecoder;
use crate::range_encoder::RangeEncoder;
use crate::toc::Bandwidth;
use crate::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StereoPredictionWeights {
pub w0_q13: i32,
pub w1_q13: i32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SignalType {
Inactive,
Unvoiced,
Voiced,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QuantizationOffsetType {
Low,
High,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FrameKind {
RegularInactive,
RegularActive,
Lbrr,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SilkFrameHeaderConfig {
pub stereo_mid_channel: bool,
pub stereo: bool,
pub has_mid_only_flag: bool,
pub kind: FrameKind,
pub bandwidth: Bandwidth,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SilkFrameHeader {
pub stereo_pred: Option<StereoPredictionWeights>,
pub mid_only_flag: Option<bool>,
pub frame_type: u8,
pub signal_type: SignalType,
pub qoff_type: QuantizationOffsetType,
pub lsf_stage1: u8,
}
const STEREO_STAGE1_ICDF: &[u8] = &[
249, 247, 246, 245, 244, 234, 210, 202, 201, 200, 197, 174, 82, 59, 56, 55, 54, 46, 22, 12, 11,
10, 9, 7, 0,
];
const STEREO_STAGE2_ICDF: &[u8] = &[171, 85, 0];
const STEREO_STAGE3_ICDF: &[u8] = &[205, 154, 102, 51, 0];
const STEREO_WEIGHT_Q13: [i32; 16] = [
-13732, -10050, -8266, -7526, -6500, -5000, -2950, -820, 820, 2950, 5000, 6500, 7526, 8266,
10050, 13732,
];
const MID_ONLY_ICDF: &[u8] = &[64, 0];
const FRAME_TYPE_INACTIVE_ICDF: &[u8] = &[230, 0];
const FRAME_TYPE_ACTIVE_ICDF: &[u8] = &[232, 158, 10, 0];
const LSF_STAGE1_NB_MB_INACTIVE_PDF: [u8; 32] = [
44, 34, 30, 19, 21, 12, 11, 3, 3, 2, 16, 2, 2, 1, 5, 2, 1, 3, 3, 1, 1, 2, 2, 2, 3, 1, 9, 9, 2,
7, 2, 1,
];
const LSF_STAGE1_NB_MB_VOICED_PDF: [u8; 32] = [
1, 10, 1, 8, 3, 8, 8, 14, 13, 14, 1, 14, 12, 13, 11, 11, 12, 11, 10, 10, 11, 8, 9, 8, 7, 8, 1,
1, 6, 1, 6, 5,
];
const LSF_STAGE1_WB_INACTIVE_PDF: [u8; 32] = [
31, 21, 3, 17, 1, 8, 17, 4, 1, 18, 16, 4, 2, 3, 1, 10, 1, 3, 16, 11, 16, 2, 2, 3, 2, 11, 1, 4,
9, 8, 7, 3,
];
const LSF_STAGE1_WB_VOICED_PDF: [u8; 32] = [
1, 4, 16, 5, 18, 11, 5, 14, 15, 1, 3, 12, 13, 14, 14, 6, 14, 12, 2, 6, 1, 12, 12, 11, 10, 3,
10, 5, 1, 1, 1, 3,
];
const fn pdf_to_icdf32(pdf: &[u8; 32]) -> [u8; 33] {
let mut icdf = [0u8; 33];
let mut acc: u32 = 0;
let mut k = 0;
while k < 32 {
acc += pdf[k] as u32;
icdf[k] = (256 - acc) as u8;
k += 1;
}
icdf[32] = 0;
icdf
}
const LSF_STAGE1_ICDF_NB_MB_INACTIVE: [u8; 33] = pdf_to_icdf32(&LSF_STAGE1_NB_MB_INACTIVE_PDF);
const LSF_STAGE1_ICDF_NB_MB_VOICED: [u8; 33] = pdf_to_icdf32(&LSF_STAGE1_NB_MB_VOICED_PDF);
const LSF_STAGE1_ICDF_WB_INACTIVE: [u8; 33] = pdf_to_icdf32(&LSF_STAGE1_WB_INACTIVE_PDF);
const LSF_STAGE1_ICDF_WB_VOICED: [u8; 33] = pdf_to_icdf32(&LSF_STAGE1_WB_VOICED_PDF);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SilkHeaderPreGains {
pub stereo_pred: Option<StereoPredictionWeights>,
pub mid_only_flag: Option<bool>,
pub frame_type: u8,
pub signal_type: SignalType,
pub qoff_type: QuantizationOffsetType,
}
impl SilkFrameHeader {
pub fn decode(rd: &mut RangeDecoder<'_>, cfg: SilkFrameHeaderConfig) -> Result<Self, Error> {
let pre = Self::decode_pre_gains(rd, cfg)?;
let lsf_stage1 = Self::decode_lsf_stage1(rd, cfg.bandwidth, pre.signal_type)?;
if rd.has_error() {
return Err(Error::MalformedPacket);
}
Ok(Self {
stereo_pred: pre.stereo_pred,
mid_only_flag: pre.mid_only_flag,
frame_type: pre.frame_type,
signal_type: pre.signal_type,
qoff_type: pre.qoff_type,
lsf_stage1,
})
}
pub fn decode_pre_gains(
rd: &mut RangeDecoder<'_>,
cfg: SilkFrameHeaderConfig,
) -> Result<SilkHeaderPreGains, Error> {
let stereo_pred = if cfg.stereo && cfg.stereo_mid_channel {
Some(Self::decode_stereo_pred(rd))
} else {
None
};
let mid_only_flag = if cfg.has_mid_only_flag {
let v = rd.dec_icdf(MID_ONLY_ICDF, 8);
Some(v == 1)
} else {
None
};
let frame_type_raw = match cfg.kind {
FrameKind::RegularInactive => {
rd.dec_icdf(FRAME_TYPE_INACTIVE_ICDF, 8) as u8
}
FrameKind::RegularActive | FrameKind::Lbrr => {
let k = rd.dec_icdf(FRAME_TYPE_ACTIVE_ICDF, 8) as u8;
k + 2
}
};
if frame_type_raw > 5 {
return Err(Error::MalformedPacket);
}
let (signal_type, qoff_type) = frame_type_to_signal_qoff(frame_type_raw);
Ok(SilkHeaderPreGains {
stereo_pred,
mid_only_flag,
frame_type: frame_type_raw,
signal_type,
qoff_type,
})
}
pub fn decode_lsf_stage1(
rd: &mut RangeDecoder<'_>,
bandwidth: Bandwidth,
signal_type: SignalType,
) -> Result<u8, Error> {
let lsf_icdf: &[u8] = match (bandwidth, signal_type) {
(Bandwidth::Nb | Bandwidth::Mb, SignalType::Inactive | SignalType::Unvoiced) => {
&LSF_STAGE1_ICDF_NB_MB_INACTIVE
}
(Bandwidth::Nb | Bandwidth::Mb, SignalType::Voiced) => &LSF_STAGE1_ICDF_NB_MB_VOICED,
(Bandwidth::Wb, SignalType::Inactive | SignalType::Unvoiced) => {
&LSF_STAGE1_ICDF_WB_INACTIVE
}
(Bandwidth::Wb, SignalType::Voiced) => &LSF_STAGE1_ICDF_WB_VOICED,
_ => return Err(Error::MalformedPacket),
};
let lsf_stage1 = rd.dec_icdf(lsf_icdf, 8) as u8;
if lsf_stage1 >= 32 {
return Err(Error::MalformedPacket);
}
Ok(lsf_stage1)
}
fn decode_stereo_pred(rd: &mut RangeDecoder<'_>) -> StereoPredictionWeights {
let n = rd.dec_icdf(STEREO_STAGE1_ICDF, 8) as u8;
let i0 = rd.dec_icdf(STEREO_STAGE2_ICDF, 8) as u8;
let i1 = rd.dec_icdf(STEREO_STAGE3_ICDF, 8) as u8;
let i2 = rd.dec_icdf(STEREO_STAGE2_ICDF, 8) as u8;
let i3 = rd.dec_icdf(STEREO_STAGE3_ICDF, 8) as u8;
StereoWeightSymbols { n, i0, i1, i2, i3 }.weights()
}
pub fn encode_pre_gains(
re: &mut RangeEncoder,
cfg: SilkFrameHeaderConfig,
symbols: &SilkHeaderSymbols,
) -> Result<SilkHeaderPreGains, Error> {
let want_stereo = cfg.stereo && cfg.stereo_mid_channel;
if want_stereo != symbols.stereo.is_some() {
return Err(Error::MalformedPacket);
}
let stereo_pred = match &symbols.stereo {
Some(s) => {
if s.n > 24 || s.i0 > 2 || s.i1 > 4 || s.i2 > 2 || s.i3 > 4 {
return Err(Error::MalformedPacket);
}
re.enc_icdf(s.n as usize, STEREO_STAGE1_ICDF, 8);
re.enc_icdf(s.i0 as usize, STEREO_STAGE2_ICDF, 8);
re.enc_icdf(s.i1 as usize, STEREO_STAGE3_ICDF, 8);
re.enc_icdf(s.i2 as usize, STEREO_STAGE2_ICDF, 8);
re.enc_icdf(s.i3 as usize, STEREO_STAGE3_ICDF, 8);
Some(s.weights())
}
None => None,
};
if cfg.has_mid_only_flag != symbols.mid_only_flag.is_some() {
return Err(Error::MalformedPacket);
}
if let Some(flag) = symbols.mid_only_flag {
re.enc_icdf(flag as usize, MID_ONLY_ICDF, 8);
}
match cfg.kind {
FrameKind::RegularInactive => {
if symbols.frame_type > 1 {
return Err(Error::MalformedPacket);
}
re.enc_icdf(symbols.frame_type as usize, FRAME_TYPE_INACTIVE_ICDF, 8);
}
FrameKind::RegularActive | FrameKind::Lbrr => {
if !(2..=5).contains(&symbols.frame_type) {
return Err(Error::MalformedPacket);
}
re.enc_icdf((symbols.frame_type - 2) as usize, FRAME_TYPE_ACTIVE_ICDF, 8);
}
}
let (signal_type, qoff_type) = frame_type_to_signal_qoff(symbols.frame_type);
Ok(SilkHeaderPreGains {
stereo_pred,
mid_only_flag: symbols.mid_only_flag,
frame_type: symbols.frame_type,
signal_type,
qoff_type,
})
}
pub fn encode_lsf_stage1(
re: &mut RangeEncoder,
bandwidth: Bandwidth,
signal_type: SignalType,
lsf_stage1: u8,
) -> Result<(), Error> {
if lsf_stage1 >= 32 {
return Err(Error::MalformedPacket);
}
let lsf_icdf: &[u8] = match (bandwidth, signal_type) {
(Bandwidth::Nb | Bandwidth::Mb, SignalType::Inactive | SignalType::Unvoiced) => {
&LSF_STAGE1_ICDF_NB_MB_INACTIVE
}
(Bandwidth::Nb | Bandwidth::Mb, SignalType::Voiced) => &LSF_STAGE1_ICDF_NB_MB_VOICED,
(Bandwidth::Wb, SignalType::Inactive | SignalType::Unvoiced) => {
&LSF_STAGE1_ICDF_WB_INACTIVE
}
(Bandwidth::Wb, SignalType::Voiced) => &LSF_STAGE1_ICDF_WB_VOICED,
_ => return Err(Error::MalformedPacket),
};
re.enc_icdf(lsf_stage1 as usize, lsf_icdf, 8);
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StereoWeightSymbols {
pub n: u8,
pub i0: u8,
pub i1: u8,
pub i2: u8,
pub i3: u8,
}
impl StereoWeightSymbols {
pub fn weights(&self) -> StereoPredictionWeights {
let n = self.n as i32;
let i1 = self.i1 as i32;
let i3 = self.i3 as i32;
let wi0 = (self.i0 as i32 + 3 * (n / 5)) as usize;
let wi1 = (self.i2 as i32 + 3 * (n % 5)) as usize;
let wi0 = wi0.min(14);
let wi1 = wi1.min(14);
let step1: i32 =
(((STEREO_WEIGHT_Q13[wi1 + 1] - STEREO_WEIGHT_Q13[wi1]) * 6554) >> 16) * (2 * i3 + 1);
let w1_q13 = STEREO_WEIGHT_Q13[wi1] + step1;
let step0: i32 =
(((STEREO_WEIGHT_Q13[wi0 + 1] - STEREO_WEIGHT_Q13[wi0]) * 6554) >> 16) * (2 * i1 + 1);
let w0_q13 = STEREO_WEIGHT_Q13[wi0] + step0 - w1_q13;
StereoPredictionWeights { w0_q13, w1_q13 }
}
pub fn quantize(target: StereoPredictionWeights) -> StereoWeightSymbols {
let mut best = StereoWeightSymbols {
n: 0,
i0: 0,
i1: 0,
i2: 0,
i3: 0,
};
let mut best_err = i64::MAX;
for n in 0..=24u8 {
for i0 in 0..=2u8 {
for i1 in 0..=4u8 {
for i2 in 0..=2u8 {
for i3 in 0..=4u8 {
let cand = StereoWeightSymbols { n, i0, i1, i2, i3 };
let w = cand.weights();
let e0 = (w.w0_q13 - target.w0_q13) as i64;
let e1 = (w.w1_q13 - target.w1_q13) as i64;
let err = e0 * e0 + e1 * e1;
if err < best_err {
best_err = err;
best = cand;
}
}
}
}
}
}
best
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SilkHeaderSymbols {
pub stereo: Option<StereoWeightSymbols>,
pub mid_only_flag: Option<bool>,
pub frame_type: u8,
}
fn frame_type_to_signal_qoff(frame_type: u8) -> (SignalType, QuantizationOffsetType) {
let signal = match frame_type {
0 | 1 => SignalType::Inactive,
2 | 3 => SignalType::Unvoiced,
4 | 5 => SignalType::Voiced,
_ => SignalType::Inactive, };
let qoff = if frame_type % 2 == 0 {
QuantizationOffsetType::Low
} else {
QuantizationOffsetType::High
};
(signal, qoff)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stereo_stage1_pdf_sums_to_256() {
let pdf = [
7, 2, 1, 1, 1, 10, 24, 8, 1, 1, 3, 23, 92, 23, 3, 1, 1, 8, 24, 10, 1, 1, 1, 2, 7,
];
let sum: u32 = pdf.iter().sum();
assert_eq!(sum, 256);
assert_eq!(STEREO_STAGE1_ICDF.len(), pdf.len());
for w in STEREO_STAGE1_ICDF.windows(2) {
assert!(w[0] > w[1] || (w[0] == 0 && w[1] == 0));
}
assert_eq!(*STEREO_STAGE1_ICDF.last().unwrap(), 0);
}
#[test]
fn stereo_stage2_pdf_self_check() {
assert_eq!(STEREO_STAGE2_ICDF, &[171u8, 85, 0]);
assert_eq!(STEREO_STAGE3_ICDF, &[205u8, 154, 102, 51, 0]);
}
#[test]
fn mid_only_pdf_self_check() {
assert_eq!(MID_ONLY_ICDF, &[64u8, 0]);
}
#[test]
fn lsf_stage1_nb_mb_inactive_sums_to_256() {
let s: u32 = LSF_STAGE1_NB_MB_INACTIVE_PDF
.iter()
.map(|&x| x as u32)
.sum();
assert_eq!(s, 256);
}
#[test]
fn lsf_stage1_nb_mb_voiced_sums_to_256() {
let s: u32 = LSF_STAGE1_NB_MB_VOICED_PDF.iter().map(|&x| x as u32).sum();
assert_eq!(s, 256);
}
#[test]
fn lsf_stage1_wb_inactive_sums_to_256() {
let s: u32 = LSF_STAGE1_WB_INACTIVE_PDF.iter().map(|&x| x as u32).sum();
assert_eq!(s, 256);
}
#[test]
fn lsf_stage1_wb_voiced_sums_to_256() {
let s: u32 = LSF_STAGE1_WB_VOICED_PDF.iter().map(|&x| x as u32).sum();
assert_eq!(s, 256);
}
#[test]
fn lsf_stage1_icdf_terminator_is_zero() {
for icdf in [
&LSF_STAGE1_ICDF_NB_MB_INACTIVE,
&LSF_STAGE1_ICDF_NB_MB_VOICED,
&LSF_STAGE1_ICDF_WB_INACTIVE,
&LSF_STAGE1_ICDF_WB_VOICED,
] {
assert_eq!(icdf[32], 0, "iCDF must terminate with zero");
assert_eq!(icdf.len(), 33);
for w in icdf.windows(2) {
assert!(
w[0] >= w[1],
"iCDF must be monotone non-increasing: {:?} -> {:?}",
w[0],
w[1]
);
}
}
}
#[test]
fn stereo_weight_table_is_symmetric() {
for k in 0..8 {
assert_eq!(STEREO_WEIGHT_Q13[15 - k], -STEREO_WEIGHT_Q13[k]);
}
assert_eq!(STEREO_WEIGHT_Q13[0], -13732);
assert_eq!(STEREO_WEIGHT_Q13[15], 13732);
}
#[test]
fn frame_type_to_signal_qoff_table10() {
let expected = [
(0, SignalType::Inactive, QuantizationOffsetType::Low),
(1, SignalType::Inactive, QuantizationOffsetType::High),
(2, SignalType::Unvoiced, QuantizationOffsetType::Low),
(3, SignalType::Unvoiced, QuantizationOffsetType::High),
(4, SignalType::Voiced, QuantizationOffsetType::Low),
(5, SignalType::Voiced, QuantizationOffsetType::High),
];
for (ft, sig, q) in expected {
assert_eq!(frame_type_to_signal_qoff(ft), (sig, q));
}
}
fn mono_inactive_cfg(bw: Bandwidth) -> SilkFrameHeaderConfig {
SilkFrameHeaderConfig {
stereo_mid_channel: false,
stereo: false,
has_mid_only_flag: false,
kind: FrameKind::RegularInactive,
bandwidth: bw,
}
}
fn mono_active_cfg(bw: Bandwidth) -> SilkFrameHeaderConfig {
SilkFrameHeaderConfig {
stereo_mid_channel: false,
stereo: false,
has_mid_only_flag: false,
kind: FrameKind::RegularActive,
bandwidth: bw,
}
}
fn stereo_mid_active_cfg(bw: Bandwidth) -> SilkFrameHeaderConfig {
SilkFrameHeaderConfig {
stereo_mid_channel: true,
stereo: true,
has_mid_only_flag: true,
kind: FrameKind::RegularActive,
bandwidth: bw,
}
}
#[test]
fn mono_inactive_nb_decode_basic() {
let buf = [
0x55, 0xAA, 0x33, 0xCC, 0x7F, 0x80, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
0x12, 0x34,
];
let mut rd = RangeDecoder::new(&buf);
let hdr = SilkFrameHeader::decode(&mut rd, mono_inactive_cfg(Bandwidth::Nb))
.expect("decode must succeed");
assert!(hdr.stereo_pred.is_none());
assert!(hdr.mid_only_flag.is_none());
assert!(hdr.frame_type <= 1, "ft={}", hdr.frame_type);
assert_eq!(hdr.signal_type, SignalType::Inactive);
assert!(hdr.lsf_stage1 < 32);
}
#[test]
fn mono_active_wb_decode_basic() {
let buf = [
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
0x77, 0x88,
];
let mut rd = RangeDecoder::new(&buf);
let hdr = SilkFrameHeader::decode(&mut rd, mono_active_cfg(Bandwidth::Wb))
.expect("decode must succeed");
assert!(hdr.stereo_pred.is_none());
assert!(hdr.mid_only_flag.is_none());
assert!((2..=5).contains(&hdr.frame_type), "ft={}", hdr.frame_type);
assert!(matches!(
hdr.signal_type,
SignalType::Unvoiced | SignalType::Voiced
));
assert!(hdr.lsf_stage1 < 32);
}
#[test]
fn stereo_mid_active_includes_pred_and_mid_only() {
let buf = [
0xC3, 0x18, 0x42, 0x7F, 0x55, 0xAA, 0x33, 0xCC, 0x77, 0x33, 0x11, 0xAA, 0xDE, 0xAD,
0xBE, 0xEF, 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
];
let mut rd = RangeDecoder::new(&buf);
let hdr = SilkFrameHeader::decode(&mut rd, stereo_mid_active_cfg(Bandwidth::Mb))
.expect("decode must succeed");
let pred = hdr.stereo_pred.expect("stereo prediction must be present");
assert!(
(-30_000..=30_000).contains(&pred.w0_q13),
"w0={}",
pred.w0_q13
);
assert!(
(-15_000..=15_000).contains(&pred.w1_q13),
"w1={}",
pred.w1_q13
);
assert!(hdr.mid_only_flag.is_some());
assert!((2..=5).contains(&hdr.frame_type), "ft={}", hdr.frame_type);
assert!(hdr.lsf_stage1 < 32);
}
#[test]
fn stereo_side_no_prediction() {
let cfg = SilkFrameHeaderConfig {
stereo_mid_channel: false,
stereo: true,
has_mid_only_flag: false,
kind: FrameKind::RegularActive,
bandwidth: Bandwidth::Wb,
};
let buf = [
0x37, 0x91, 0xC4, 0x18, 0xA2, 0x5D, 0x6E, 0xFF, 0x77, 0x33, 0x11, 0xAA,
];
let mut rd = RangeDecoder::new(&buf);
let hdr = SilkFrameHeader::decode(&mut rd, cfg).expect("decode must succeed");
assert!(hdr.stereo_pred.is_none());
assert!(hdr.mid_only_flag.is_none());
}
#[test]
fn lbrr_frame_uses_active_pdf() {
let cfg = SilkFrameHeaderConfig {
stereo_mid_channel: false,
stereo: false,
has_mid_only_flag: false,
kind: FrameKind::Lbrr,
bandwidth: Bandwidth::Nb,
};
let buf = [
0x55, 0xAA, 0x33, 0xCC, 0x7F, 0x80, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC,
];
let mut rd = RangeDecoder::new(&buf);
let hdr = SilkFrameHeader::decode(&mut rd, cfg).expect("decode must succeed");
assert!(
(2..=5).contains(&hdr.frame_type),
"lbrr ft must be active: {}",
hdr.frame_type
);
}
#[test]
fn pdf_to_icdf_terminates_and_decreases() {
let pdf = [8u8; 32]; let icdf = pdf_to_icdf32(&pdf);
assert_eq!(icdf[32], 0);
for w in icdf.windows(2) {
assert!(w[0] >= w[1]);
}
assert_eq!(icdf[0], 248);
assert_eq!(icdf[31], 0);
}
#[test]
fn stereo_pred_wi_clamped_in_bounds() {
for seed in 0..32u8 {
let buf = [
seed,
seed.wrapping_mul(3),
seed.wrapping_add(7),
seed ^ 0xA5,
seed.wrapping_mul(11),
seed.wrapping_add(13),
seed ^ 0x5A,
seed.wrapping_mul(17),
seed.wrapping_add(19),
seed ^ 0xC3,
seed.wrapping_mul(23),
seed.wrapping_add(29),
seed ^ 0x3C,
seed.wrapping_mul(31),
seed.wrapping_add(37),
seed ^ 0x55,
];
let mut rd = RangeDecoder::new(&buf);
let pred = SilkFrameHeader::decode_stereo_pred(&mut rd);
assert!(
(-30_000..=30_000).contains(&pred.w0_q13),
"seed={seed}, w0={}",
pred.w0_q13
);
assert!(
(-15_000..=15_000).contains(&pred.w1_q13),
"seed={seed}, w1={}",
pred.w1_q13
);
}
}
struct Lcg(u64);
impl Lcg {
fn next_u32(&mut self) -> u32 {
self.0 = self
.0
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
(self.0 >> 32) as u32
}
fn below(&mut self, n: u32) -> u32 {
self.next_u32() % n
}
}
#[test]
fn header_encode_decode_roundtrip_random_scripts() {
use crate::range_encoder::RangeEncoder;
let mut rng = Lcg(0x00AC_E0F5_EED5);
for _ in 0..500 {
let stereo_mid = rng.below(2) == 0;
let has_mid_only = stereo_mid && rng.below(2) == 0;
let kind = match rng.below(3) {
0 => FrameKind::RegularInactive,
1 => FrameKind::RegularActive,
_ => FrameKind::Lbrr,
};
let bandwidth = match rng.below(3) {
0 => Bandwidth::Nb,
1 => Bandwidth::Mb,
_ => Bandwidth::Wb,
};
let cfg = SilkFrameHeaderConfig {
stereo_mid_channel: stereo_mid,
stereo: stereo_mid,
has_mid_only_flag: has_mid_only,
kind,
bandwidth,
};
let symbols = SilkHeaderSymbols {
stereo: stereo_mid.then(|| StereoWeightSymbols {
n: rng.below(25) as u8,
i0: rng.below(3) as u8,
i1: rng.below(5) as u8,
i2: rng.below(3) as u8,
i3: rng.below(5) as u8,
}),
mid_only_flag: has_mid_only.then(|| rng.below(2) == 1),
frame_type: match kind {
FrameKind::RegularInactive => rng.below(2) as u8,
_ => 2 + rng.below(4) as u8,
},
};
let lsf_stage1 = rng.below(32) as u8;
let mut re = RangeEncoder::new();
let predicted =
SilkFrameHeader::encode_pre_gains(&mut re, cfg, &symbols).expect("encode");
SilkFrameHeader::encode_lsf_stage1(
&mut re,
bandwidth,
predicted.signal_type,
lsf_stage1,
)
.expect("encode lsf1");
let bytes = re.finish();
let mut rd = RangeDecoder::new(&bytes);
let decoded = SilkFrameHeader::decode_pre_gains(&mut rd, cfg).expect("decode");
assert_eq!(decoded, predicted, "symbols={symbols:?}");
let got_lsf1 =
SilkFrameHeader::decode_lsf_stage1(&mut rd, bandwidth, decoded.signal_type)
.expect("decode lsf1");
assert_eq!(got_lsf1, lsf_stage1);
assert!(!rd.has_error());
}
}
#[test]
fn header_encode_rejects_bad_symbols() {
use crate::range_encoder::RangeEncoder;
let mono_cfg = SilkFrameHeaderConfig {
stereo_mid_channel: false,
stereo: false,
has_mid_only_flag: false,
kind: FrameKind::RegularActive,
bandwidth: Bandwidth::Nb,
};
let stereo_syms = SilkHeaderSymbols {
stereo: Some(StereoWeightSymbols {
n: 0,
i0: 0,
i1: 0,
i2: 0,
i3: 0,
}),
mid_only_flag: None,
frame_type: 4,
};
let mut re = RangeEncoder::new();
assert!(SilkFrameHeader::encode_pre_gains(&mut re, mono_cfg, &stereo_syms).is_err());
let mut re = RangeEncoder::new();
let bad_type = SilkHeaderSymbols {
stereo: None,
mid_only_flag: None,
frame_type: 1,
};
assert!(SilkFrameHeader::encode_pre_gains(&mut re, mono_cfg, &bad_type).is_err());
let mut re = RangeEncoder::new();
let stereo_cfg = SilkFrameHeaderConfig {
stereo_mid_channel: true,
stereo: true,
has_mid_only_flag: false,
kind: FrameKind::RegularActive,
bandwidth: Bandwidth::Nb,
};
let bad_stereo = SilkHeaderSymbols {
stereo: Some(StereoWeightSymbols {
n: 25,
i0: 0,
i1: 0,
i2: 0,
i3: 0,
}),
mid_only_flag: None,
frame_type: 4,
};
assert!(SilkFrameHeader::encode_pre_gains(&mut re, stereo_cfg, &bad_stereo).is_err());
let mut re = RangeEncoder::new();
assert!(
SilkFrameHeader::encode_lsf_stage1(&mut re, Bandwidth::Nb, SignalType::Voiced, 32)
.is_err()
);
let mut re = RangeEncoder::new();
assert!(
SilkFrameHeader::encode_lsf_stage1(&mut re, Bandwidth::Swb, SignalType::Voiced, 0)
.is_err()
);
}
fn all_weight_quintuples() -> Vec<StereoWeightSymbols> {
let mut v = Vec::with_capacity(5625);
for n in 0..=24u8 {
for i0 in 0..=2u8 {
for i1 in 0..=4u8 {
for i2 in 0..=2u8 {
for i3 in 0..=4u8 {
v.push(StereoWeightSymbols { n, i0, i1, i2, i3 });
}
}
}
}
}
v
}
#[test]
fn stereo_weight_quantize_exact_on_representable_targets() {
let all = all_weight_quintuples();
for q in all.iter().step_by(7) {
let target = q.weights();
let quant = StereoWeightSymbols::quantize(target);
assert_eq!(
quant.weights(),
target,
"quintuple {q:?} did not roundtrip through quantize"
);
}
}
#[test]
fn stereo_weight_quantize_is_argmin_and_deterministic() {
let all = all_weight_quintuples();
let err = |w: StereoPredictionWeights, t: StereoPredictionWeights| -> i64 {
let e0 = (w.w0_q13 - t.w0_q13) as i64;
let e1 = (w.w1_q13 - t.w1_q13) as i64;
e0 * e0 + e1 * e1
};
let mut s = 0x5EED_0385u64;
let mut next = move || {
s = s
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
((s >> 33) as i32 % 60001) - 30000
};
for _ in 0..25 {
let target = StereoPredictionWeights {
w0_q13: next(),
w1_q13: next(),
};
let quant = StereoWeightSymbols::quantize(target);
let best = err(quant.weights(), target);
for cand in &all {
assert!(
err(cand.weights(), target) >= best,
"candidate {cand:?} beats quantize({target:?}) = {quant:?}"
);
}
assert_eq!(StereoWeightSymbols::quantize(target), quant);
}
}
#[test]
fn stereo_weight_quantize_saturates_at_extremes() {
let all = all_weight_quintuples();
let max_w1 = all.iter().map(|q| q.weights().w1_q13).max().unwrap();
let min_w1 = all.iter().map(|q| q.weights().w1_q13).min().unwrap();
let hi = StereoWeightSymbols::quantize(StereoPredictionWeights {
w0_q13: 0,
w1_q13: 1 << 20,
});
assert_eq!(hi.weights().w1_q13, max_w1);
let lo = StereoWeightSymbols::quantize(StereoPredictionWeights {
w0_q13: 0,
w1_q13: -(1 << 20),
});
assert_eq!(lo.weights().w1_q13, min_w1);
}
}