use crate::range_decoder::RangeDecoder;
use crate::silk_frame::{QuantizationOffsetType, SignalType};
use crate::toc::Bandwidth;
use crate::Error;
pub const SHELL_BLOCK_SAMPLES: usize = 16;
pub const MAX_SHELL_BLOCKS: usize = 20;
pub const MAX_EXCITATION_SAMPLES: usize = MAX_SHELL_BLOCKS * SHELL_BLOCK_SAMPLES;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SilkFrameSize {
TenMs,
TwentyMs,
}
pub fn shell_block_count(bandwidth: Bandwidth, size: SilkFrameSize) -> Result<usize, Error> {
Ok(match (bandwidth, size) {
(Bandwidth::Nb, SilkFrameSize::TenMs) => 5,
(Bandwidth::Mb, SilkFrameSize::TenMs) => 8,
(Bandwidth::Wb, SilkFrameSize::TenMs) => 10,
(Bandwidth::Nb, SilkFrameSize::TwentyMs) => 10,
(Bandwidth::Mb, SilkFrameSize::TwentyMs) => 15,
(Bandwidth::Wb, SilkFrameSize::TwentyMs) => 20,
_ => return Err(Error::MalformedPacket),
})
}
const RATE_LEVEL_ICDF_INACTIVE_UNVOICED: &[u8] = &[241, 190, 178, 132, 87, 74, 41, 14, 0];
const RATE_LEVEL_ICDF_VOICED: &[u8] = &[223, 193, 157, 140, 106, 57, 39, 18, 0];
const PULSE_COUNT_ICDF_L0: &[u8] = &[
125, 51, 26, 18, 15, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
];
const PULSE_COUNT_ICDF_L1: &[u8] = &[
198, 105, 45, 22, 15, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
];
const PULSE_COUNT_ICDF_L2: &[u8] = &[
213, 162, 116, 83, 59, 43, 32, 24, 18, 15, 12, 9, 7, 6, 5, 3, 2, 0,
];
const PULSE_COUNT_ICDF_L3: &[u8] = &[
239, 187, 116, 59, 28, 16, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
];
const PULSE_COUNT_ICDF_L4: &[u8] = &[
250, 229, 188, 135, 86, 51, 30, 19, 13, 10, 8, 6, 5, 4, 3, 2, 1, 0,
];
const PULSE_COUNT_ICDF_L5: &[u8] = &[
249, 235, 213, 185, 156, 128, 103, 83, 66, 53, 42, 33, 26, 21, 17, 13, 10, 0,
];
const PULSE_COUNT_ICDF_L6: &[u8] = &[
254, 249, 235, 206, 164, 118, 77, 46, 27, 16, 10, 7, 5, 4, 3, 2, 1, 0,
];
const PULSE_COUNT_ICDF_L7: &[u8] = &[
255, 253, 249, 239, 220, 191, 156, 119, 85, 57, 37, 23, 15, 10, 6, 4, 2, 0,
];
const PULSE_COUNT_ICDF_L8: &[u8] = &[
255, 253, 251, 246, 237, 223, 203, 179, 152, 124, 98, 75, 55, 40, 29, 21, 15, 0,
];
const PULSE_COUNT_ICDF_L9: &[u8] = &[
255, 254, 253, 247, 220, 162, 106, 67, 42, 28, 18, 12, 9, 6, 4, 3, 2, 0,
];
const PULSE_COUNT_ICDF_L10: &[u8] = &[
254, 253, 247, 220, 162, 106, 67, 42, 28, 18, 12, 9, 6, 4, 3, 2, 0, 0,
];
const PULSE_COUNT_ICDFS: [&[u8]; 11] = [
PULSE_COUNT_ICDF_L0,
PULSE_COUNT_ICDF_L1,
PULSE_COUNT_ICDF_L2,
PULSE_COUNT_ICDF_L3,
PULSE_COUNT_ICDF_L4,
PULSE_COUNT_ICDF_L5,
PULSE_COUNT_ICDF_L6,
PULSE_COUNT_ICDF_L7,
PULSE_COUNT_ICDF_L8,
PULSE_COUNT_ICDF_L9,
PULSE_COUNT_ICDF_L10,
];
const SPLIT16_ICDF_1: &[u8] = &[130, 0];
const SPLIT16_ICDF_2: &[u8] = &[200, 58, 0];
const SPLIT16_ICDF_3: &[u8] = &[231, 130, 26, 0];
const SPLIT16_ICDF_4: &[u8] = &[244, 184, 76, 12, 0];
const SPLIT16_ICDF_5: &[u8] = &[249, 214, 130, 43, 6, 0];
const SPLIT16_ICDF_6: &[u8] = &[252, 232, 173, 87, 24, 3, 0];
const SPLIT16_ICDF_7: &[u8] = &[253, 241, 203, 131, 56, 14, 2, 0];
const SPLIT16_ICDF_8: &[u8] = &[254, 246, 221, 167, 94, 35, 8, 1, 0];
const SPLIT16_ICDF_9: &[u8] = &[254, 249, 232, 193, 130, 67, 25, 7, 1, 0];
const SPLIT16_ICDF_10: &[u8] = &[255, 251, 239, 211, 162, 99, 45, 15, 4, 1, 0];
const SPLIT16_ICDF_11: &[u8] = &[255, 251, 243, 223, 186, 131, 76, 35, 13, 5, 1, 0];
const SPLIT16_ICDF_12: &[u8] = &[255, 252, 245, 230, 202, 158, 105, 57, 24, 8, 2, 1, 0];
const SPLIT16_ICDF_13: &[u8] = &[255, 253, 247, 235, 214, 179, 132, 84, 44, 19, 7, 2, 1, 0];
const SPLIT16_ICDF_14: &[u8] = &[
255, 254, 250, 240, 223, 196, 159, 112, 69, 36, 15, 6, 2, 1, 0,
];
const SPLIT16_ICDF_15: &[u8] = &[
255, 254, 253, 245, 231, 209, 176, 136, 93, 55, 27, 11, 3, 2, 1, 0,
];
const SPLIT16_ICDF_16: &[u8] = &[
255, 254, 253, 252, 239, 221, 194, 158, 117, 76, 42, 18, 4, 3, 2, 1, 0,
];
const SPLIT16_ICDFS: [&[u8]; 16] = [
SPLIT16_ICDF_1,
SPLIT16_ICDF_2,
SPLIT16_ICDF_3,
SPLIT16_ICDF_4,
SPLIT16_ICDF_5,
SPLIT16_ICDF_6,
SPLIT16_ICDF_7,
SPLIT16_ICDF_8,
SPLIT16_ICDF_9,
SPLIT16_ICDF_10,
SPLIT16_ICDF_11,
SPLIT16_ICDF_12,
SPLIT16_ICDF_13,
SPLIT16_ICDF_14,
SPLIT16_ICDF_15,
SPLIT16_ICDF_16,
];
const SPLIT8_ICDF_1: &[u8] = &[129, 0];
const SPLIT8_ICDF_2: &[u8] = &[203, 54, 0];
const SPLIT8_ICDF_3: &[u8] = &[234, 129, 23, 0];
const SPLIT8_ICDF_4: &[u8] = &[245, 184, 73, 10, 0];
const SPLIT8_ICDF_5: &[u8] = &[250, 215, 129, 41, 5, 0];
const SPLIT8_ICDF_6: &[u8] = &[252, 232, 173, 86, 24, 3, 0];
const SPLIT8_ICDF_7: &[u8] = &[253, 240, 200, 129, 56, 15, 2, 0];
const SPLIT8_ICDF_8: &[u8] = &[253, 244, 217, 164, 94, 38, 10, 1, 0];
const SPLIT8_ICDF_9: &[u8] = &[253, 245, 226, 189, 132, 75, 31, 11, 5, 0];
const SPLIT8_ICDF_10: &[u8] = &[253, 246, 231, 203, 159, 105, 56, 23, 6, 1, 0];
const SPLIT8_ICDF_11: &[u8] = &[255, 248, 235, 213, 179, 133, 85, 47, 19, 5, 1, 0];
const SPLIT8_ICDF_12: &[u8] = &[255, 254, 243, 221, 194, 159, 117, 70, 37, 12, 2, 1, 0];
const SPLIT8_ICDF_13: &[u8] = &[255, 254, 248, 234, 208, 171, 128, 85, 48, 22, 8, 2, 1, 0];
const SPLIT8_ICDF_14: &[u8] = &[
255, 254, 250, 240, 220, 189, 149, 107, 67, 36, 16, 6, 2, 1, 0,
];
const SPLIT8_ICDF_15: &[u8] = &[
255, 254, 251, 243, 227, 201, 166, 128, 90, 55, 29, 13, 5, 2, 1, 0,
];
const SPLIT8_ICDF_16: &[u8] = &[
255, 254, 252, 246, 234, 213, 183, 147, 109, 73, 43, 22, 10, 4, 2, 1, 0,
];
const SPLIT8_ICDFS: [&[u8]; 16] = [
SPLIT8_ICDF_1,
SPLIT8_ICDF_2,
SPLIT8_ICDF_3,
SPLIT8_ICDF_4,
SPLIT8_ICDF_5,
SPLIT8_ICDF_6,
SPLIT8_ICDF_7,
SPLIT8_ICDF_8,
SPLIT8_ICDF_9,
SPLIT8_ICDF_10,
SPLIT8_ICDF_11,
SPLIT8_ICDF_12,
SPLIT8_ICDF_13,
SPLIT8_ICDF_14,
SPLIT8_ICDF_15,
SPLIT8_ICDF_16,
];
const SPLIT4_ICDF_1: &[u8] = &[129, 0];
const SPLIT4_ICDF_2: &[u8] = &[207, 50, 0];
const SPLIT4_ICDF_3: &[u8] = &[236, 129, 20, 0];
const SPLIT4_ICDF_4: &[u8] = &[245, 185, 72, 10, 0];
const SPLIT4_ICDF_5: &[u8] = &[249, 213, 129, 42, 6, 0];
const SPLIT4_ICDF_6: &[u8] = &[250, 226, 169, 87, 27, 4, 0];
const SPLIT4_ICDF_7: &[u8] = &[251, 233, 194, 130, 66, 24, 8, 0];
const SPLIT4_ICDF_8: &[u8] = &[250, 236, 207, 160, 99, 47, 17, 3, 0];
const SPLIT4_ICDF_9: &[u8] = &[255, 240, 217, 182, 131, 81, 41, 11, 1, 0];
const SPLIT4_ICDF_10: &[u8] = &[255, 254, 233, 201, 159, 107, 61, 20, 2, 1, 0];
const SPLIT4_ICDF_11: &[u8] = &[255, 249, 233, 206, 170, 128, 86, 50, 23, 7, 1, 0];
const SPLIT4_ICDF_12: &[u8] = &[255, 250, 238, 217, 186, 148, 108, 70, 39, 18, 6, 1, 0];
const SPLIT4_ICDF_13: &[u8] = &[255, 252, 243, 226, 200, 166, 128, 90, 56, 30, 13, 4, 1, 0];
const SPLIT4_ICDF_14: &[u8] = &[
255, 252, 245, 231, 209, 180, 146, 110, 76, 47, 25, 11, 4, 1, 0,
];
const SPLIT4_ICDF_15: &[u8] = &[
255, 253, 248, 237, 219, 194, 163, 128, 93, 62, 37, 19, 8, 3, 1, 0,
];
const SPLIT4_ICDF_16: &[u8] = &[
255, 254, 250, 241, 226, 205, 177, 145, 111, 77, 49, 28, 13, 4, 1, 0, 0,
];
const SPLIT4_ICDFS: [&[u8]; 16] = [
SPLIT4_ICDF_1,
SPLIT4_ICDF_2,
SPLIT4_ICDF_3,
SPLIT4_ICDF_4,
SPLIT4_ICDF_5,
SPLIT4_ICDF_6,
SPLIT4_ICDF_7,
SPLIT4_ICDF_8,
SPLIT4_ICDF_9,
SPLIT4_ICDF_10,
SPLIT4_ICDF_11,
SPLIT4_ICDF_12,
SPLIT4_ICDF_13,
SPLIT4_ICDF_14,
SPLIT4_ICDF_15,
SPLIT4_ICDF_16,
];
const SPLIT2_ICDF_1: &[u8] = &[128, 0];
const SPLIT2_ICDF_2: &[u8] = &[214, 42, 0];
const SPLIT2_ICDF_3: &[u8] = &[235, 128, 21, 0];
const SPLIT2_ICDF_4: &[u8] = &[244, 184, 72, 11, 0];
const SPLIT2_ICDF_5: &[u8] = &[248, 214, 128, 42, 7, 0];
const SPLIT2_ICDF_6: &[u8] = &[248, 225, 170, 80, 25, 5, 0];
const SPLIT2_ICDF_7: &[u8] = &[251, 236, 198, 126, 54, 18, 3, 0];
const SPLIT2_ICDF_8: &[u8] = &[250, 238, 211, 159, 82, 35, 15, 5, 0];
const SPLIT2_ICDF_9: &[u8] = &[250, 231, 203, 168, 128, 88, 53, 25, 6, 0];
const SPLIT2_ICDF_10: &[u8] = &[252, 238, 216, 185, 148, 108, 71, 40, 18, 4, 0];
const SPLIT2_ICDF_11: &[u8] = &[253, 243, 225, 199, 166, 128, 90, 57, 31, 13, 3, 0];
const SPLIT2_ICDF_12: &[u8] = &[254, 246, 233, 212, 183, 147, 109, 73, 44, 23, 10, 2, 0];
const SPLIT2_ICDF_13: &[u8] = &[255, 250, 240, 223, 198, 166, 128, 90, 58, 33, 16, 6, 1, 0];
const SPLIT2_ICDF_14: &[u8] = &[
255, 251, 244, 231, 210, 181, 146, 110, 75, 46, 25, 12, 5, 1, 0,
];
const SPLIT2_ICDF_15: &[u8] = &[
255, 253, 248, 238, 221, 196, 164, 128, 92, 60, 35, 18, 8, 3, 1, 0,
];
const SPLIT2_ICDF_16: &[u8] = &[
255, 253, 249, 242, 229, 208, 180, 146, 110, 76, 48, 27, 14, 7, 3, 1, 0,
];
const SPLIT2_ICDFS: [&[u8]; 16] = [
SPLIT2_ICDF_1,
SPLIT2_ICDF_2,
SPLIT2_ICDF_3,
SPLIT2_ICDF_4,
SPLIT2_ICDF_5,
SPLIT2_ICDF_6,
SPLIT2_ICDF_7,
SPLIT2_ICDF_8,
SPLIT2_ICDF_9,
SPLIT2_ICDF_10,
SPLIT2_ICDF_11,
SPLIT2_ICDF_12,
SPLIT2_ICDF_13,
SPLIT2_ICDF_14,
SPLIT2_ICDF_15,
SPLIT2_ICDF_16,
];
fn split_icdf(partition_size: usize, pulses: u8) -> &'static [u8] {
let p = (pulses as usize) - 1;
match partition_size {
16 => SPLIT16_ICDFS[p],
8 => SPLIT8_ICDFS[p],
4 => SPLIT4_ICDFS[p],
2 => SPLIT2_ICDFS[p],
_ => unreachable!("partition size {partition_size} not 16/8/4/2"),
}
}
const LSB_ICDF: &[u8] = &[120, 0];
const SIGN_ICDF_INACTIVE_LOW: [&[u8]; 7] = [
&[254, 0], &[49, 0], &[67, 0], &[77, 0], &[82, 0], &[93, 0], &[99, 0], ];
const SIGN_ICDF_INACTIVE_HIGH: [&[u8]; 7] = [
&[198, 0], &[11, 0], &[18, 0], &[24, 0], &[31, 0], &[36, 0], &[45, 0], ];
const SIGN_ICDF_UNVOICED_LOW: [&[u8]; 7] = [
&[255, 0], &[46, 0], &[66, 0], &[78, 0], &[87, 0], &[94, 0], &[104, 0], ];
const SIGN_ICDF_UNVOICED_HIGH: [&[u8]; 7] = [
&[208, 0], &[14, 0], &[21, 0], &[32, 0], &[42, 0], &[51, 0], &[66, 0], ];
const SIGN_ICDF_VOICED_LOW: [&[u8]; 7] = [
&[255, 0], &[94, 0], &[104, 0], &[109, 0], &[112, 0], &[115, 0], &[118, 0], ];
const SIGN_ICDF_VOICED_HIGH: [&[u8]; 7] = [
&[248, 0], &[53, 0], &[69, 0], &[80, 0], &[88, 0], &[95, 0], &[102, 0], ];
fn sign_icdf(
signal_type: SignalType,
qoff_type: QuantizationOffsetType,
pulses_in_block: u32,
) -> &'static [u8] {
let bin = pulses_in_block.min(6) as usize;
match (signal_type, qoff_type) {
(SignalType::Inactive, QuantizationOffsetType::Low) => SIGN_ICDF_INACTIVE_LOW[bin],
(SignalType::Inactive, QuantizationOffsetType::High) => SIGN_ICDF_INACTIVE_HIGH[bin],
(SignalType::Unvoiced, QuantizationOffsetType::Low) => SIGN_ICDF_UNVOICED_LOW[bin],
(SignalType::Unvoiced, QuantizationOffsetType::High) => SIGN_ICDF_UNVOICED_HIGH[bin],
(SignalType::Voiced, QuantizationOffsetType::Low) => SIGN_ICDF_VOICED_LOW[bin],
(SignalType::Voiced, QuantizationOffsetType::High) => SIGN_ICDF_VOICED_HIGH[bin],
}
}
pub fn quantization_offset_q23(signal_type: SignalType, qoff_type: QuantizationOffsetType) -> i32 {
match (signal_type, qoff_type) {
(SignalType::Inactive, QuantizationOffsetType::Low) => 25,
(SignalType::Inactive, QuantizationOffsetType::High) => 60,
(SignalType::Unvoiced, QuantizationOffsetType::Low) => 25,
(SignalType::Unvoiced, QuantizationOffsetType::High) => 60,
(SignalType::Voiced, QuantizationOffsetType::Low) => 8,
(SignalType::Voiced, QuantizationOffsetType::High) => 25,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ExcitationConfig {
pub bandwidth: Bandwidth,
pub frame_size: SilkFrameSize,
pub signal_type: SignalType,
pub qoff_type: QuantizationOffsetType,
pub lcg_seed: u8,
}
#[derive(Debug, Clone)]
pub struct Excitation {
e_q23: Vec<i32>,
rate_level: u8,
shell_blocks: usize,
pulses_per_block: Vec<u8>,
lsb_count_per_block: Vec<u8>,
}
impl Excitation {
pub fn decode(rd: &mut RangeDecoder<'_>, cfg: ExcitationConfig) -> Result<Self, Error> {
if cfg.lcg_seed > 3 {
return Err(Error::MalformedPacket);
}
let shell_blocks = shell_block_count(cfg.bandwidth, cfg.frame_size)?;
let total_samples = shell_blocks * SHELL_BLOCK_SAMPLES;
let rate_level_icdf = match cfg.signal_type {
SignalType::Inactive | SignalType::Unvoiced => RATE_LEVEL_ICDF_INACTIVE_UNVOICED,
SignalType::Voiced => RATE_LEVEL_ICDF_VOICED,
};
let rate_level = rd.dec_icdf(rate_level_icdf, 8) as u8;
debug_assert!(rate_level <= 8);
if rd.has_error() {
return Err(Error::MalformedPacket);
}
let mut pulses_per_block = vec![0u8; shell_blocks];
let mut lsb_count_per_block = vec![0u8; shell_blocks];
for block in 0..shell_blocks {
let mut sym = rd.dec_icdf(PULSE_COUNT_ICDFS[rate_level as usize], 8);
let mut lsbs: u32 = 0;
while sym == 17 {
lsbs += 1;
if lsbs >= 10 {
sym = rd.dec_icdf(PULSE_COUNT_ICDFS[10], 8);
if sym == 17 {
return Err(Error::MalformedPacket);
}
break;
} else {
sym = rd.dec_icdf(PULSE_COUNT_ICDFS[9], 8);
}
}
debug_assert!(sym <= 16);
pulses_per_block[block] = sym as u8;
debug_assert!(lsbs <= 10);
lsb_count_per_block[block] = lsbs as u8;
if rd.has_error() {
return Err(Error::MalformedPacket);
}
}
let mut magnitudes = vec![0u32; total_samples];
for (block, &p) in pulses_per_block.iter().enumerate() {
let pulses = p as u32;
if pulses == 0 {
continue;
}
let base = block * SHELL_BLOCK_SAMPLES;
Self::decode_pulse_locations(
rd,
&mut magnitudes[base..base + SHELL_BLOCK_SAMPLES],
16,
pulses,
)?;
}
for (block, &l) in lsb_count_per_block.iter().enumerate() {
let lsbs = l as u32;
if lsbs == 0 {
continue;
}
let base = block * SHELL_BLOCK_SAMPLES;
for slot in magnitudes[base..base + SHELL_BLOCK_SAMPLES].iter_mut() {
let mut mag = *slot;
for _ in 0..lsbs {
let bit = rd.dec_icdf(LSB_ICDF, 8);
mag = (mag << 1) | bit;
}
*slot = mag;
}
if rd.has_error() {
return Err(Error::MalformedPacket);
}
}
let mut signs = vec![1i32; total_samples]; for (block, &p) in pulses_per_block.iter().enumerate() {
let pulses_in_block = p as u32;
let icdf = sign_icdf(cfg.signal_type, cfg.qoff_type, pulses_in_block);
let base = block * SHELL_BLOCK_SAMPLES;
for (mag, sign) in magnitudes[base..base + SHELL_BLOCK_SAMPLES]
.iter()
.zip(signs[base..base + SHELL_BLOCK_SAMPLES].iter_mut())
{
if *mag > 0 {
let s = rd.dec_icdf(icdf, 8);
*sign = if s == 0 { -1 } else { 1 };
}
}
if rd.has_error() {
return Err(Error::MalformedPacket);
}
}
let offset_q23 = quantization_offset_q23(cfg.signal_type, cfg.qoff_type);
let mut e_q23 = vec![0i32; total_samples];
let mut seed: u32 = cfg.lcg_seed as u32;
for i in 0..total_samples {
let mag = magnitudes[i] as i32;
let sign = signs[i];
let e_raw = sign * mag;
let sign_e = if mag == 0 { 0 } else { sign };
let mut e = (e_raw << 8) - sign_e * 20 + offset_q23;
seed = seed.wrapping_mul(196_314_165).wrapping_add(907_633_515);
if (seed & 0x8000_0000) != 0 {
e = -e;
}
seed = seed.wrapping_add(e_raw as u32);
e_q23[i] = e;
}
Ok(Self {
e_q23,
rate_level,
shell_blocks,
pulses_per_block,
lsb_count_per_block,
})
}
fn decode_pulse_locations(
rd: &mut RangeDecoder<'_>,
magnitudes: &mut [u32],
partition_size: usize,
pulses: u32,
) -> Result<(), Error> {
if pulses == 0 {
return Ok(());
}
if partition_size == 1 {
magnitudes[0] = pulses;
return Ok(());
}
let icdf = split_icdf(partition_size, pulses as u8);
let left = rd.dec_icdf(icdf, 8);
if rd.has_error() {
return Err(Error::MalformedPacket);
}
let right = pulses - left;
debug_assert!(left <= pulses);
let half = partition_size / 2;
if left > 0 {
Self::decode_pulse_locations(rd, &mut magnitudes[..half], half, left)?;
}
if right > 0 {
Self::decode_pulse_locations(rd, &mut magnitudes[half..], half, right)?;
}
Ok(())
}
pub fn e_q23(&self) -> &[i32] {
&self.e_q23
}
pub fn samples(&self) -> usize {
self.e_q23.len()
}
pub fn shell_blocks(&self) -> usize {
self.shell_blocks
}
pub fn rate_level(&self) -> u8 {
self.rate_level
}
pub fn pulses_per_block(&self) -> &[u8] {
&self.pulses_per_block
}
pub fn lsb_count_per_block(&self) -> &[u8] {
&self.lsb_count_per_block
}
}
#[cfg(test)]
mod tests {
use super::*;
fn check_icdf(pdf: &[u32], icdf: &[u8]) {
assert_eq!(pdf.iter().sum::<u32>(), 256, "PDF must sum to 256");
assert_eq!(pdf.len(), icdf.len(), "iCDF length must match PDF length");
let mut acc: u32 = 0;
for k in 0..pdf.len() {
acc += pdf[k];
let expected = 256u32.saturating_sub(acc);
assert_eq!(
icdf[k] as u32, expected,
"iCDF mismatch at k={k}: expected {expected}, got {}",
icdf[k]
);
}
}
#[test]
fn table44_shell_block_counts() {
assert_eq!(
shell_block_count(Bandwidth::Nb, SilkFrameSize::TenMs).unwrap(),
5
);
assert_eq!(
shell_block_count(Bandwidth::Mb, SilkFrameSize::TenMs).unwrap(),
8
);
assert_eq!(
shell_block_count(Bandwidth::Wb, SilkFrameSize::TenMs).unwrap(),
10
);
assert_eq!(
shell_block_count(Bandwidth::Nb, SilkFrameSize::TwentyMs).unwrap(),
10
);
assert_eq!(
shell_block_count(Bandwidth::Mb, SilkFrameSize::TwentyMs).unwrap(),
15
);
assert_eq!(
shell_block_count(Bandwidth::Wb, SilkFrameSize::TwentyMs).unwrap(),
20
);
assert!(shell_block_count(Bandwidth::Swb, SilkFrameSize::TwentyMs).is_err());
assert!(shell_block_count(Bandwidth::Fb, SilkFrameSize::TwentyMs).is_err());
}
#[test]
fn table45_rate_level_icdf() {
check_icdf(
&[15, 51, 12, 46, 45, 13, 33, 27, 14],
RATE_LEVEL_ICDF_INACTIVE_UNVOICED,
);
check_icdf(
&[33, 30, 36, 17, 34, 49, 18, 21, 18],
RATE_LEVEL_ICDF_VOICED,
);
}
#[test]
fn table46_pulse_count_icdfs_all_eleven() {
let pdfs: [&[u32]; 11] = [
&[131, 74, 25, 8, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
&[58, 93, 60, 23, 7, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
&[43, 51, 46, 33, 24, 16, 11, 8, 6, 3, 3, 3, 2, 1, 1, 2, 1, 2],
&[17, 52, 71, 57, 31, 12, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
&[6, 21, 41, 53, 49, 35, 21, 11, 6, 3, 2, 2, 1, 1, 1, 1, 1, 1],
&[
7, 14, 22, 28, 29, 28, 25, 20, 17, 13, 11, 9, 7, 5, 4, 4, 3, 10,
],
&[2, 5, 14, 29, 42, 46, 41, 31, 19, 11, 6, 3, 2, 1, 1, 1, 1, 1],
&[
1, 2, 4, 10, 19, 29, 35, 37, 34, 28, 20, 14, 8, 5, 4, 2, 2, 2,
],
&[
1, 2, 2, 5, 9, 14, 20, 24, 27, 28, 26, 23, 20, 15, 11, 8, 6, 15,
],
&[1, 1, 1, 6, 27, 58, 56, 39, 25, 14, 10, 6, 3, 3, 2, 1, 1, 2],
&[2, 1, 6, 27, 58, 56, 39, 25, 14, 10, 6, 3, 3, 2, 1, 1, 2, 0],
];
for (i, pdf) in pdfs.iter().enumerate() {
check_icdf(pdf, PULSE_COUNT_ICDFS[i]);
}
assert_eq!(PULSE_COUNT_ICDFS[10][16], 0);
assert_eq!(PULSE_COUNT_ICDFS[10][17], 0);
}
#[test]
fn table47_split16_one_pulse() {
check_icdf(&[126, 130], SPLIT16_ICDF_1);
}
#[test]
fn table47_split16_eight_pulses() {
check_icdf(&[2, 8, 25, 54, 73, 59, 27, 7, 1], SPLIT16_ICDF_8);
}
#[test]
fn table48_split8_one_pulse() {
check_icdf(&[127, 129], SPLIT8_ICDF_1);
}
#[test]
fn table48_split8_seven_pulses() {
check_icdf(&[3, 13, 40, 71, 73, 41, 13, 2], SPLIT8_ICDF_7);
}
#[test]
fn table49_split4_one_pulse() {
check_icdf(&[127, 129], SPLIT4_ICDF_1);
}
#[test]
fn table49_split4_eleven_pulses() {
check_icdf(
&[1, 6, 16, 27, 36, 42, 42, 36, 27, 16, 6, 1],
SPLIT4_ICDF_11,
);
}
#[test]
fn table50_split2_one_pulse() {
check_icdf(&[128, 128], SPLIT2_ICDF_1);
}
#[test]
fn table50_split2_thirteen_pulses() {
check_icdf(
&[1, 5, 10, 17, 25, 32, 38, 38, 32, 25, 17, 10, 5, 1],
SPLIT2_ICDF_13,
);
}
#[test]
fn table51_lsb_icdf() {
check_icdf(&[136, 120], LSB_ICDF);
}
#[test]
fn table52_sign_icdf_spot_checks() {
check_icdf(&[2, 254], SIGN_ICDF_INACTIVE_LOW[0]);
check_icdf(&[232, 24], SIGN_ICDF_INACTIVE_HIGH[3]);
check_icdf(&[152, 104], SIGN_ICDF_UNVOICED_LOW[6]);
check_icdf(&[235, 21], SIGN_ICDF_UNVOICED_HIGH[2]);
check_icdf(&[144, 112], SIGN_ICDF_VOICED_LOW[4]);
check_icdf(&[8, 248], SIGN_ICDF_VOICED_HIGH[0]);
}
#[test]
fn table52_sign_icdf_pulse_count_clamping() {
let a = sign_icdf(SignalType::Voiced, QuantizationOffsetType::Low, 6);
let b = sign_icdf(SignalType::Voiced, QuantizationOffsetType::Low, 7);
let c = sign_icdf(SignalType::Voiced, QuantizationOffsetType::Low, 16);
assert_eq!(a, b);
assert_eq!(a, c);
}
#[test]
fn table53_quantization_offsets() {
assert_eq!(
quantization_offset_q23(SignalType::Inactive, QuantizationOffsetType::Low),
25
);
assert_eq!(
quantization_offset_q23(SignalType::Inactive, QuantizationOffsetType::High),
60
);
assert_eq!(
quantization_offset_q23(SignalType::Unvoiced, QuantizationOffsetType::Low),
25
);
assert_eq!(
quantization_offset_q23(SignalType::Unvoiced, QuantizationOffsetType::High),
60
);
assert_eq!(
quantization_offset_q23(SignalType::Voiced, QuantizationOffsetType::Low),
8
);
assert_eq!(
quantization_offset_q23(SignalType::Voiced, QuantizationOffsetType::High),
25
);
}
#[test]
fn lcg_recurrence_pinned() {
let mut s: u32 = 0;
s = s.wrapping_mul(196_314_165).wrapping_add(907_633_515);
assert_eq!(s, 907_633_515);
let s2 = s.wrapping_mul(196_314_165).wrapping_add(907_633_515);
let prod = 907_633_515u64.wrapping_mul(196_314_165) & 0xFFFF_FFFF;
let expected = (prod as u32).wrapping_add(907_633_515);
assert_eq!(s2, expected);
}
#[test]
fn rejects_invalid_lcg_seed() {
let buf = [0x55u8, 0xAA, 0x33, 0xCC];
let mut rd = RangeDecoder::new(&buf);
let cfg = ExcitationConfig {
bandwidth: Bandwidth::Nb,
frame_size: SilkFrameSize::TenMs,
signal_type: SignalType::Voiced,
qoff_type: QuantizationOffsetType::Low,
lcg_seed: 4,
};
assert!(Excitation::decode(&mut rd, cfg).is_err());
}
#[test]
fn rejects_swb_fb() {
let buf = [0x55u8; 64];
for bw in [Bandwidth::Swb, Bandwidth::Fb] {
let mut rd = RangeDecoder::new(&buf);
let cfg = ExcitationConfig {
bandwidth: bw,
frame_size: SilkFrameSize::TwentyMs,
signal_type: SignalType::Voiced,
qoff_type: QuantizationOffsetType::Low,
lcg_seed: 0,
};
assert!(Excitation::decode(&mut rd, cfg).is_err());
}
}
#[test]
fn decode_produces_correct_sample_count() {
let buf = [0x55u8; 128];
for (bw, size, expected_samples) in [
(Bandwidth::Nb, SilkFrameSize::TenMs, 80),
(Bandwidth::Mb, SilkFrameSize::TenMs, 128),
(Bandwidth::Wb, SilkFrameSize::TenMs, 160),
(Bandwidth::Nb, SilkFrameSize::TwentyMs, 160),
(Bandwidth::Mb, SilkFrameSize::TwentyMs, 240),
(Bandwidth::Wb, SilkFrameSize::TwentyMs, 320),
] {
let mut rd = RangeDecoder::new(&buf);
let cfg = ExcitationConfig {
bandwidth: bw,
frame_size: size,
signal_type: SignalType::Voiced,
qoff_type: QuantizationOffsetType::Low,
lcg_seed: 1,
};
let exc = Excitation::decode(&mut rd, cfg).unwrap();
assert_eq!(exc.samples(), expected_samples, "bw={bw:?} size={size:?}");
assert_eq!(exc.shell_blocks(), expected_samples / SHELL_BLOCK_SAMPLES);
assert_eq!(exc.pulses_per_block().len(), exc.shell_blocks());
assert_eq!(exc.lsb_count_per_block().len(), exc.shell_blocks());
}
}
#[test]
fn decode_e_q23_fits_in_24_bits() {
let buffers: [&[u8]; 3] = [
&[0x00u8; 64],
&[0xFFu8; 64],
&[
0x5A, 0xC3, 0x17, 0x9E, 0x42, 0xFB, 0x08, 0x71, 0x2D, 0xB6, 0x49, 0x88, 0xE0, 0x21,
0x77, 0xCD, 0x35, 0x9A, 0x6E, 0x04, 0xBB, 0x52, 0xA1, 0xFC, 0x10, 0x83, 0x6F, 0xD4,
0x29, 0x95, 0x4B, 0xC7, 0x1E, 0x80, 0x67, 0xAC, 0x33, 0xD9, 0x06, 0x71, 0x58, 0xE2,
0x4F, 0x90, 0x2B, 0xC4, 0x16, 0x83, 0x6D, 0xD1, 0x28, 0x9E, 0x4A, 0xC0, 0x1F, 0x85,
0x65, 0xAD, 0x32, 0xDF, 0x07, 0x70, 0x55, 0xE1,
],
];
for buf in buffers {
for bw in [Bandwidth::Nb, Bandwidth::Mb, Bandwidth::Wb] {
for size in [SilkFrameSize::TenMs, SilkFrameSize::TwentyMs] {
let mut rd = RangeDecoder::new(buf);
let cfg = ExcitationConfig {
bandwidth: bw,
frame_size: size,
signal_type: SignalType::Voiced,
qoff_type: QuantizationOffsetType::High,
lcg_seed: 2,
};
let exc = Excitation::decode(&mut rd, cfg).unwrap();
for &v in exc.e_q23() {
assert!(
v.abs() <= (1i32 << 23),
"e_Q23 sample {v} exceeds 24-bit range (bw={bw:?} size={size:?})"
);
}
}
}
}
}
#[test]
fn pulse_count_invariants() {
let buf = [
0x10u8, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0,
0xF0, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x9A, 0xAB, 0xBC, 0xCD,
0xDE, 0xEF, 0x00, 0x11,
];
let mut rd = RangeDecoder::new(&buf);
let cfg = ExcitationConfig {
bandwidth: Bandwidth::Wb,
frame_size: SilkFrameSize::TwentyMs,
signal_type: SignalType::Voiced,
qoff_type: QuantizationOffsetType::Low,
lcg_seed: 0,
};
let exc = Excitation::decode(&mut rd, cfg).unwrap();
for &p in exc.pulses_per_block() {
assert!(p <= 16, "pulse count {p} > 16");
}
for &l in exc.lsb_count_per_block() {
assert!(l <= 10, "lsb count {l} > 10");
}
assert!(exc.rate_level() <= 8);
}
#[test]
fn hand_derived_no_pulses_zero_lsbs_path() {
let total_samples = 16usize;
let mut magnitudes = vec![0u32; total_samples];
magnitudes[3] = 5;
let mut signs = vec![1i32; total_samples];
signs[3] = -1;
let offset_q23 = 25; let mut e_q23 = vec![0i32; total_samples];
let mut seed: u32 = 1;
for i in 0..total_samples {
let mag = magnitudes[i] as i32;
let sign = signs[i];
let e_raw = sign * mag;
let sign_e = if mag == 0 { 0 } else { sign };
let mut e = (e_raw << 8) - sign_e * 20 + offset_q23;
seed = seed.wrapping_mul(196_314_165).wrapping_add(907_633_515);
if (seed & 0x8000_0000) != 0 {
e = -e;
}
seed = seed.wrapping_add(e_raw as u32);
e_q23[i] = e;
}
assert_eq!(e_q23[0], 25);
assert!(
e_q23[3] == -1235 || e_q23[3] == 1235,
"e_q23[3] = {} not ±1235",
e_q23[3]
);
}
#[test]
fn zero_magnitude_samples_use_offset_only() {
let buf = [0x00u8; 32]; let mut rd = RangeDecoder::new(&buf);
let cfg = ExcitationConfig {
bandwidth: Bandwidth::Nb,
frame_size: SilkFrameSize::TenMs,
signal_type: SignalType::Inactive,
qoff_type: QuantizationOffsetType::Low,
lcg_seed: 0,
};
let exc = Excitation::decode(&mut rd, cfg).unwrap();
let offset = quantization_offset_q23(SignalType::Inactive, QuantizationOffsetType::Low);
for (b, (&p, &l)) in exc
.pulses_per_block()
.iter()
.zip(exc.lsb_count_per_block().iter())
.enumerate()
{
if p == 0 && l == 0 {
let base = b * SHELL_BLOCK_SAMPLES;
for i in 0..SHELL_BLOCK_SAMPLES {
assert_eq!(
exc.e_q23()[base + i].abs(),
offset,
"block {b} sample {i} should be ±{offset}"
);
}
}
}
}
#[test]
fn reproducible_across_runs() {
let buf = [
0x9A, 0x42, 0x18, 0xC7, 0x6E, 0xB1, 0x05, 0xFD, 0x33, 0x80, 0x55, 0xAC, 0x21, 0xE9,
0x4B, 0x96, 0x0F, 0xD2, 0x68, 0xA7,
];
let cfg = ExcitationConfig {
bandwidth: Bandwidth::Wb,
frame_size: SilkFrameSize::TwentyMs,
signal_type: SignalType::Voiced,
qoff_type: QuantizationOffsetType::High,
lcg_seed: 2,
};
let mut rd1 = RangeDecoder::new(&buf);
let mut rd2 = RangeDecoder::new(&buf);
let e1 = Excitation::decode(&mut rd1, cfg).unwrap();
let e2 = Excitation::decode(&mut rd2, cfg).unwrap();
assert_eq!(e1.e_q23(), e2.e_q23());
assert_eq!(e1.pulses_per_block(), e2.pulses_per_block());
}
#[test]
fn different_lcg_seeds_diverge() {
let buf = [
0x42, 0xC3, 0x17, 0x9F, 0x88, 0x01, 0x55, 0xAA, 0x33, 0xCC, 0x6E, 0x91, 0x04, 0xFD,
0x28, 0xB6,
];
let base_cfg = ExcitationConfig {
bandwidth: Bandwidth::Wb,
frame_size: SilkFrameSize::TwentyMs,
signal_type: SignalType::Voiced,
qoff_type: QuantizationOffsetType::Low,
lcg_seed: 0,
};
let mut rd0 = RangeDecoder::new(&buf);
let e0 = Excitation::decode(&mut rd0, base_cfg).unwrap();
let mut rd1 = RangeDecoder::new(&buf);
let e1 = Excitation::decode(
&mut rd1,
ExcitationConfig {
lcg_seed: 1,
..base_cfg
},
)
.unwrap();
assert_ne!(
e0.e_q23(),
e1.e_q23(),
"different LCG seeds should diverge somewhere across 320 samples"
);
}
#[test]
fn sweep_never_panics() {
let buffers: [&[u8]; 3] = [
&[
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD,
0xEE, 0xFF,
],
&[
0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22,
0x11, 0x00,
],
&[
0x5A, 0xA5, 0x3C, 0xC3, 0x0F, 0xF0, 0x69, 0x96, 0x12, 0x48, 0x84, 0x21, 0x7E, 0xE7,
0xB4, 0x4B,
],
];
for buf in buffers {
for bw in [Bandwidth::Nb, Bandwidth::Mb, Bandwidth::Wb] {
for size in [SilkFrameSize::TenMs, SilkFrameSize::TwentyMs] {
for signal in [
SignalType::Inactive,
SignalType::Unvoiced,
SignalType::Voiced,
] {
for qoff in [QuantizationOffsetType::Low, QuantizationOffsetType::High] {
for seed in 0u8..=3 {
let mut rd = RangeDecoder::new(buf);
let cfg = ExcitationConfig {
bandwidth: bw,
frame_size: size,
signal_type: signal,
qoff_type: qoff,
lcg_seed: seed,
};
let _ = Excitation::decode(&mut rd, cfg);
}
}
}
}
}
}
}
}