use crate::codecs::amr::wb::lp::autocorr::LP_ORDER;
use crate::codecs::amr::wb::lp::isf_codebooks::{
DICO1, DICO2, DICO21, DICO21_36B, DICO22, DICO22_36B, DICO23, DICO23_36B, DICO24, DICO25,
MEAN_ISF,
};
use crate::codecs::amr::wb::lp::isf_dequant::IsfQuantizer;
use crate::fixed_point::arith::{add, mult, sub};
use crate::fixed_point::arith32::{l_add, l_mac, l_sub};
use crate::fixed_point::types::{DspContext, Word16, Word32, MAX_32};
const MU: Word16 = Word16(10923);
pub(crate) const ISF_GAP: Word16 = Word16(128);
const SURVIVORS: usize = 4;
const MAX_SPLIT_DIM: usize = 9;
const MAX_REFINEMENTS: usize = 3;
const MAX_INDICES: usize = 7;
const LOW_DIM: usize = 9;
#[derive(Clone, Copy, Debug)]
struct Split {
stage1: &'static [i16],
dim: usize,
stage2: &'static [(&'static [i16], usize)],
}
const LOW_46: Split = Split {
stage1: &DICO1,
dim: LOW_DIM,
stage2: &[(&DICO21, 3), (&DICO22, 3), (&DICO23, 3)],
};
const HIGH_46: Split = Split {
stage1: &DICO2,
dim: LP_ORDER - LOW_DIM,
stage2: &[(&DICO24, 3), (&DICO25, 4)],
};
const LOW_36: Split = Split {
stage1: &DICO1,
dim: LOW_DIM,
stage2: &[(&DICO21_36B, 5), (&DICO22_36B, 4)],
};
const HIGH_36: Split = Split {
stage1: &DICO2,
dim: LP_ORDER - LOW_DIM,
stage2: &[(&DICO23_36B, 7)],
};
const fn splits(rate: IsfQuantizer) -> (Split, Split) {
match rate {
IsfQuantizer::Bits46 => (LOW_46, HIGH_46),
IsfQuantizer::Bits36 => (LOW_36, HIGH_36),
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct QuantizedIsf {
isf: [Word16; LP_ORDER],
indices: [u16; MAX_INDICES],
count: usize,
}
impl QuantizedIsf {
#[must_use]
pub const fn isf(&self) -> &[Word16; LP_ORDER] {
&self.isf
}
#[must_use]
pub fn indices(&self) -> &[u16] {
&self.indices[..self.count]
}
}
#[derive(Clone, Debug)]
pub struct IsfEncoder {
past: [Word16; LP_ORDER],
}
impl Default for IsfEncoder {
fn default() -> Self {
Self::new()
}
}
impl IsfEncoder {
#[must_use]
pub const fn new() -> Self {
Self {
past: [Word16(0); LP_ORDER],
}
}
pub const fn reset(&mut self) {
self.past = [Word16(0); LP_ORDER];
}
pub fn quantize(&mut self, isf: &[Word16; LP_ORDER], rate: IsfQuantizer) -> QuantizedIsf {
let mut ctx = DspContext::default();
let (low, high) = splits(rate);
let residual = self.residual(&mut ctx, isf);
let mut low_picks = [0u16; MAX_REFINEMENTS];
let mut high_picks = [0u16; MAX_REFINEMENTS];
let low_stage1 = search_split(&mut ctx, &residual[..LOW_DIM], low, &mut low_picks);
let high_stage1 = search_split(&mut ctx, &residual[LOW_DIM..], high, &mut high_picks);
let mut indices = [0u16; MAX_INDICES];
indices[0] = low_stage1;
indices[1] = high_stage1;
let refinements = low.stage2.len() + high.stage2.len();
indices[2..2 + low.stage2.len()].copy_from_slice(&low_picks[..low.stage2.len()]);
indices[2 + low.stage2.len()..2 + refinements]
.copy_from_slice(&high_picks[..high.stage2.len()]);
let isf = self.reconstruct(&mut ctx, rate, &indices);
QuantizedIsf {
isf,
indices,
count: 2 + refinements,
}
}
fn residual(&self, ctx: &mut DspContext, isf: &[Word16; LP_ORDER]) -> [Word16; LP_ORDER] {
let mut residual = [Word16(0); LP_ORDER];
for (i, slot) in residual.iter_mut().enumerate() {
let centred = sub(ctx, isf[i], Word16(MEAN_ISF[i]));
let predicted = mult(ctx, MU, self.past[i]);
*slot = sub(ctx, centred, predicted);
}
residual
}
fn reconstruct(
&mut self,
ctx: &mut DspContext,
rate: IsfQuantizer,
indices: &[u16; MAX_INDICES],
) -> [Word16; LP_ORDER] {
let (low, high) = splits(rate);
let mut isf = [Word16(0); LP_ORDER];
let refinements = low.stage2.len();
place_codevectors(ctx, &mut isf[..LOW_DIM], low, indices[0], &indices[2..]);
place_codevectors(
ctx,
&mut isf[LOW_DIM..],
high,
indices[1],
&indices[2 + refinements..],
);
for (i, slot) in isf.iter_mut().enumerate() {
let residual = *slot;
let centred = add(ctx, residual, Word16(MEAN_ISF[i]));
let predicted = mult(ctx, MU, self.past[i]);
*slot = add(ctx, centred, predicted);
self.past[i] = residual;
}
enforce_min_spacing(ctx, &mut isf);
isf
}
}
fn place_codevectors(
ctx: &mut DspContext,
dst: &mut [Word16],
split: Split,
stage1: u16,
stage2: &[u16],
) {
let base = usize::from(stage1) * split.dim;
for (slot, &value) in dst.iter_mut().zip(&split.stage1[base..base + split.dim]) {
*slot = Word16(value);
}
let mut offset = 0;
for (&(book, dim), &index) in split.stage2.iter().zip(stage2) {
let base = usize::from(index) * dim;
for (slot, &value) in dst[offset..offset + dim]
.iter_mut()
.zip(&book[base..base + dim])
{
*slot = add(ctx, *slot, Word16(value));
}
offset += dim;
}
}
pub(crate) fn enforce_min_spacing(ctx: &mut DspContext, isf: &mut [Word16; LP_ORDER]) {
let mut floor = ISF_GAP;
for slot in isf.iter_mut().take(LP_ORDER - 1) {
if slot.0 < floor.0 {
*slot = floor;
}
floor = add(ctx, *slot, ISF_GAP);
}
}
fn search_split(
ctx: &mut DspContext,
residual: &[Word16],
split: Split,
picks: &mut [u16; MAX_REFINEMENTS],
) -> u16 {
let survivors = n_best_entries(ctx, residual, split.stage1, split.dim);
let mut best_error = Word32(MAX_32);
let mut best_stage1 = 0;
let mut refined = [Word16(0); MAX_SPLIT_DIM];
for &stage1 in &survivors {
let base = stage1 * split.dim;
let stage1_vector = &split.stage1[base..base + split.dim];
for (slot, (&x, &c)) in refined
.iter_mut()
.zip(residual.iter().zip(stage1_vector.iter()))
{
*slot = sub(ctx, x, Word16(c));
}
let mut total = Word32(0);
let mut candidate = [0u16; MAX_REFINEMENTS];
let mut offset = 0;
for (n, (&(book, dim), pick)) in split.stage2.iter().zip(candidate.iter_mut()).enumerate() {
let (index, error) = nearest_entry(ctx, &refined[offset..offset + dim], book);
*pick = index;
total = if n == 0 {
error
} else {
l_add(ctx, total, error)
};
offset += dim;
}
if l_sub(ctx, total, best_error).0 < 0 {
best_error = total;
best_stage1 = stage1;
*picks = candidate;
}
}
u16::try_from(best_stage1).expect("first-stage codebooks hold at most 256 entries")
}
fn n_best_entries(
ctx: &mut DspContext,
target: &[Word16],
book: &[i16],
dim: usize,
) -> [usize; SURVIVORS] {
let mut best = [Word32(MAX_32); SURVIVORS];
let mut index = [0usize; SURVIVORS];
for (slot, seed) in index.iter_mut().zip(0..) {
*slot = seed;
}
for (i, entry) in book.chunks_exact(dim).enumerate() {
let dist = squared_distance(ctx, target, entry);
let slot = best.iter().position(|&d| l_sub(ctx, dist, d).0 < 0);
if let Some(k) = slot {
best.copy_within(k..SURVIVORS - 1, k + 1);
index.copy_within(k..SURVIVORS - 1, k + 1);
best[k] = dist;
index[k] = i;
}
}
index
}
pub(crate) fn nearest_entry(
ctx: &mut DspContext,
target: &[Word16],
book: &[i16],
) -> (u16, Word32) {
let mut best = Word32(MAX_32);
let mut index = 0usize;
for (i, entry) in book.chunks_exact(target.len()).enumerate() {
let dist = squared_distance(ctx, target, entry);
if l_sub(ctx, dist, best).0 < 0 {
best = dist;
index = i;
}
}
(
u16::try_from(index).expect("refinement codebooks hold at most 128 entries"),
best,
)
}
pub(crate) fn squared_distance(ctx: &mut DspContext, target: &[Word16], entry: &[i16]) -> Word32 {
let mut acc = Word32(0);
for (&x, &c) in target.iter().zip(entry) {
let error = sub(ctx, x, Word16(c));
acc = l_mac(ctx, acc, error, error);
}
acc
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codecs::amr::wb::lp::isf_dequant::IsfDecoder;
const TRACE: &str = include_str!("../../testdata/wb_enc_trace.txt");
fn frame_row(frame: usize, name: &str) -> Vec<i32> {
let prefix = format!("T {frame} -1 {name} ");
let line = TRACE
.lines()
.find(|l| l.starts_with(&prefix))
.unwrap_or_else(|| panic!("the trace has no frame {frame} row {name:?}"));
line[prefix.len()..]
.split_whitespace()
.map(|v| v.parse().expect("trace values are integers"))
.collect()
}
fn traced_frames(name: &str) -> usize {
let tail = format!(" -1 {name} ");
TRACE
.lines()
.filter(|l| l.starts_with("T ") && l.contains(&tail))
.count()
}
fn isf_vector(frame: usize, name: &str) -> [Word16; LP_ORDER] {
let row = frame_row(frame, name);
assert_eq!(row.len(), LP_ORDER, "frame {frame} row {name:?} length");
let mut isf = [Word16(0); LP_ORDER];
for (slot, &v) in isf.iter_mut().zip(row.iter()) {
*slot = Word16(i16::try_from(v).expect("ISFs are Word16"));
}
isf
}
fn replay_trace() -> Vec<QuantizedIsf> {
let frames = traced_frames("isf_unq46");
assert!(
frames >= 3,
"the trace holds {frames} frames; fewer than two leaves the predictor untested"
);
let mut enc = IsfEncoder::new();
(0..frames)
.map(|f| enc.quantize(&isf_vector(f, "isf_unq46"), IsfQuantizer::Bits46))
.collect()
}
#[test]
fn the_46_bit_search_picks_the_reference_indices() {
let got = replay_trace();
let mut compared = 0;
for (f, frame) in got.iter().enumerate() {
let want = frame_row(f, "isf_indice46");
assert_eq!(want.len(), 7, "frame {f}: the 46-bit rate transmits seven");
assert_eq!(frame.indices().len(), 7, "frame {f}: indices produced");
for (i, (&mine, &theirs)) in frame.indices().iter().zip(want.iter()).enumerate() {
assert_eq!(
i32::from(mine),
theirs,
"frame {f}: index {i} = {mine} but the reference chose {theirs}"
);
compared += 1;
}
}
assert_eq!(compared, 21, "compared {compared} indices, expected 3 x 7");
}
#[test]
fn the_46_bit_quantizer_reconstructs_the_reference_isfs() {
let got = replay_trace();
let mut compared = 0;
for (f, frame) in got.iter().enumerate() {
let want = frame_row(f, "isf_q");
assert_eq!(want.len(), LP_ORDER, "frame {f}: isf_q length");
for (i, (&mine, &theirs)) in frame.isf().iter().zip(want.iter()).enumerate() {
assert_eq!(
i32::from(mine.0),
theirs,
"frame {f}: isf[{i}] = {} but the reference gives {theirs}",
mine.0
);
compared += 1;
}
}
assert_eq!(
compared,
3 * LP_ORDER,
"compared {compared} ISFs, expected 48"
);
}
#[test]
fn the_predictor_carries_between_frames() {
let sequence = replay_trace();
let mut cold = IsfEncoder::new();
let alone = cold.quantize(&isf_vector(1, "isf_unq46"), IsfQuantizer::Bits46);
assert_ne!(
alone.indices(),
sequence[1].indices(),
"frame 1 quantised cold chose the same indices as frame 1 in sequence, \
so the predictor memory is not reaching the search"
);
}
#[test]
fn a_reset_encoder_repeats_itself() {
let first = replay_trace();
let mut enc = IsfEncoder::new();
enc.quantize(&isf_vector(0, "isf_unq46"), IsfQuantizer::Bits46);
enc.quantize(&isf_vector(1, "isf_unq46"), IsfQuantizer::Bits46);
enc.reset();
let mut compared = 0;
for (f, want) in first.iter().enumerate() {
let got = enc.quantize(&isf_vector(f, "isf_unq46"), IsfQuantizer::Bits46);
assert_eq!(got, *want, "frame {f} differs after a reset");
compared += 1;
}
assert_eq!(compared, first.len(), "compared {compared} frames");
}
#[test]
fn both_rates_reconstruct_what_the_dequantiser_decodes() {
for rate in [IsfQuantizer::Bits46, IsfQuantizer::Bits36] {
let mut enc = IsfEncoder::new();
let mut dec = IsfDecoder::new();
let mut compared = 0;
for f in 0..traced_frames("isf_unq46") {
let out = enc.quantize(&isf_vector(f, "isf_unq46"), rate);
assert_eq!(
out.indices().len(),
rate.index_count(),
"{rate:?} frame {f}: index count"
);
let decoded = dec.decode(rate, out.indices(), false);
for (i, (&mine, &theirs)) in out.isf().iter().zip(decoded.iter()).enumerate() {
assert_eq!(
mine.0, theirs.0,
"{rate:?} frame {f}: encoder has isf[{i}] = {} where the \
dequantiser gives {}",
mine.0, theirs.0
);
compared += 1;
}
}
assert_eq!(compared, 3 * LP_ORDER, "{rate:?}: compared {compared} ISFs");
}
}
const TIED_STAGE1: [i16; 8] = [0, 0, 2, 0, 40, 0, 60, 0];
const FLAT_STAGE2: [i16; 2] = [0, 0];
const TIED_STAGE2: [i16; 4] = [0, 0, 2, 0];
const SPREAD_STAGE1: [i16; 8] = [4, 0, 20, 0, 1000, 0, 2000, 0];
const DISTANT_STAGE2: [i16; 2] = [-20, 0];
#[test]
fn a_first_stage_tie_ranks_the_lower_codebook_index_first() {
let mut ctx = DspContext::default();
let target = [Word16(1), Word16(0)];
let survivors = n_best_entries(&mut ctx, &target, &TIED_STAGE1, 2);
assert_eq!(
survivors,
[0, 1, 2, 3],
"survivors should be distance-ascending then index-ascending"
);
let survivors = n_best_entries(&mut ctx, &[Word16(2), Word16(0)], &TIED_STAGE1, 2);
assert_eq!(survivors, [1, 0, 2, 3]);
}
#[test]
fn a_refinement_tie_goes_to_the_lower_codebook_index() {
let mut ctx = DspContext::default();
let (index, error) = nearest_entry(&mut ctx, &[Word16(1), Word16(0)], &TIED_STAGE2);
assert_eq!(
index, 0,
"an equal distance must not displace the incumbent"
);
assert_eq!(error.0, 2, "twice the squared error of one LSB");
let (index, _) = nearest_entry(&mut ctx, &[Word16(2), Word16(0)], &TIED_STAGE2);
assert_eq!(index, 1, "a strictly nearer later entry must win");
}
#[test]
fn a_saturating_search_keeps_its_seeds() {
let mut ctx = DspContext::default();
let target = [Word16(i16::MAX); MAX_SPLIT_DIM];
let book = [i16::MIN; MAX_SPLIT_DIM * SURVIVORS];
let survivors = n_best_entries(&mut ctx, &target, &book, MAX_SPLIT_DIM);
assert_eq!(survivors, [0, 1, 2, 3]);
let (index, error) = nearest_entry(&mut ctx, &target, &book);
assert_eq!(index, 0);
assert_eq!(
error.0, MAX_32,
"the accumulator saturates rather than wraps"
);
}
#[test]
fn the_survivor_loop_keeps_the_earlier_survivor_on_a_tie() {
let mut ctx = DspContext::default();
let split = Split {
stage1: &TIED_STAGE1,
dim: 2,
stage2: &[(&FLAT_STAGE2, 2)],
};
let mut picks = [0u16; MAX_REFINEMENTS];
let chosen = search_split(&mut ctx, &[Word16(1), Word16(0)], split, &mut picks);
assert_eq!(chosen, 0, "an equal refinement error must keep survivor 0");
assert_eq!(picks[0], 0);
let chosen = search_split(&mut ctx, &[Word16(2), Word16(0)], split, &mut picks);
assert_eq!(chosen, 1);
}
#[test]
fn the_first_stage_distance_stays_out_of_the_objective() {
let mut ctx = DspContext::default();
let split = Split {
stage1: &SPREAD_STAGE1,
dim: 2,
stage2: &[(&DISTANT_STAGE2, 2)],
};
let mut picks = [0u16; MAX_REFINEMENTS];
let chosen = search_split(&mut ctx, &[Word16(0), Word16(0)], split, &mut picks);
assert_eq!(
chosen, 1,
"the survivor with the larger first-stage distance refines better and must win"
);
}
#[test]
fn the_spacing_rule_clamps_the_first_isf_and_spares_the_last() {
let mut ctx = DspContext::default();
let mut isf = [Word16(0); LP_ORDER];
isf[15] = Word16(3);
enforce_min_spacing(&mut ctx, &mut isf);
assert_eq!(
isf[0].0, ISF_GAP.0,
"the floor starts at the gap, not at isf[0]"
);
for (i, slot) in isf.iter().enumerate().take(LP_ORDER - 1).skip(1) {
assert_eq!(
slot.0,
ISF_GAP.0 * i16::try_from(i + 1).expect("fifteen fits"),
"isf[{i}] should have been pushed one gap above its predecessor"
);
}
assert_eq!(
isf[15].0, 3,
"the last ISF carries half the usual scale and is never clamped"
);
}
#[test]
fn the_two_rates_share_a_first_stage_but_not_its_winner() {
let isf = isf_vector(0, "isf_unq46");
let wide = IsfEncoder::new().quantize(&isf, IsfQuantizer::Bits46);
let narrow = IsfEncoder::new().quantize(&isf, IsfQuantizer::Bits36);
assert_eq!(wide.indices().len(), 7);
assert_eq!(narrow.indices().len(), 5);
let mut ctx = DspContext::default();
let residual = IsfEncoder::new().residual(&mut ctx, &isf);
let low = n_best_entries(&mut ctx, &residual[..LOW_DIM], &DICO1, LOW_DIM);
let high = n_best_entries(&mut ctx, &residual[LOW_DIM..], &DICO2, LP_ORDER - LOW_DIM);
let mut checked = 0;
for picked in [wide.indices(), narrow.indices()] {
assert!(
low.contains(&usize::from(picked[0])),
"low-split index {} is not among the survivors {low:?}",
picked[0]
);
assert!(
high.contains(&usize::from(picked[1])),
"high-split index {} is not among the survivors {high:?}",
picked[1]
);
checked += 2;
}
assert_eq!(checked, 4, "checked {checked} first-stage indices");
}
}