use alloc::vec;
use core::cmp::Ordering;
use crate::silk::decoder_control::DecoderControl;
use crate::silk::nlsf2a::nlsf2a;
use crate::silk::{FrameSignalType, MAX_LPC_ORDER, MAX_NB_SUBFR};
const SUBFRAME_MS: usize = 5;
const MAX_FS_KHZ: usize = 16;
const MAX_SUBFRAME_LENGTH: usize = SUBFRAME_MS * MAX_FS_KHZ;
const MAX_FRAME_LENGTH: usize = MAX_SUBFRAME_LENGTH * MAX_NB_SUBFR;
const CNG_BUF_MASK_MAX: i32 = 255;
const CNG_GAIN_SMTH_Q16: i32 = 4_634;
const CNG_GAIN_SMTH_THRESHOLD_Q16: i32 = 46_396;
const CNG_NLSF_SMTH_Q16: i32 = 16_348;
const INITIAL_RAND_SEED: i32 = 3_176_576;
const RAND_MULTIPLIER: i32 = 196_314_165;
const RAND_INCREMENT: i32 = 907_633_515;
const INVALID_FS_KHZ: i32 = -1;
#[derive(Clone, Debug)]
pub struct CngState {
exc_buf_q14: [i32; MAX_FRAME_LENGTH],
smth_nlsf_q15: [i16; MAX_LPC_ORDER],
synth_state: [i32; MAX_LPC_ORDER],
smth_gain_q16: i32,
rand_seed: i32,
fs_khz: i32,
}
impl Default for CngState {
fn default() -> Self {
Self {
exc_buf_q14: [0; MAX_FRAME_LENGTH],
smth_nlsf_q15: [0; MAX_LPC_ORDER],
synth_state: [0; MAX_LPC_ORDER],
smth_gain_q16: 0,
rand_seed: INITIAL_RAND_SEED,
fs_khz: INVALID_FS_KHZ,
}
}
}
impl CngState {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn reset(&mut self, lpc_order: usize) {
assert!(
(1..=MAX_LPC_ORDER).contains(&lpc_order),
"invalid LPC order {lpc_order}"
);
let step_q15 = div32_16(i32::from(i16::MAX), (lpc_order + 1) as i32);
let mut acc_q15: i32 = 0;
for value in self.smth_nlsf_q15.iter_mut().take(lpc_order) {
acc_q15 = acc_q15.saturating_add(step_q15);
*value = acc_q15 as i16;
}
for value in self.smth_nlsf_q15.iter_mut().skip(lpc_order) {
*value = 0;
}
self.smth_gain_q16 = 0;
self.rand_seed = INITIAL_RAND_SEED;
self.synth_state = [0; MAX_LPC_ORDER];
}
#[must_use]
pub fn smoothed_gain_q16(&self) -> i32 {
self.smth_gain_q16
}
#[must_use]
pub fn smoothed_nlsf_q15(&self) -> &[i16; MAX_LPC_ORDER] {
&self.smth_nlsf_q15
}
#[must_use]
pub fn excitation_buffer(&self) -> &[i32; MAX_FRAME_LENGTH] {
&self.exc_buf_q14
}
pub fn excitation_buffer_mut(&mut self) -> &mut [i32; MAX_FRAME_LENGTH] {
&mut self.exc_buf_q14
}
#[must_use]
pub fn synth_state(&self) -> &[i32; MAX_LPC_ORDER] {
&self.synth_state
}
#[must_use]
pub fn fs_khz(&self) -> i32 {
self.fs_khz
}
}
#[derive(Clone, Debug, Default)]
pub struct PlcState {
pub rand_scale_q14: i32,
pub prev_gain_q16: [i32; 2],
}
#[derive(Clone, Debug)]
pub struct ComfortNoiseInputs<'a> {
pub fs_khz: i32,
pub lpc_order: usize,
pub nb_subfr: usize,
pub subfr_length: usize,
pub prev_signal_type: FrameSignalType,
pub loss_count: i32,
pub prev_nlsf_q15: &'a [i16],
pub exc_q14: &'a [i32],
}
impl<'a> ComfortNoiseInputs<'a> {
#[must_use]
pub fn frame_length(&self) -> usize {
self.nb_subfr * self.subfr_length
}
}
pub fn apply_cng(
state: &mut CngState,
plc: &PlcState,
control: &DecoderControl,
inputs: &ComfortNoiseInputs<'_>,
frame: &mut [i16],
) {
validate_inputs(inputs, frame);
if inputs.fs_khz != state.fs_khz {
if state.fs_khz == INVALID_FS_KHZ {
state
.synth_state
.iter_mut()
.take(inputs.lpc_order)
.for_each(|value| *value = 0);
} else {
state.reset(inputs.lpc_order);
}
state.smth_nlsf_q15[..inputs.lpc_order]
.copy_from_slice(&inputs.prev_nlsf_q15[..inputs.lpc_order]);
state
.smth_nlsf_q15
.iter_mut()
.skip(inputs.lpc_order)
.for_each(|value| *value = 0);
state.fs_khz = inputs.fs_khz;
}
if inputs.loss_count == 0 && inputs.prev_signal_type == FrameSignalType::Inactive {
smooth_nlsf(state, inputs);
update_excitation_buffer(state, inputs, control);
smooth_gain(state, control, inputs.nb_subfr);
}
if inputs.loss_count > 0 {
synthesize_noise(state, plc, inputs, frame);
} else {
state
.synth_state
.iter_mut()
.take(inputs.lpc_order)
.for_each(|value| *value = 0);
}
}
fn validate_inputs(inputs: &ComfortNoiseInputs<'_>, frame: &[i16]) {
assert!(
(1..=MAX_LPC_ORDER).contains(&inputs.lpc_order),
"invalid LPC order {}",
inputs.lpc_order
);
assert!(
inputs.nb_subfr > 0 && inputs.nb_subfr <= MAX_NB_SUBFR,
"invalid subframe count {}",
inputs.nb_subfr
);
assert!(inputs.subfr_length > 0 && inputs.subfr_length <= MAX_SUBFRAME_LENGTH);
assert!(
frame.len() == inputs.frame_length(),
"frame length {} does not match {}",
frame.len(),
inputs.frame_length()
);
assert!(
frame.len() <= MAX_FRAME_LENGTH,
"frame length {} exceeds MAX_FRAME_LENGTH {}",
frame.len(),
MAX_FRAME_LENGTH
);
assert!(
inputs.prev_nlsf_q15.len() >= inputs.lpc_order,
"prev_nlsf slice too small"
);
assert!(
inputs.exc_q14.len() >= inputs.frame_length(),
"excitation buffer too small"
);
}
fn smooth_nlsf(state: &mut CngState, inputs: &ComfortNoiseInputs<'_>) {
for (idx, target) in state
.smth_nlsf_q15
.iter_mut()
.take(inputs.lpc_order)
.enumerate()
{
let prev = i32::from(inputs.prev_nlsf_q15[idx]);
let current = i32::from(*target);
let updated = current + smulwb(prev - current, CNG_NLSF_SMTH_Q16);
*target = updated as i16;
}
}
fn update_excitation_buffer(
state: &mut CngState,
inputs: &ComfortNoiseInputs<'_>,
control: &DecoderControl,
) {
let mut max_gain_q16 = 0;
let mut strongest_subfr = 0usize;
for (idx, &gain) in control.gains_q16.iter().take(inputs.nb_subfr).enumerate() {
if gain > max_gain_q16 {
max_gain_q16 = gain;
strongest_subfr = idx;
}
}
if inputs.nb_subfr == 0 {
return;
}
let move_len = inputs.subfr_length * (inputs.nb_subfr - 1);
if move_len > 0 {
state
.exc_buf_q14
.copy_within(0..move_len, inputs.subfr_length);
}
let start = strongest_subfr * inputs.subfr_length;
let end = start + inputs.subfr_length;
state.exc_buf_q14[..inputs.subfr_length].copy_from_slice(&inputs.exc_q14[start..end]);
}
fn smooth_gain(state: &mut CngState, control: &DecoderControl, nb_subfr: usize) {
for gain in control.gains_q16.iter().take(nb_subfr) {
state.smth_gain_q16 =
state.smth_gain_q16 + smulwb(*gain - state.smth_gain_q16, CNG_GAIN_SMTH_Q16);
if smulww(state.smth_gain_q16, CNG_GAIN_SMTH_THRESHOLD_Q16) > *gain {
state.smth_gain_q16 = *gain;
}
}
}
fn synthesize_noise(
state: &mut CngState,
plc: &PlcState,
inputs: &ComfortNoiseInputs<'_>,
frame: &mut [i16],
) {
let length = frame.len();
let mut cng_sig_q14 = vec![0i32; length + MAX_LPC_ORDER];
let mut gain_q16 = smulww(plc.rand_scale_q14, plc.prev_gain_q16[1]);
if gain_q16 >= (1 << 21) || state.smth_gain_q16 > (1 << 23) {
gain_q16 = smultt(gain_q16, gain_q16);
gain_q16 = sub_lshift32(
smultt(state.smth_gain_q16, state.smth_gain_q16),
gain_q16,
5,
);
gain_q16 = lshift_sat32(sqrt_approx(gain_q16), 16);
} else {
gain_q16 = smulww(gain_q16, gain_q16);
gain_q16 = sub_lshift32(
smulww(state.smth_gain_q16, state.smth_gain_q16),
gain_q16,
5,
);
gain_q16 = lshift_sat32(sqrt_approx(gain_q16), 8);
}
let gain_q10 = gain_q16 >> 6;
generate_excitation(
&mut cng_sig_q14[MAX_LPC_ORDER..],
&state.exc_buf_q14,
length,
&mut state.rand_seed,
);
let mut a_q12 = [0i16; MAX_LPC_ORDER];
nlsf2a(
&mut a_q12[..inputs.lpc_order],
&state.smth_nlsf_q15[..inputs.lpc_order],
0,
);
cng_sig_q14[..MAX_LPC_ORDER].copy_from_slice(&state.synth_state);
for i in 0..length {
let mut lpc_pred_q10 = (inputs.lpc_order as i32) >> 1;
for (tap, coeff) in a_q12.iter().take(inputs.lpc_order).enumerate() {
let hist_idx = MAX_LPC_ORDER + i - 1 - tap;
lpc_pred_q10 = smlawb(lpc_pred_q10, cng_sig_q14[hist_idx], i32::from(*coeff));
}
let updated = add_sat32(
cng_sig_q14[MAX_LPC_ORDER + i],
lshift_sat32(lpc_pred_q10, 4),
);
cng_sig_q14[MAX_LPC_ORDER + i] = updated;
let scaled = smulww(updated, gain_q10);
let sample = sat16(rshift_round(scaled, 8));
frame[i] = add_sat16(frame[i], sample);
}
state
.synth_state
.copy_from_slice(&cng_sig_q14[length..length + MAX_LPC_ORDER]);
}
fn generate_excitation(
out: &mut [i32],
exc_buf: &[i32; MAX_FRAME_LENGTH],
length: usize,
seed: &mut i32,
) {
if out.is_empty() {
return;
}
let mut exc_mask = CNG_BUF_MASK_MAX;
let target = length as i32;
while exc_mask > target {
exc_mask >>= 1;
}
let mut current_seed = *seed;
for sample in out.iter_mut() {
current_seed = silk_rand(current_seed);
let idx =
((current_seed >> 24) & exc_mask).clamp(0, (MAX_FRAME_LENGTH - 1) as i32) as usize;
*sample = exc_buf[idx];
}
*seed = current_seed;
}
pub(crate) fn silk_rand(seed: i32) -> i32 {
RAND_INCREMENT.wrapping_add(seed.wrapping_mul(RAND_MULTIPLIER))
}
fn smulwb(a: i32, b: i32) -> i32 {
((i64::from(a) * i64::from(i32::from(b as i16))) >> 16) as i32
}
fn smlawb(acc: i32, b: i32, c: i32) -> i32 {
acc.wrapping_add(smulwb(b, c))
}
fn smulww(a: i32, b: i32) -> i32 {
((i64::from(a) * i64::from(b)) >> 16) as i32
}
fn smultt(a: i32, b: i32) -> i32 {
(a >> 16) * (b >> 16)
}
fn div32_16(a: i32, b: i32) -> i32 {
if b == 0 { 0 } else { a / b }
}
fn add_sat32(a: i32, b: i32) -> i32 {
let sum = i64::from(a) + i64::from(b);
sum.clamp(i64::from(i32::MIN), i64::from(i32::MAX)) as i32
}
fn add_sat16(a: i16, b: i16) -> i16 {
sat16(i32::from(a) + i32::from(b))
}
fn sat16(value: i32) -> i16 {
value.clamp(i32::from(i16::MIN), i32::from(i16::MAX)) as i16
}
fn lshift_sat32(value: i32, shift: i32) -> i32 {
if shift <= 0 {
value >> (-shift)
} else if shift >= 31 {
match value.cmp(&0) {
Ordering::Greater => i32::MAX,
Ordering::Less => i32::MIN,
Ordering::Equal => 0,
}
} else {
let min_val = i32::MIN >> shift;
let max_val = i32::MAX >> shift;
(value.clamp(min_val, max_val)) << shift
}
}
fn sub_lshift32(a: i32, b: i32, shift: i32) -> i32 {
a.wrapping_sub(b.wrapping_shl(shift as u32))
}
fn rshift_round(value: i32, shift: i32) -> i32 {
if shift <= 0 {
value.wrapping_shl((-shift) as u32)
} else if shift == 1 {
(value >> 1) + (value & 1)
} else {
((value >> (shift - 1)) + 1) >> 1
}
}
fn sqrt_approx(x: i32) -> i32 {
if x <= 0 {
return 0;
}
let (lz, frac_q7) = clz_frac(x);
let mut y = if lz & 1 != 0 { 32_768 } else { 46_214 };
y >>= lz >> 1;
smlawb(y, y, smulbb(213, frac_q7))
}
fn clz_frac(x: i32) -> (i32, i32) {
let ux = x as u32;
let lz = ux.leading_zeros() as i32;
let rotate = ((24 - lz) & 31) as u32;
let frac = (ux.rotate_right(rotate) & 0x7f) as i32;
(lz, frac)
}
fn smulbb(a: i32, b: i32) -> i32 {
i32::from(a as i16) * i32::from(b as i16)
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec::Vec;
fn default_inputs<'a>(
prev_nlsf_q15: &'a [i16],
exc_q14: &'a [i32],
loss_count: i32,
prev_signal_type: FrameSignalType,
subfr_length: usize,
nb_subfr: usize,
lpc_order: usize,
) -> ComfortNoiseInputs<'a> {
ComfortNoiseInputs {
fs_khz: 16,
lpc_order,
nb_subfr,
subfr_length,
prev_signal_type,
loss_count,
prev_nlsf_q15,
exc_q14,
}
}
#[test]
fn reset_seeds_nlsf_grid() {
let mut state = CngState::new();
state.reset(10);
let step_q15 = div32_16(i32::from(i16::MAX), 11);
for (idx, &value) in state.smth_nlsf_q15.iter().take(10).enumerate() {
let expected = ((idx as i32 + 1) * step_q15) as i16;
assert_eq!(value, expected);
}
assert_eq!(state.smoothed_gain_q16(), 0);
assert_eq!(state.rand_seed, INITIAL_RAND_SEED);
}
#[test]
fn update_tracks_highest_gain_subframe() {
let mut state = CngState::new();
let plc = PlcState::default();
let mut control = DecoderControl::default();
control.gains_q16 = [1 << 16, 2 << 16, 4 << 16, 3 << 16];
let subfr_length = 4;
let nb_subfr = 4;
let lpc_order = 10;
let frame_len = subfr_length * nb_subfr;
let prev_nlsf: Vec<i16> = (0..MAX_LPC_ORDER).map(|idx| (idx as i16) * 123).collect();
let mut exc_q14 = vec![0i32; frame_len];
for (idx, sample) in exc_q14.iter_mut().enumerate() {
*sample = (idx as i32 + 1) * 10;
}
let inputs = default_inputs(
&prev_nlsf,
&exc_q14,
0,
FrameSignalType::Inactive,
subfr_length,
nb_subfr,
lpc_order,
);
let mut frame = vec![0i16; frame_len];
apply_cng(&mut state, &plc, &control, &inputs, &mut frame);
let mut expected_gain = 0;
for gain in control.gains_q16.iter().take(nb_subfr) {
expected_gain += smulwb(*gain - expected_gain, CNG_GAIN_SMTH_Q16);
if smulww(expected_gain, CNG_GAIN_SMTH_THRESHOLD_Q16) > *gain {
expected_gain = *gain;
}
}
assert_eq!(state.smoothed_gain_q16(), expected_gain);
assert_eq!(frame, vec![0i16; frame_len]);
let max_subfr_start = 2 * subfr_length;
assert_eq!(
&state.exc_buf_q14[..subfr_length],
&exc_q14[max_subfr_start..max_subfr_start + subfr_length]
);
assert!(
state
.smth_nlsf_q15
.iter()
.take(lpc_order)
.zip(prev_nlsf.iter())
.all(|(&smoothed, &target)| smoothed.abs() <= target.abs()),
"smoothed_nlsf={:?} prev={:?}",
&state.smth_nlsf_q15[..lpc_order],
&prev_nlsf[..lpc_order]
);
}
#[test]
fn loss_path_injects_noise() {
let mut state = CngState::new();
state.reset(10);
state.smth_gain_q16 = 1 << 20;
state
.smth_nlsf_q15
.iter_mut()
.enumerate()
.for_each(|(idx, value)| *value = 200 + (idx as i16) * 50);
state
.exc_buf_q14
.iter_mut()
.enumerate()
.for_each(|(idx, sample)| *sample = (idx as i32) * 113);
let mut state_clone = state.clone();
let mut control = DecoderControl::default();
control.gains_q16 = [1 << 16; MAX_NB_SUBFR];
let mut plc = PlcState::default();
plc.rand_scale_q14 = 1 << 14;
plc.prev_gain_q16 = [1 << 16, 1 << 16];
let subfr_length = 8;
let nb_subfr = 2;
let lpc_order = 10;
let frame_len = subfr_length * nb_subfr;
let prev_nlsf = vec![300i16; MAX_LPC_ORDER];
let exc_q14 = vec![0i32; frame_len];
let mut frame = vec![0i16; frame_len];
let inputs = default_inputs(
&prev_nlsf,
&exc_q14,
1,
FrameSignalType::Voiced,
subfr_length,
nb_subfr,
lpc_order,
);
apply_cng(&mut state, &plc, &control, &inputs, &mut frame);
assert!(frame.iter().any(|&sample| sample != 0), "frame={:?}", frame);
assert!(
state
.synth_state
.iter()
.take(lpc_order)
.any(|&value| value != 0)
);
let mut expected_frame = vec![0i16; frame_len];
apply_cng(
&mut state_clone,
&plc,
&control,
&inputs,
&mut expected_frame,
);
assert_eq!(frame, expected_frame);
}
}