use super::resampler_rom::{SILK_RESAMPLER_UP2_HQ_0, SILK_RESAMPLER_UP2_HQ_1};
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ResamplerStateUp2Hq {
pub s_iir: [i32; 6],
}
impl ResamplerStateUp2Hq {
pub const fn new() -> Self {
Self { s_iir: [0; 6] }
}
pub fn resampler_private_up2_hq_wrapper(&mut self, output: &mut [i16], input: &[i16]) {
resampler_private_up2_hq(&mut self.s_iir, output, input);
}
}
#[allow(
clippy::cast_possible_wrap,
clippy::cast_possible_truncation,
clippy::cast_sign_loss
)]
pub fn resampler_private_up2_hq(state: &mut [i32; 6], output: &mut [i16], input: &[i16]) {
assert!(
output.len() >= input.len() * 2,
"output buffer too small: need {} samples",
input.len() * 2
);
for (k, &sample) in input.iter().enumerate() {
let in32 = i32::from(sample) << 10;
let mut y = in32 - state[0];
let mut x = smulwb(y, i32::from(SILK_RESAMPLER_UP2_HQ_0[0]));
let mut out32_1 = state[0] + x;
state[0] = in32 + x;
y = out32_1 - state[1];
x = smulwb(y, i32::from(SILK_RESAMPLER_UP2_HQ_0[1]));
let mut out32_2 = state[1] + x;
state[1] = out32_1 + x;
y = out32_2 - state[2];
x = smlawb(y, y, i32::from(SILK_RESAMPLER_UP2_HQ_0[2]));
out32_1 = state[2] + x;
state[2] = out32_2 + x;
output[2 * k] = sat16(rshift_round(out32_1, 10));
y = in32 - state[3];
x = smulwb(y, i32::from(SILK_RESAMPLER_UP2_HQ_1[0]));
out32_1 = state[3] + x;
state[3] = in32 + x;
y = out32_1 - state[4];
x = smulwb(y, i32::from(SILK_RESAMPLER_UP2_HQ_1[1]));
out32_2 = state[4] + x;
state[4] = out32_1 + x;
y = out32_2 - state[5];
x = smlawb(y, y, i32::from(SILK_RESAMPLER_UP2_HQ_1[2]));
out32_1 = state[5] + x;
state[5] = out32_2 + x;
output[2 * k + 1] = sat16(rshift_round(out32_1, 10));
}
}
#[inline]
fn smulwb(a: i32, b_q15: i32) -> i32 {
let product = i64::from(a) * i64::from(b_q15 as i16);
(product >> 16) as i32
}
#[inline]
fn smlawb(a: i32, b: i32, c_q15: i32) -> i32 {
let product = i64::from(b) * i64::from(c_q15 as i16);
a.wrapping_add((product >> 16) as i32)
}
#[inline]
fn sat16(value: i32) -> i16 {
if value > i32::from(i16::MAX) {
i16::MAX
} else if value < i32::from(i16::MIN) {
i16::MIN
} else {
value as i16
}
}
#[inline]
fn rshift_round(value: i32, shift: u32) -> i32 {
assert!(shift > 0);
if shift == 1 {
(value >> 1) + (value & 1)
} else {
((value >> (shift - 1)) + 1) >> 1
}
}
#[cfg(test)]
mod tests {
use super::{ResamplerStateUp2Hq, resampler_private_up2_hq};
#[test]
fn produces_zero_output_for_zero_input() {
let mut state = [0i32; 6];
let input = [0i16; 8];
let mut output = [0i16; 16];
resampler_private_up2_hq(&mut state, &mut output, &input);
assert!(output.iter().all(|&sample| sample == 0));
assert_eq!(state, [0; 6]);
}
#[test]
fn matches_expected_sequence() {
let mut state = [0i32; 6];
let input = [1000i16, -1000, 2000, -2000];
let mut output = [0i16; 8];
resampler_private_up2_hq(&mut state, &mut output, &input);
assert_eq!(output, [4, 35, 152, 381, 571, 423, -52, -236]);
assert_eq!(
state,
[
-2_159_345, 2_825_288, -1_646_130, -2_512_442, 3_368_205, -583_113
]
);
}
#[test]
fn wrapper_delegates_to_internal_state() {
let mut state = ResamplerStateUp2Hq::new();
let input = [3123i16, -1812, 904, -222];
let mut output = [0i16; 8];
state.resampler_private_up2_hq_wrapper(&mut output, &input);
assert_eq!(output, [11, 109, 478, 1236, 1967, 1680, -29, -1_740]);
assert_eq!(
state.s_iir,
[
-260_118, 1_931_422, -5_580_563, -384_499, 3_302_757, -4_867_408
]
);
}
#[test]
#[should_panic(expected = "output buffer too small")]
fn panics_if_output_is_too_small() {
let mut state = [0i32; 6];
let input = [1i16, 2, 3];
let mut output = [0i16; 5];
resampler_private_up2_hq(&mut state, &mut output, &input);
}
}