use super::autocorr::LP_ORDER;
use super::isf_codebooks::{
DICO1, DICO2, DICO21, DICO21_36B, DICO22, DICO22_36B, DICO23, DICO23_36B, DICO24, DICO25,
MEAN_ISF,
};
use crate::fixed_point::arith::{add, mult, round, sub};
use crate::fixed_point::arith32::{l_mac, l_mult};
use crate::fixed_point::shift::shr;
use crate::fixed_point::types::{DspContext, Word16};
const MU: Word16 = Word16(10923);
const ALPHA: Word16 = Word16(29491);
const ONE_ALPHA: Word16 = Word16(3277);
const ISF_GAP: Word16 = Word16(128);
const L_MEANBUF: usize = 3;
pub const ISF_INIT: [i16; LP_ORDER] = [
1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192, 9216, 10240, 11264, 12288, 13312, 14336, 15360,
3840,
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IsfQuantizer {
Bits46,
Bits36,
}
impl IsfQuantizer {
#[must_use]
pub const fn index_count(self) -> usize {
match self {
Self::Bits46 => 7,
Self::Bits36 => 5,
}
}
}
#[derive(Debug, Clone)]
pub struct IsfDecoder {
past_isfq: [Word16; LP_ORDER],
isf_old: [Word16; LP_ORDER],
isf_buf: [[Word16; LP_ORDER]; L_MEANBUF],
}
impl Default for IsfDecoder {
fn default() -> Self {
Self::new()
}
}
impl IsfDecoder {
#[must_use]
pub fn new() -> Self {
let init = ISF_INIT.map(Word16);
Self {
past_isfq: [Word16(0); LP_ORDER],
isf_old: init,
isf_buf: [init; L_MEANBUF],
}
}
pub fn decode(
&mut self,
quantizer: IsfQuantizer,
indices: &[u16],
bad_frame: bool,
) -> [Word16; LP_ORDER] {
let mut ctx = DspContext::default();
let mut isf = if bad_frame {
self.conceal(&mut ctx)
} else {
assert!(
indices.len() >= quantizer.index_count(),
"the {quantizer:?} quantiser needs {} indices, got {}",
quantizer.index_count(),
indices.len()
);
self.decode_good(&mut ctx, quantizer, indices)
};
reorder_isf(&mut ctx, &mut isf, ISF_GAP);
self.isf_old = isf;
isf
}
fn decode_good(
&mut self,
ctx: &mut DspContext,
quantizer: IsfQuantizer,
indices: &[u16],
) -> [Word16; LP_ORDER] {
let mut isf = [Word16(0); LP_ORDER];
copy_entry(&mut isf[0..9], &DICO1, indices[0], 9);
copy_entry(&mut isf[9..16], &DICO2, indices[1], 7);
match quantizer {
IsfQuantizer::Bits46 => {
add_entry(ctx, &mut isf[0..3], &DICO21, indices[2], 3);
add_entry(ctx, &mut isf[3..6], &DICO22, indices[3], 3);
add_entry(ctx, &mut isf[6..9], &DICO23, indices[4], 3);
add_entry(ctx, &mut isf[9..12], &DICO24, indices[5], 3);
add_entry(ctx, &mut isf[12..16], &DICO25, indices[6], 4);
}
IsfQuantizer::Bits36 => {
add_entry(ctx, &mut isf[0..5], &DICO21_36B, indices[2], 5);
add_entry(ctx, &mut isf[5..9], &DICO22_36B, indices[3], 4);
add_entry(ctx, &mut isf[9..16], &DICO23_36B, indices[4], 7);
}
}
for (i, slot) in isf.iter_mut().enumerate() {
let residual = *slot;
let with_mean = add(ctx, residual, Word16(MEAN_ISF[i]));
let predicted = mult(ctx, MU, self.past_isfq[i]);
*slot = add(ctx, with_mean, predicted);
self.past_isfq[i] = residual;
}
self.isf_buf.rotate_right(1);
self.isf_buf[0] = isf;
isf
}
fn conceal(&mut self, ctx: &mut DspContext) -> [Word16; LP_ORDER] {
let mut reference = [Word16(0); LP_ORDER];
for (i, slot) in reference.iter_mut().enumerate() {
let mut acc = l_mult(ctx, Word16(MEAN_ISF[i]), Word16(8192));
for past in &self.isf_buf {
acc = l_mac(ctx, acc, past[i], Word16(8192));
}
*slot = round(ctx, acc);
}
let mut isf = [Word16(0); LP_ORDER];
for (i, slot) in isf.iter_mut().enumerate() {
let held = mult(ctx, ALPHA, self.isf_old[i]);
let pulled = mult(ctx, ONE_ALPHA, reference[i]);
*slot = add(ctx, held, pulled);
}
for i in 0..LP_ORDER {
let carried = mult(ctx, self.past_isfq[i], MU);
let predicted = add(ctx, reference[i], carried);
let estimate = sub(ctx, isf[i], predicted);
self.past_isfq[i] = shr(ctx, estimate, 1);
}
isf
}
}
fn copy_entry(dst: &mut [Word16], book: &[i16], index: u16, dim: usize) {
let base = index as usize * dim;
for (i, slot) in dst.iter_mut().enumerate() {
*slot = Word16(book[base + i]);
}
}
fn add_entry(ctx: &mut DspContext, dst: &mut [Word16], book: &[i16], index: u16, dim: usize) {
let base = index as usize * dim;
for (i, slot) in dst.iter_mut().enumerate() {
*slot = add(ctx, *slot, Word16(book[base + i]));
}
}
fn reorder_isf(ctx: &mut DspContext, isf: &mut [Word16; LP_ORDER], min_dist: Word16) {
let mut floor = min_dist;
for slot in isf.iter_mut().take(LP_ORDER - 1) {
if slot.0 < floor.0 {
*slot = floor;
}
floor = add(ctx, *slot, min_dist);
}
}
#[cfg(test)]
mod tests {
use super::super::isp_to_lp::tests_support::{block_row, has_block};
use super::*;
fn vector(block: &str, label: &str) -> Vec<i16> {
block_row(block, label)
}
fn replay(block: &str, quantizer: IsfQuantizer) {
assert!(has_block(block), "fixture block {block} missing");
let mut dec = IsfDecoder::new();
let mut frames = 0;
for f in 0.. {
let label = format!("ind{f}");
if !super::super::isp_to_lp::tests_support::block_has(block, &label) {
break;
}
frames += 1;
let indices: Vec<u16> = vector(block, &label)
.iter()
.map(|&v| u16::try_from(v).expect("index is non-negative"))
.collect();
assert_eq!(
indices.len(),
quantizer.index_count(),
"frame {f}: index count"
);
let next_exists =
super::super::isp_to_lp::tests_support::block_has(block, &format!("ind{}", f + 1));
let bad = !next_exists;
let got = dec.decode(quantizer, &indices, bad);
let want = vector(block, &format!("isfq{f}"));
for i in 0..LP_ORDER {
assert_eq!(
got[i].0,
want[i],
"{block} frame {f}{}: isf[{i}] = {} but the reference gives {}",
if bad { " (erased)" } else { "" },
got[i].0,
want[i]
);
}
let want_past = vector(block, &format!("past{f}"));
for (i, (&got, &want)) in dec.past_isfq.iter().zip(want_past.iter()).enumerate() {
assert_eq!(
got.0, want,
"{block} frame {f}: past_isfq[{i}] = {} but the reference gives {want}",
got.0
);
}
}
assert!(
frames >= 2,
"{block}: only {frames} frames, prediction untested"
);
}
#[test]
fn the_46_bit_quantizer_is_bit_exact_against_ts26173() {
replay("isfdq0", IsfQuantizer::Bits46);
}
#[test]
fn the_36_bit_quantizer_is_bit_exact_against_ts26173() {
replay("isfdq1", IsfQuantizer::Bits36);
}
#[test]
fn decoded_isfs_keep_their_minimum_spacing() {
let mut dec = IsfDecoder::new();
for f in 0..8u16 {
let indices: Vec<u16> = (0..7).map(|i| (f * 37 + i * 101 + 13) % 32).collect();
let isf = dec.decode(IsfQuantizer::Bits46, &indices, f % 3 == 2);
for i in 1..LP_ORDER - 1 {
assert!(
isf[i].0 - isf[i - 1].0 >= ISF_GAP.0,
"frame {f}: isf[{i}] and isf[{}] are {} apart",
i - 1,
isf[i].0 - isf[i - 1].0
);
}
}
}
#[test]
fn sustained_erasure_decays_toward_the_mean_rather_than_holding() {
let mut dec = IsfDecoder::new();
let indices = [7u16, 19, 3, 11, 29, 5, 13];
for _ in 0..4 {
dec.decode(IsfQuantizer::Bits46, &indices, false);
}
let mut previous = dec.decode(IsfQuantizer::Bits46, &indices, true);
let mut first_step = 0i32;
for n in 0..12 {
let next = dec.decode(IsfQuantizer::Bits46, &indices, true);
let step: i32 = (0..LP_ORDER)
.map(|i| i32::from(next[i].0 - previous[i].0).abs())
.sum();
if n == 0 {
first_step = step;
} else if n == 11 {
assert!(
step < first_step,
"erasure {n} still moves {step}, no less than the first step {first_step}"
);
}
previous = next;
}
}
#[test]
fn a_clean_frame_after_an_erasure_recovers() {
let indices = [7u16, 19, 3, 11, 29, 5, 13];
let mut clean = IsfDecoder::new();
for _ in 0..3 {
clean.decode(IsfQuantizer::Bits46, &indices, false);
}
let undisturbed = clean.decode(IsfQuantizer::Bits46, &indices, false);
let mut disturbed = IsfDecoder::new();
for _ in 0..3 {
disturbed.decode(IsfQuantizer::Bits46, &indices, false);
}
disturbed.decode(IsfQuantizer::Bits46, &indices, true);
let recovered = disturbed.decode(IsfQuantizer::Bits46, &indices, false);
let drift: i32 = (0..LP_ORDER)
.map(|i| i32::from(recovered[i].0 - undisturbed[i].0).abs())
.sum();
assert!(
drift < 4000,
"one erasure left the next good frame {drift} away from an undisturbed decode"
);
}
#[test]
fn the_two_rates_share_their_first_stage() {
let mut a = IsfDecoder::new();
let mut b = IsfDecoder::new();
let wide = [5u16, 9, 0, 0, 0, 0, 0];
let narrow = [5u16, 9, 0, 0, 0];
let from_46 = a.decode(IsfQuantizer::Bits46, &wide, false);
let from_36 = b.decode(IsfQuantizer::Bits36, &narrow, false);
let gap: i32 = (0..LP_ORDER)
.map(|i| i32::from(from_46[i].0 - from_36[i].0).abs())
.sum();
assert!(
gap < 8000,
"the two rates disagree by {gap} on a shared first stage"
);
}
}