use super::bitstream::parameter_widths;
use super::decoder_tables::LP_WINDOW_200_40;
use crate::fixed_point::arith::extract_l;
use crate::fixed_point::arith32::{l_add, l_mult};
use crate::fixed_point::shift::l_shr;
use crate::fixed_point::types::{DspContext, Word16, Word32};
pub const NODATA_SEED_INIT: Word16 = Word16(21845);
pub fn advance_seed(ctx: &mut DspContext, seed: Word16) -> Word16 {
let product = l_mult(ctx, seed, Word16(31821));
let halved = l_shr(ctx, product, 1);
extract_l(l_add(ctx, halved, Word32(13849)))
}
#[must_use]
pub fn build_cn_param(ctx: &mut DspContext, seed: &mut Word16, mode_index: u8) -> Vec<u16> {
assert!(
mode_index < 8,
"AMR-NB concealment needs a speech mode, got {mode_index}"
);
*seed = advance_seed(ctx, *seed);
#[allow(clippy::cast_sign_loss)]
let base = usize::from(seed.0 as u16 & 0x7F);
let widths = parameter_widths(mode_index);
debug_assert!(
base + widths.len() <= LP_WINDOW_200_40.len(),
"window walk left the table: base {base} + {} parameters",
widths.len()
);
widths
.iter()
.enumerate()
.map(|(index, &width)| {
#[allow(clippy::cast_sign_loss)]
let entry = LP_WINDOW_200_40[base + index] as u16;
entry & ((1u16 << width) - 1)
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_seed_advances_once_per_frame_not_once_per_parameter() {
let mut ctx = DspContext::default();
let mut seed = NODATA_SEED_INIT;
let params = build_cn_param(&mut ctx, &mut seed, 4);
#[allow(clippy::cast_sign_loss)]
let base = usize::from(seed.0 as u16 & 0x7F);
let widths = parameter_widths(4);
assert_eq!(params.len(), widths.len());
for (index, (&value, &width)) in params.iter().zip(widths).enumerate() {
#[allow(clippy::cast_sign_loss)]
let expected = (LP_WINDOW_200_40[base + index] as u16) & ((1u16 << width) - 1);
assert_eq!(value, expected, "parameter {index}");
}
}
#[test]
fn every_parameter_fits_the_width_it_was_masked_to() {
let mut ctx = DspContext::default();
let mut compared = 0;
for mode_index in 0..8u8 {
let mut seed = NODATA_SEED_INIT;
for _ in 0..8 {
let params = build_cn_param(&mut ctx, &mut seed, mode_index);
for (&value, &width) in params.iter().zip(parameter_widths(mode_index)) {
assert!(
u32::from(value) < (1u32 << width),
"mode {mode_index}: {value} does not fit {width} bits"
);
compared += 1;
}
}
}
assert_eq!(compared, 8 * (17 + 19 + 19 + 19 + 19 + 23 + 39 + 57));
}
#[test]
fn successive_frames_draw_different_vectors() {
let mut ctx = DspContext::default();
let mut seed = NODATA_SEED_INIT;
let first = build_cn_param(&mut ctx, &mut seed, 4);
let second = build_cn_param(&mut ctx, &mut seed, 4);
assert_ne!(first, second);
let mut fresh = NODATA_SEED_INIT;
let restarted = build_cn_param(&mut ctx, &mut fresh, 4);
assert_eq!(first, restarted, "the sequence must be deterministic");
}
#[test]
#[should_panic(expected = "needs a speech mode")]
fn sid_has_no_parameter_layout_to_synthesise() {
let mut ctx = DspContext::default();
let mut seed = NODATA_SEED_INIT;
let _ = build_cn_param(&mut ctx, &mut seed, 8);
}
}