use crate::fixed::{acc, hi, low, mul, sat, shift};
const PERIODIC_CAP: i64 = 15565;
pub fn cap_gain(periodic: i16, gain: &mut i16) {
if periodic == 0 {
return;
}
*gain = low(acc((*gain as i64).min(PERIODIC_CAP)));
}
const CARRY_LOW: i64 = 3277;
const CARRY_HIGH: i64 = 13017;
pub fn carry_gain(gain: i16) -> i16 {
low(acc((gain as i64).clamp(CARRY_LOW, CARRY_HIGH)))
}
const HALF: i64 = 16384;
const WAS_STRONG: i64 = 13107;
const SAME_PITCH: i64 = 24576;
fn peaky_spectrum(reflection: i16) -> bool {
acc(shift((reflection as i64) << 16, 3) - (HALF << 16)) > 0
}
fn gain_was_strong(previous_gain: i16) -> bool {
acc(shift((previous_gain as i64) << 16, 1) - (WAS_STRONG << 16)) > 0
}
fn pitch_continues(lag: i16, previous_lag: i16) -> bool {
let above_floor = acc(((lag as i64) << 16) - acc(SAME_PITCH * (previous_lag as i64) * 2)) > 0;
let below_ceiling =
acc(shift(acc((previous_lag as i64) * SAME_PITCH * 2), 1) - ((lag as i64) << 16)) > 0;
above_floor && below_ceiling
}
pub fn interpolation_gains(
reflection: i16,
previous_gain: i16,
lag: i16,
previous_lag: i16,
) -> (i16, i16) {
let peaky = peaky_spectrum(reflection);
let strong = gain_was_strong(previous_gain);
let continuous = pitch_continues(lag, previous_lag);
let second = if peaky {
0
} else if strong && continuous {
HALF
} else {
previous_gain as i64
};
(low(HALF), low(second))
}
pub(crate) fn mixed_sample(
adaptive_sample: i16,
innovation: i16,
adaptive_gain: i16,
fixed_gain: i16,
) -> i16 {
let shaped = acc(mul(fixed_gain, innovation));
let mut total = acc(mul(adaptive_gain, adaptive_sample));
total = acc(total + shift(shaped, 1));
total = acc(shift(total, 1) + 0x8000);
hi(sat(total))
}
pub fn excite(excitation: &mut [i16], innovation: &[i16], adaptive: i16, fixed: i16) {
for k in 0..crate::SUBFRAME {
excitation[k] = mixed_sample(excitation[k], innovation[k], adaptive, fixed);
}
}
const MEMORY: usize = crate::LPC_ORDER;
fn reconstruction_error(input: i16, synthesised: i16) -> i16 {
hi(sat(acc(
((input as i64) << 16) - ((synthesised as i64) << 16)
)))
}
fn target_residual(weighted: i16, adaptive: i16, innovation: i16, gains: (i16, i16)) -> i16 {
let mut total = (weighted as i64) << 16;
total = acc(total - shift(acc(mul(gains.0, adaptive)), 1));
total = acc(total - shift(acc(mul(gains.1, innovation)), 2));
hi(sat(total))
}
pub fn update_memories(
input: &[i16],
synthesised: &[i16],
weighted: &[i16],
adaptive: &[i16],
innovation: &[i16],
gains: (i16, i16),
) -> ([i16; MEMORY], [i16; MEMORY]) {
let (mut error, mut residual) = ([0i16; MEMORY], [0i16; MEMORY]);
for k in 0..MEMORY {
error[k] = reconstruction_error(input[k], synthesised[k]);
residual[k] = target_residual(weighted[k], adaptive[k], innovation[k], gains);
}
(error, residual)
}