use crate::range_decoder::RangeDecoder;
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);
impl SilkFrameHeader {
pub fn decode(rd: &mut RangeDecoder<'_>, cfg: SilkFrameHeaderConfig) -> Result<Self, 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);
let lsf_icdf: &[u8] = match (cfg.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);
}
if rd.has_error() {
return Err(Error::MalformedPacket);
}
Ok(Self {
stereo_pred,
mid_only_flag,
frame_type: frame_type_raw,
signal_type,
qoff_type,
lsf_stage1,
})
}
fn decode_stereo_pred(rd: &mut RangeDecoder<'_>) -> StereoPredictionWeights {
let n = rd.dec_icdf(STEREO_STAGE1_ICDF, 8) as i32;
let i0 = rd.dec_icdf(STEREO_STAGE2_ICDF, 8) as i32;
let i1 = rd.dec_icdf(STEREO_STAGE3_ICDF, 8) as i32;
let i2 = rd.dec_icdf(STEREO_STAGE2_ICDF, 8) as i32;
let i3 = rd.dec_icdf(STEREO_STAGE3_ICDF, 8) as i32;
let wi0 = (i0 + 3 * (n / 5)) as usize;
let wi1 = (i2 + 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 }
}
}
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
);
}
}
}