use crate::range_decoder::RangeDecoder;
use crate::toc::Bandwidth;
use crate::Error;
pub const D_LPC_NB_MB: usize = 10;
pub const D_LPC_WB: usize = 16;
pub const D_LPC_MAX: usize = D_LPC_WB;
pub const QSTEP_NB_MB_Q16: i32 = 11796;
pub const QSTEP_WB_Q16: i32 = 9830;
const NBMB_STAGE2_ICDF_A: &[u8] = &[255, 254, 253, 238, 14, 3, 2, 1, 0];
const NBMB_STAGE2_ICDF_B: &[u8] = &[255, 254, 252, 218, 35, 3, 2, 1, 0];
const NBMB_STAGE2_ICDF_C: &[u8] = &[255, 254, 250, 208, 59, 4, 2, 1, 0];
const NBMB_STAGE2_ICDF_D: &[u8] = &[255, 254, 246, 194, 71, 10, 2, 1, 0];
const NBMB_STAGE2_ICDF_E: &[u8] = &[255, 252, 236, 183, 82, 8, 2, 1, 0];
const NBMB_STAGE2_ICDF_F: &[u8] = &[255, 252, 235, 180, 90, 17, 2, 1, 0];
const NBMB_STAGE2_ICDF_G: &[u8] = &[255, 248, 224, 171, 97, 30, 4, 1, 0];
const NBMB_STAGE2_ICDF_H: &[u8] = &[255, 254, 236, 173, 95, 37, 7, 1, 0];
const WB_STAGE2_ICDF_I: &[u8] = &[255, 254, 253, 244, 12, 3, 2, 1, 0];
const WB_STAGE2_ICDF_J: &[u8] = &[255, 254, 252, 224, 38, 3, 2, 1, 0];
const WB_STAGE2_ICDF_K: &[u8] = &[255, 254, 251, 209, 57, 4, 2, 1, 0];
const WB_STAGE2_ICDF_L: &[u8] = &[255, 254, 244, 195, 69, 4, 2, 1, 0];
const WB_STAGE2_ICDF_M: &[u8] = &[255, 251, 232, 184, 84, 7, 2, 1, 0];
const WB_STAGE2_ICDF_N: &[u8] = &[255, 254, 240, 186, 86, 14, 2, 1, 0];
const WB_STAGE2_ICDF_O: &[u8] = &[255, 254, 239, 178, 91, 30, 5, 1, 0];
const WB_STAGE2_ICDF_P: &[u8] = &[255, 248, 227, 177, 100, 19, 2, 1, 0];
const STAGE2_EXTENSION_ICDF: &[u8] = &[100, 40, 16, 7, 3, 1, 0];
#[rustfmt::skip]
const NBMB_STAGE2_SELECT: [[u8; D_LPC_NB_MB]; 32] = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 3, 1, 2, 2, 1, 2, 1, 1, 1], [2, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 2, 2, 2, 2, 1, 2, 1, 1, 1], [2, 3, 3, 3, 3, 2, 2, 2, 2, 2], [0, 5, 3, 3, 2, 2, 2, 2, 1, 1], [0, 2, 2, 2, 2, 2, 2, 2, 2, 1], [2, 3, 6, 4, 4, 4, 5, 4, 5, 5], [2, 4, 5, 5, 4, 5, 4, 6, 4, 4], [2, 4, 4, 7, 4, 5, 4, 5, 5, 4], [4, 3, 3, 3, 2, 3, 2, 2, 2, 2], [1, 5, 5, 6, 4, 5, 4, 5, 5, 5], [2, 7, 4, 6, 5, 5, 5, 5, 5, 5], [2, 7, 5, 5, 5, 5, 5, 6, 5, 4], [3, 3, 5, 4, 4, 5, 4, 5, 4, 4], [2, 3, 3, 5, 5, 4, 4, 4, 4, 4], [2, 4, 4, 6, 4, 5, 4, 5, 5, 5], [2, 5, 4, 6, 5, 5, 5, 4, 5, 4], [2, 7, 4, 5, 4, 5, 4, 5, 5, 5], [2, 5, 4, 6, 7, 6, 5, 6, 5, 4], [3, 6, 7, 4, 6, 5, 5, 6, 4, 5], [2, 7, 6, 4, 4, 4, 5, 4, 5, 5], [4, 5, 5, 4, 6, 6, 5, 6, 5, 4], [2, 5, 5, 6, 5, 6, 4, 6, 4, 4], [4, 5, 5, 5, 3, 7, 4, 5, 5, 4], [2, 3, 4, 5, 5, 6, 4, 5, 5, 4], [2, 3, 2, 3, 3, 4, 2, 3, 3, 3], [1, 1, 2, 2, 2, 2, 2, 3, 2, 2], [4, 5, 5, 6, 6, 6, 5, 6, 4, 5], [3, 5, 5, 4, 4, 4, 4, 3, 3, 2], [2, 5, 3, 7, 5, 5, 4, 4, 5, 4], [4, 4, 5, 4, 5, 6, 5, 6, 5, 4], ];
#[rustfmt::skip]
const WB_STAGE2_SELECT: [[u8; D_LPC_WB]; 32] = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 0, 3],
[2, 5, 5, 3, 7, 4, 4, 5, 2, 5, 4, 5, 5, 4, 3, 3],
[0, 2, 1, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1],
[0, 6, 5, 4, 6, 4, 7, 5, 4, 4, 4, 5, 5, 4, 4, 3],
[0, 3, 5, 5, 4, 3, 3, 5, 3, 3, 3, 3, 3, 3, 2, 4],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 2, 6, 3, 7, 2, 5, 3, 4, 5, 5, 4, 3, 3, 2, 3],
[0, 6, 2, 6, 6, 4, 5, 4, 6, 5, 4, 4, 5, 3, 3, 3],
[2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[2, 2, 3, 4, 5, 3, 3, 3, 3, 3, 3, 3, 2, 2, 1, 3],
[2, 2, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 3],
[3, 4, 4, 4, 6, 4, 4, 5, 3, 5, 4, 4, 5, 4, 3, 4],
[0, 6, 4, 5, 4, 7, 5, 2, 6, 5, 7, 4, 4, 3, 5, 3],
[0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0],
[1, 6, 5, 7, 5, 4, 5, 3, 4, 5, 4, 4, 4, 3, 3, 4],
[1, 3, 3, 4, 4, 3, 3, 5, 2, 3, 3, 5, 5, 5, 3, 4],
[2, 3, 3, 2, 2, 2, 3, 2, 1, 2, 1, 2, 1, 1, 1, 4],
[0, 2, 3, 5, 3, 3, 2, 2, 2, 1, 1, 0, 0, 0, 0, 0],
[3, 4, 3, 5, 3, 3, 2, 2, 1, 1, 1, 1, 1, 2, 2, 4],
[2, 6, 3, 7, 7, 4, 5, 4, 5, 3, 5, 3, 3, 2, 3, 3],
[2, 3, 5, 6, 6, 3, 5, 3, 4, 4, 3, 3, 3, 3, 2, 4],
[1, 3, 3, 4, 4, 4, 4, 3, 5, 5, 5, 3, 1, 1, 1, 1],
[2, 5, 3, 6, 6, 4, 7, 4, 4, 5, 3, 4, 4, 3, 3, 3],
[0, 6, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 6, 6, 3, 5, 2, 5, 5, 3, 4, 4, 7, 7, 4, 4, 4],
[3, 3, 7, 3, 5, 4, 3, 3, 3, 2, 2, 3, 3, 3, 2, 3],
[0, 0, 1, 0, 0, 0, 2, 1, 2, 1, 1, 2, 2, 2, 1, 1],
[0, 3, 2, 5, 3, 3, 2, 3, 2, 1, 0, 0, 1, 0, 0, 1],
[3, 5, 5, 4, 7, 5, 3, 3, 2, 3, 2, 2, 1, 0, 1, 0],
[2, 3, 5, 3, 4, 3, 3, 3, 2, 1, 2, 6, 4, 0, 0, 0],
];
const fn nbmb_stage2_icdf(letter: u8) -> &'static [u8] {
match letter {
0 => NBMB_STAGE2_ICDF_A,
1 => NBMB_STAGE2_ICDF_B,
2 => NBMB_STAGE2_ICDF_C,
3 => NBMB_STAGE2_ICDF_D,
4 => NBMB_STAGE2_ICDF_E,
5 => NBMB_STAGE2_ICDF_F,
6 => NBMB_STAGE2_ICDF_G,
7 => NBMB_STAGE2_ICDF_H,
_ => NBMB_STAGE2_ICDF_A,
}
}
const fn wb_stage2_icdf(letter: u8) -> &'static [u8] {
match letter {
0 => WB_STAGE2_ICDF_I,
1 => WB_STAGE2_ICDF_J,
2 => WB_STAGE2_ICDF_K,
3 => WB_STAGE2_ICDF_L,
4 => WB_STAGE2_ICDF_M,
5 => WB_STAGE2_ICDF_N,
6 => WB_STAGE2_ICDF_O,
7 => WB_STAGE2_ICDF_P,
_ => WB_STAGE2_ICDF_I,
}
}
const NBMB_PRED_WEIGHT_A: [u8; 9] = [179, 138, 140, 148, 151, 149, 153, 151, 163];
const NBMB_PRED_WEIGHT_B: [u8; 9] = [116, 67, 82, 59, 92, 72, 100, 89, 92];
const WB_PRED_WEIGHT_C: [u8; 15] = [
175, 148, 160, 176, 178, 173, 174, 164, 177, 174, 196, 182, 198, 192, 182,
];
const WB_PRED_WEIGHT_D: [u8; 15] = [
68, 62, 66, 60, 72, 117, 85, 90, 118, 136, 151, 142, 160, 142, 155,
];
#[rustfmt::skip]
const NBMB_PRED_WEIGHT_SELECT: [[u8; D_LPC_NB_MB - 1]; 32] = [
[0, 1, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 0, 0, 0, 0, 1, 0], [0, 1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0, 0], [1, 0, 1, 1, 0, 0, 0, 1, 0], [0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 1, 0, 1, 1], [0, 0, 1, 1, 0, 0, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 1, 1, 1, 1, 1, 0], [0, 1, 0, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 0], [1, 0, 1, 1, 0, 1, 1, 1, 1], [0, 1, 1, 1, 1, 1, 0, 1, 0], [0, 0, 1, 1, 0, 1, 0, 1, 0], [0, 0, 1, 1, 1, 0, 1, 1, 1], [0, 1, 1, 0, 0, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 0, 1, 0], [0, 1, 1, 0, 0, 1, 0, 1, 0], [0, 1, 1, 0, 0, 0, 1, 1, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1], [0, 0, 1, 1, 0, 0, 0, 1, 1], [0, 0, 0, 1, 0, 1, 1, 1, 1], [0, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 1, 1, 0, 1, 0], [1, 0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 0, 1, 0, 1], [1, 0, 1, 1, 0, 1, 1, 1, 1], ];
#[rustfmt::skip]
const WB_PRED_WEIGHT_SELECT: [[u8; D_LPC_WB - 1]; 32] = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], [0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0], [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0], [0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1], [0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0], [0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0], [0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1], [0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0], [0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0], [0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0], [0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0], ];
struct StageTables {
d_lpc: usize,
qstep: i32,
select_row: &'static [u8],
icdf_lookup: fn(u8) -> &'static [u8],
pred_weight_select_row: &'static [u8],
weight_list_0: &'static [u8],
weight_list_1: &'static [u8],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LsfStage2 {
len: u8,
i2: [i8; D_LPC_MAX],
res_q10: [i32; D_LPC_MAX],
}
impl LsfStage2 {
pub fn decode(
rd: &mut RangeDecoder<'_>,
bandwidth: Bandwidth,
lsf_stage1: u8,
) -> Result<Self, Error> {
if lsf_stage1 >= 32 {
return Err(Error::MalformedPacket);
}
let i1 = lsf_stage1 as usize;
let tables = match bandwidth {
Bandwidth::Nb | Bandwidth::Mb => StageTables {
d_lpc: D_LPC_NB_MB,
qstep: QSTEP_NB_MB_Q16,
select_row: &NBMB_STAGE2_SELECT[i1][..],
icdf_lookup: nbmb_stage2_icdf,
pred_weight_select_row: &NBMB_PRED_WEIGHT_SELECT[i1][..],
weight_list_0: &NBMB_PRED_WEIGHT_A[..],
weight_list_1: &NBMB_PRED_WEIGHT_B[..],
},
Bandwidth::Wb => StageTables {
d_lpc: D_LPC_WB,
qstep: QSTEP_WB_Q16,
select_row: &WB_STAGE2_SELECT[i1][..],
icdf_lookup: wb_stage2_icdf,
pred_weight_select_row: &WB_PRED_WEIGHT_SELECT[i1][..],
weight_list_0: &WB_PRED_WEIGHT_C[..],
weight_list_1: &WB_PRED_WEIGHT_D[..],
},
_ => return Err(Error::MalformedPacket),
};
let StageTables {
d_lpc,
qstep,
select_row,
icdf_lookup,
pred_weight_select_row,
weight_list_0,
weight_list_1,
} = tables;
let mut i2 = [0i8; D_LPC_MAX];
for k in 0..d_lpc {
let letter = select_row[k];
let icdf = icdf_lookup(letter);
let raw = rd.dec_icdf(icdf, 8) as i32;
let mut idx = raw - 4;
if idx == 4 || idx == -4 {
let ext = rd.dec_icdf(STAGE2_EXTENSION_ICDF, 8) as i32;
if idx > 0 {
idx += ext;
} else {
idx -= ext;
}
}
if !(-10..=10).contains(&idx) {
return Err(Error::MalformedPacket);
}
i2[k] = idx as i8;
}
if rd.has_error() {
return Err(Error::MalformedPacket);
}
let mut res_q10 = [0i32; D_LPC_MAX];
for k in (0..d_lpc).rev() {
let i2k = i2[k] as i32;
let sign = match i2k.cmp(&0) {
core::cmp::Ordering::Less => -1,
core::cmp::Ordering::Greater => 1,
core::cmp::Ordering::Equal => 0,
};
let q_contrib = (((i2k << 10) - sign * 102) * qstep) >> 16;
let pred_contrib = if k + 1 < d_lpc {
let pred_q8 =
pred_weight((weight_list_0, weight_list_1), pred_weight_select_row, k);
(res_q10[k + 1] * pred_q8) >> 8
} else {
0
};
res_q10[k] = pred_contrib + q_contrib;
}
Ok(Self {
len: d_lpc as u8,
i2,
res_q10,
})
}
pub fn len(&self) -> usize {
self.len as usize
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn i2(&self) -> &[i8] {
&self.i2[..self.len()]
}
pub fn res_q10(&self) -> &[i32] {
&self.res_q10[..self.len()]
}
}
fn pred_weight(weight_lists: (&[u8], &[u8]), select_row: &[u8], k: usize) -> i32 {
let list = if select_row[k] == 0 {
weight_lists.0
} else {
weight_lists.1
};
list[k] as i32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nbmb_stage2_pdfs_sum_to_256() {
let pdfs: &[(&str, [u8; 9])] = &[
("a", [1, 1, 1, 15, 224, 11, 1, 1, 1]),
("b", [1, 1, 2, 34, 183, 32, 1, 1, 1]),
("c", [1, 1, 4, 42, 149, 55, 2, 1, 1]),
("d", [1, 1, 8, 52, 123, 61, 8, 1, 1]),
("e", [1, 3, 16, 53, 101, 74, 6, 1, 1]),
("f", [1, 3, 17, 55, 90, 73, 15, 1, 1]),
("g", [1, 7, 24, 53, 74, 67, 26, 3, 1]),
("h", [1, 1, 18, 63, 78, 58, 30, 6, 1]),
];
for (label, pdf) in pdfs {
let s: u32 = pdf.iter().map(|&x| x as u32).sum();
assert_eq!(s, 256, "Table 15 codebook {label} PDF sum");
}
}
#[test]
fn wb_stage2_pdfs_sum_to_256() {
let pdfs: &[(&str, [u8; 9])] = &[
("i", [1, 1, 1, 9, 232, 9, 1, 1, 1]),
("j", [1, 1, 2, 28, 186, 35, 1, 1, 1]),
("k", [1, 1, 3, 42, 152, 53, 2, 1, 1]),
("l", [1, 1, 10, 49, 126, 65, 2, 1, 1]),
("m", [1, 4, 19, 48, 100, 77, 5, 1, 1]),
("n", [1, 1, 14, 54, 100, 72, 12, 1, 1]),
("o", [1, 1, 15, 61, 87, 61, 25, 4, 1]),
("p", [1, 7, 21, 50, 77, 81, 17, 1, 1]),
];
for (label, pdf) in pdfs {
let s: u32 = pdf.iter().map(|&x| x as u32).sum();
assert_eq!(s, 256, "Table 16 codebook {label} PDF sum");
}
}
#[test]
fn stage2_icdfs_well_formed() {
let tables: &[(&str, &[u8])] = &[
("a", NBMB_STAGE2_ICDF_A),
("b", NBMB_STAGE2_ICDF_B),
("c", NBMB_STAGE2_ICDF_C),
("d", NBMB_STAGE2_ICDF_D),
("e", NBMB_STAGE2_ICDF_E),
("f", NBMB_STAGE2_ICDF_F),
("g", NBMB_STAGE2_ICDF_G),
("h", NBMB_STAGE2_ICDF_H),
("i", WB_STAGE2_ICDF_I),
("j", WB_STAGE2_ICDF_J),
("k", WB_STAGE2_ICDF_K),
("l", WB_STAGE2_ICDF_L),
("m", WB_STAGE2_ICDF_M),
("n", WB_STAGE2_ICDF_N),
("o", WB_STAGE2_ICDF_O),
("p", WB_STAGE2_ICDF_P),
];
for (label, icdf) in tables {
assert_eq!(icdf.len(), 9, "{label} iCDF length");
assert_eq!(*icdf.last().unwrap(), 0, "{label} iCDF terminator");
for w in icdf.windows(2) {
assert!(w[0] >= w[1], "{label} iCDF must be non-increasing");
}
assert_eq!(icdf[0], 255, "{label} first iCDF cell == 256-PDF[0]");
}
}
#[test]
fn stage2_extension_pdf_self_check() {
let pdf = [156, 60, 24, 9, 4, 2, 1];
let s: u32 = pdf.iter().sum();
assert_eq!(s, 256);
assert_eq!(STAGE2_EXTENSION_ICDF.len(), pdf.len());
let mut acc = 0u32;
for (k, p) in pdf.iter().enumerate() {
acc += *p;
assert_eq!(STAGE2_EXTENSION_ICDF[k] as u32, 256u32.saturating_sub(acc));
}
}
#[test]
fn nbmb_stage2_select_table_well_formed() {
for (i1, row) in NBMB_STAGE2_SELECT.iter().enumerate() {
assert_eq!(row.len(), D_LPC_NB_MB, "Table 17 row {i1} width");
for (k, letter) in row.iter().enumerate() {
assert!(*letter <= 7, "Table 17[{i1}][{k}] = {letter} must be 0..=7");
}
}
}
#[test]
fn wb_stage2_select_table_well_formed() {
for (i1, row) in WB_STAGE2_SELECT.iter().enumerate() {
assert_eq!(row.len(), D_LPC_WB, "Table 18 row {i1} width");
for (k, letter) in row.iter().enumerate() {
assert!(*letter <= 7, "Table 18[{i1}][{k}] = {letter} must be 0..=7");
}
}
}
#[test]
fn nbmb_table17_i1_0_is_all_a() {
assert_eq!(NBMB_STAGE2_SELECT[0], [0u8; D_LPC_NB_MB]);
}
#[test]
fn nbmb_table17_i1_2_spot_check() {
assert_eq!(NBMB_STAGE2_SELECT[2], [2, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
}
#[test]
fn nbmb_table17_i1_6_typo_row() {
assert_eq!(NBMB_STAGE2_SELECT[6], [0, 2, 2, 2, 2, 2, 2, 2, 2, 1]);
}
#[test]
fn wb_table18_i1_0_is_all_i() {
assert_eq!(WB_STAGE2_SELECT[0], [0u8; D_LPC_WB]);
}
#[test]
fn wb_table18_i1_6_is_all_i() {
assert_eq!(WB_STAGE2_SELECT[6], [0u8; D_LPC_WB]);
}
#[test]
fn wb_table18_i1_9_spot_check() {
assert_eq!(
WB_STAGE2_SELECT[9],
[2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
}
#[test]
fn pred_weight_tables_self_check() {
assert_eq!(NBMB_PRED_WEIGHT_A.len(), 9);
assert_eq!(NBMB_PRED_WEIGHT_B.len(), 9);
assert_eq!(WB_PRED_WEIGHT_C.len(), 15);
assert_eq!(WB_PRED_WEIGHT_D.len(), 15);
assert_eq!(NBMB_PRED_WEIGHT_A[0], 179);
assert_eq!(NBMB_PRED_WEIGHT_B[0], 116);
assert_eq!(NBMB_PRED_WEIGHT_A[8], 163);
assert_eq!(NBMB_PRED_WEIGHT_B[8], 92);
assert_eq!(WB_PRED_WEIGHT_C[0], 175);
assert_eq!(WB_PRED_WEIGHT_D[0], 68);
assert_eq!(WB_PRED_WEIGHT_C[14], 182);
assert_eq!(WB_PRED_WEIGHT_D[14], 155);
}
#[test]
fn pred_weight_selection_tables_well_formed() {
for (i1, row) in NBMB_PRED_WEIGHT_SELECT.iter().enumerate() {
assert_eq!(row.len(), D_LPC_NB_MB - 1, "Table 21 row {i1} width");
for sel in row {
assert!(*sel <= 1, "Table 21 entry must be 0 or 1");
}
}
for (i1, row) in WB_PRED_WEIGHT_SELECT.iter().enumerate() {
assert_eq!(row.len(), D_LPC_WB - 1, "Table 22 row {i1} width");
for sel in row {
assert!(*sel <= 1, "Table 22 entry must be 0 or 1");
}
}
}
#[test]
fn table21_row0_spot_check() {
assert_eq!(NBMB_PRED_WEIGHT_SELECT[0], [0, 1, 0, 0, 0, 0, 0, 0, 0]);
}
#[test]
fn table22_row0_spot_check() {
assert_eq!(
WB_PRED_WEIGHT_SELECT[0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
);
}
#[test]
fn table22_row2_spot_check() {
assert_eq!(
WB_PRED_WEIGHT_SELECT[2],
[0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0]
);
}
#[test]
fn pred_weight_resolves_a_vs_b() {
let lists = (&NBMB_PRED_WEIGHT_A[..], &NBMB_PRED_WEIGHT_B[..]);
let select = &NBMB_PRED_WEIGHT_SELECT[0][..];
assert_eq!(pred_weight(lists, select, 0), 179); assert_eq!(pred_weight(lists, select, 1), 67); assert_eq!(pred_weight(lists, select, 2), 140); assert_eq!(pred_weight(lists, select, 8), 163); }
#[test]
fn pred_weight_resolves_c_vs_d() {
let lists = (&WB_PRED_WEIGHT_C[..], &WB_PRED_WEIGHT_D[..]);
let select = &WB_PRED_WEIGHT_SELECT[0][..];
assert_eq!(pred_weight(lists, select, 0), 175); assert_eq!(pred_weight(lists, select, 13), 192); assert_eq!(pred_weight(lists, select, 14), 155); }
fn long_buf() -> [u8; 32] {
[
0x55, 0xAA, 0x33, 0xCC, 0x7F, 0x80, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
0x77, 0x88, 0x99, 0xAA,
]
}
#[test]
fn nb_i1_0_decode_basic() {
let buf = long_buf();
let mut rd = RangeDecoder::new(&buf);
let stage2 = LsfStage2::decode(&mut rd, Bandwidth::Nb, 0).expect("decode");
assert_eq!(stage2.len(), D_LPC_NB_MB);
assert_eq!(stage2.i2().len(), D_LPC_NB_MB);
assert_eq!(stage2.res_q10().len(), D_LPC_NB_MB);
for (k, idx) in stage2.i2().iter().enumerate() {
assert!((-10..=10).contains(idx), "i2[{k}]={idx}");
}
}
#[test]
fn mb_i1_5_decode_basic() {
let buf = long_buf();
let mut rd = RangeDecoder::new(&buf);
let stage2 = LsfStage2::decode(&mut rd, Bandwidth::Mb, 5).expect("decode");
assert_eq!(stage2.len(), D_LPC_NB_MB);
for (k, idx) in stage2.i2().iter().enumerate() {
assert!((-10..=10).contains(idx), "i2[{k}]={idx}");
}
}
#[test]
fn wb_i1_0_decode_basic() {
let buf = long_buf();
let mut rd = RangeDecoder::new(&buf);
let stage2 = LsfStage2::decode(&mut rd, Bandwidth::Wb, 0).expect("decode");
assert_eq!(stage2.len(), D_LPC_WB);
for (k, idx) in stage2.i2().iter().enumerate() {
assert!((-10..=10).contains(idx), "i2[{k}]={idx}");
}
}
#[test]
fn wb_i1_9_decode_basic() {
let buf = long_buf();
let mut rd = RangeDecoder::new(&buf);
let stage2 = LsfStage2::decode(&mut rd, Bandwidth::Wb, 9).expect("decode");
assert_eq!(stage2.len(), D_LPC_WB);
}
#[test]
fn invalid_i1_is_rejected() {
let buf = long_buf();
let mut rd = RangeDecoder::new(&buf);
let err = LsfStage2::decode(&mut rd, Bandwidth::Nb, 32).unwrap_err();
assert_eq!(err, Error::MalformedPacket);
}
#[test]
fn swb_bandwidth_is_rejected() {
let buf = long_buf();
let mut rd = RangeDecoder::new(&buf);
let err = LsfStage2::decode(&mut rd, Bandwidth::Swb, 0).unwrap_err();
assert_eq!(err, Error::MalformedPacket);
}
#[test]
fn fb_bandwidth_is_rejected() {
let buf = long_buf();
let mut rd = RangeDecoder::new(&buf);
let err = LsfStage2::decode(&mut rd, Bandwidth::Fb, 0).unwrap_err();
assert_eq!(err, Error::MalformedPacket);
}
#[test]
fn res_q10_reproduces_formula_nbmb() {
let buf = long_buf();
let mut rd = RangeDecoder::new(&buf);
let stage2 = LsfStage2::decode(&mut rd, Bandwidth::Nb, 7).expect("decode");
let d_lpc = D_LPC_NB_MB;
let qstep = QSTEP_NB_MB_Q16;
let select_row = &NBMB_PRED_WEIGHT_SELECT[7][..];
let lists = (&NBMB_PRED_WEIGHT_A[..], &NBMB_PRED_WEIGHT_B[..]);
let mut expect = [0i32; D_LPC_MAX];
for k in (0..d_lpc).rev() {
let i2k = stage2.i2()[k] as i32;
let sign = match i2k.cmp(&0) {
core::cmp::Ordering::Less => -1,
core::cmp::Ordering::Greater => 1,
core::cmp::Ordering::Equal => 0,
};
let q_contrib = (((i2k << 10) - sign * 102) * qstep) >> 16;
let pred_contrib = if k + 1 < d_lpc {
let p = pred_weight(lists, select_row, k);
(expect[k + 1] * p) >> 8
} else {
0
};
expect[k] = pred_contrib + q_contrib;
}
assert_eq!(stage2.res_q10(), &expect[..d_lpc]);
}
#[test]
fn res_q10_reproduces_formula_wb() {
let buf = long_buf();
let mut rd = RangeDecoder::new(&buf);
let stage2 = LsfStage2::decode(&mut rd, Bandwidth::Wb, 13).expect("decode");
let d_lpc = D_LPC_WB;
let qstep = QSTEP_WB_Q16;
let select_row = &WB_PRED_WEIGHT_SELECT[13][..];
let lists = (&WB_PRED_WEIGHT_C[..], &WB_PRED_WEIGHT_D[..]);
let mut expect = [0i32; D_LPC_MAX];
for k in (0..d_lpc).rev() {
let i2k = stage2.i2()[k] as i32;
let sign = match i2k.cmp(&0) {
core::cmp::Ordering::Less => -1,
core::cmp::Ordering::Greater => 1,
core::cmp::Ordering::Equal => 0,
};
let q_contrib = (((i2k << 10) - sign * 102) * qstep) >> 16;
let pred_contrib = if k + 1 < d_lpc {
let p = pred_weight(lists, select_row, k);
(expect[k + 1] * p) >> 8
} else {
0
};
expect[k] = pred_contrib + q_contrib;
}
assert_eq!(stage2.res_q10(), &expect[..d_lpc]);
}
#[test]
fn sweep_all_i1_nb_wb_decodes() {
let buf = long_buf();
for i1 in 0..32u8 {
for bw in [Bandwidth::Nb, Bandwidth::Mb, Bandwidth::Wb] {
let mut rd = RangeDecoder::new(&buf);
let stage2 = LsfStage2::decode(&mut rd, bw, i1).expect("decode");
for (k, idx) in stage2.i2().iter().enumerate() {
assert!(
(-10..=10).contains(idx),
"bw={bw:?} i1={i1} k={k} idx={idx}"
);
}
let expect_len = match bw {
Bandwidth::Wb => D_LPC_WB,
_ => D_LPC_NB_MB,
};
assert_eq!(stage2.len(), expect_len);
}
}
}
#[test]
fn tell_monotone_through_stage2_decode() {
let buf = long_buf();
let mut rd = RangeDecoder::new(&buf);
let tell0 = rd.tell();
let _ = LsfStage2::decode(&mut rd, Bandwidth::Wb, 4).expect("decode");
let tell1 = rd.tell();
assert!(
tell1 >= tell0,
"tell must not decrease across stage-2 decode: {tell0} -> {tell1}"
);
assert!(tell1 - tell0 > 0, "stage-2 decode should consume bits");
}
}