use crate::SUBFRAME;
use crate::fixed::{acc, hi, shift};
const CLASSES: usize = 6;
const ALT_CLASS: usize = 5;
const ALT_PULSES: usize = 5;
const ALT_AMPLITUDE: i16 = 8192;
const SHAPE_LEN: usize = 20;
const POSITIONS: usize = 40;
const MAX_PULSES: usize = 3;
pub struct Innovation {
pub code: [i16; SUBFRAME],
pub class: usize,
}
fn classify(index: u16) -> (usize, u16) {
let index = index.min(crate::tables::FCB_INDEX_LIMIT[0] - 1);
let class = (0..CLASSES)
.rev()
.find(|&k| index >= crate::tables::FCB_CLASS_BASE[k])
.unwrap_or(0);
(class, index)
}
fn decompose(class: usize, index: u16, pulses: usize) -> ([i16; MAX_PULSES], [i16; MAX_PULSES]) {
let mut rest = (index as i64) - (crate::tables::FCB_CLASS_BASE[class] as i64);
let mut signs = [0i16; MAX_PULSES];
for s in signs.iter_mut().take(pulses) {
*s = (rest & 1) as i16;
rest = shift(rest, -1);
}
let mut positions = [0i16; MAX_PULSES];
let mut value = rest << 16;
for k in (0..pulses).rev() {
let recip = crate::tables::FCB_RADIX_INV[MAX_PULSES * class + k];
let radix = crate::tables::FCB_RADIX[MAX_PULSES * class + k];
let quotient = hi(shift(acc((hi(value) as i64) * (recip as i64) * 2), -2));
value = acc(value - (((quotient as i64) * (radix as i64) * 2) << 15));
positions[k] = hi(value);
value = (quotient as i64) << 16;
}
(positions, signs)
}
fn place_shapes(class: usize, positions: &[i16], signs: &[i16], pulses: usize) -> [i16; SUBFRAME] {
let first_shape = crate::tables::FCB_SHAPE_SEL[MAX_PULSES * class] as usize;
let mut code = [0i16; SUBFRAME];
for k in 0..pulses {
let slot = positions[k] as usize;
let pos =
crate::tables::FCB_POSITIONS[POSITIONS * (MAX_PULSES * class + k) + slot] as usize;
let shape = &crate::tables::FCB_SHAPES
[SHAPE_LEN * (first_shape + k)..SHAPE_LEN * (first_shape + k) + SHAPE_LEN];
for n in pos..SUBFRAME.min(pos + SHAPE_LEN) {
let x = (code[n] as i64) << 16;
let y = (shape[n - pos] as i64) << 16;
code[n] = hi(if signs[k] != 0 {
acc(x + y)
} else {
acc(x - y)
});
}
}
code
}
fn alternative(index: u16) -> [i16; SUBFRAME] {
let rest = (index as i64) - (crate::tables::FCB_CLASS_BASE[ALT_CLASS] as i64);
let upper = shift(rest, -7);
let lower = rest - shift(upper, 7);
let mut code = [0i16; SUBFRAME];
for (base, offset) in [
(&crate::tables::FCB_ALT_A, upper as usize),
(&crate::tables::FCB_ALT_B, lower as usize),
] {
for k in 0..ALT_PULSES {
let entry = base[ALT_PULSES * offset + k];
let pos = entry.unsigned_abs() as usize - 1;
let x = (code[pos] as i64) << 16;
let amp = (ALT_AMPLITUDE as i64) << 16;
code[pos] = hi(if entry <= 0 {
acc(x - amp)
} else {
acc(x + amp)
});
}
}
code
}
pub fn decode(index: u16) -> Innovation {
let (class, index) = classify(index);
if class == ALT_CLASS {
return Innovation {
code: alternative(index),
class,
};
}
let pulses = crate::tables::FCB_PULSES[class] as usize;
let (positions, signs) = decompose(class, index, pulses);
Innovation {
code: place_shapes(class, &positions, &signs, pulses),
class,
}
}