use crate::fixedpoint::*;
use crate::tables::*;
pub fn inverse(input: &[i16], output: &mut [i16], frame_size: i16) {
let n = frame_size as usize;
let mut buf_a = [0i16; 320];
let mut buf_b = [0i16; 320];
{
let mut src_pos = 0usize;
let mut front = 0usize;
let mut back = n;
while front < back {
let a = input[src_pos] as i32;
let b = input[src_pos + 1] as i32;
src_pos += 2;
let s = extract_l(l_shr(l_add(a, b), 1));
let d = extract_l(l_shr(l_add(a, -b), 1));
back -= 1;
buf_a[front] = s;
front += 1;
buf_a[back] = d;
}
}
butterfly_16(&buf_a, &mut buf_b, n, 1);
butterfly_16(&buf_b, &mut buf_a, n, 2);
butterfly_16(&buf_a, &mut buf_b, n, 3);
butterfly_16(&buf_b, &mut buf_a, n, 4);
for g in 0..32usize {
for k in 0..10usize {
let mut acc: i32 = 0;
for j in 0..10usize {
acc = l_mac(acc, buf_a[g * 10 + j], COSINE_MOD_MATRIX[k + j * 10]);
}
buf_b[g * 10 + k] = extract_h(l_shr(acc, 1));
}
}
buf_a[..n].copy_from_slice(&buf_b[..n]);
reconstruct(&buf_a, &mut buf_b, n, 4, &FILTERBANK_COEFF_0);
reconstruct(&buf_b, &mut buf_a, n, 3, &FILTERBANK_COEFF_1);
reconstruct(&buf_a, &mut buf_b, n, 2, &FILTERBANK_COEFF_2);
reconstruct(&buf_b, &mut buf_a, n, 1, &FILTERBANK_COEFF_3);
reconstruct(&buf_a, output, n, 0, &FILTERBANK_COEFF_4);
if frame_size == 320 {
for i in 0..n {
output[i] = shl(output[i], 1);
}
}
}
pub fn forward(input: &[i16], output: &mut [i16], frame_size: i16) {
let n = frame_size as usize;
let mut buf_a = [0i16; 320];
let mut buf_b = [0i16; 320];
fwd_butterfly_32(input, &mut buf_a, n, 0);
fwd_butterfly_32(&buf_a, &mut buf_b, n, 1);
fwd_butterfly_32(&buf_b, &mut buf_a, n, 2);
fwd_butterfly_32(&buf_a, &mut buf_b, n, 3);
fwd_butterfly_32(&buf_b, &mut buf_a, n, 4);
for g in 0..32usize {
for k in 0..10usize {
let mut acc: i32 = 0;
for j in 0..10usize {
acc = l_mac(acc, buf_a[g * 10 + j], FWD_COSINE_MOD_MATRIX[k + j * 10]);
}
buf_b[g * 10 + k] = extract_h(acc);
}
}
buf_a[..n].copy_from_slice(&buf_b[..n]);
fwd_reconstruct(&buf_a, &mut buf_b, n, 4, &FWD_FILTERBANK_COEFF_0);
fwd_reconstruct(&buf_b, &mut buf_a, n, 3, &FWD_FILTERBANK_COEFF_1);
fwd_reconstruct(&buf_a, &mut buf_b, n, 2, &FWD_FILTERBANK_COEFF_2);
fwd_reconstruct(&buf_b, &mut buf_a, n, 1, &FWD_FILTERBANK_COEFF_3);
fwd_reconstruct(&buf_a, output, n, 0, &FWD_FILTERBANK_COEFF_4);
}
fn fwd_butterfly_32(src: &[i16], dst: &mut [i16], n: usize, stage: usize) {
let group_size = n >> stage;
let num_groups = 1usize << stage;
let mut src_pos = 0usize;
for g in 0..num_groups {
let base = g * group_size;
let mut front = base;
let mut back = base + group_size;
while front < back {
let a = src[src_pos] as i32;
let b = src[src_pos + 1] as i32;
src_pos += 2;
let a_shr = l_shr(a, 1);
let b_shr = l_shr(b, 1);
back -= 1;
dst[front] = extract_l(l_add(a_shr, b_shr));
front += 1;
dst[back] = extract_l(l_sub(a_shr, b_shr));
}
}
}
fn fwd_reconstruct(src: &[i16], dst: &mut [i16], n: usize, stage: i16, coeffs: &[i16]) {
let group_size = shr(n as i16, stage) as usize;
let num_groups = shl(1, stage) as usize;
let half = group_size / 2;
for g in 0..num_groups {
let src_base = g * group_size;
let dst_base = g * group_size;
let mut src_first = src_base;
let mut src_second = src_base + half;
let mut dst_front = dst_base;
let mut dst_back = dst_base + group_size;
let mut ci = 0usize;
while dst_front < dst_back {
let a = src[src_first];
let b = src[src_first + 1];
let c = src[src_second];
let d = src[src_second + 1];
src_first += 2;
src_second += 2;
let c0 = coeffs[ci];
let c1 = coeffs[ci + 1];
let c2 = coeffs[ci + 2];
let c3 = coeffs[ci + 3];
ci += 4;
let val_a = extract_h(l_mac(l_mac(0, c0, a), negate(c1), c));
let val_b = extract_h(l_mac(l_mac(0, c1, a), c0, c));
let val_c = extract_h(l_mac(l_mac(0, c2, b), c3, d));
let val_d = extract_h(l_mac(l_mac(0, c3, b), negate(c2), d));
dst[dst_front] = val_a;
dst[dst_front + 1] = val_c;
dst[dst_back - 1] = val_b;
dst[dst_back - 2] = val_d;
dst_front += 2;
dst_back -= 2;
}
}
}
fn butterfly_16(src: &[i16], dst: &mut [i16], n: usize, stage: i16) {
let group_size = shr(n as i16, stage) as usize;
let num_groups = shl(1, stage) as usize;
let mut src_pos = 0usize;
for g in 0..num_groups {
let base = g * group_size;
let mut front = base;
let mut back = base + group_size;
while front < back {
let a = src[src_pos];
let b = src[src_pos + 1];
src_pos += 2;
back -= 1;
dst[front] = add(a, b);
front += 1;
dst[back] = add(a, negate(b));
}
}
}
fn reconstruct(src: &[i16], dst: &mut [i16], n: usize, stage: i16, coeffs: &[i16]) {
let group_size = shr(n as i16, stage) as usize;
let num_groups = shl(1, stage) as usize;
let half = group_size / 2;
for g in 0..num_groups {
let src_base = g * group_size;
let dst_base = g * group_size;
let mut src_first = src_base;
let mut src_second = src_base + half;
let mut dst_front = dst_base;
let mut dst_back = dst_base + group_size;
let mut ci = 0usize;
while dst_front < dst_back {
let a = src[src_first];
let b = src[src_first + 1];
let c = src[src_second];
let d = src[src_second + 1];
src_first += 2;
src_second += 2;
let c0 = coeffs[ci];
let c1 = coeffs[ci + 1];
let c2 = coeffs[ci + 2];
let c3 = coeffs[ci + 3];
ci += 4;
let val_a = extract_h(l_shl(l_mac(l_mac(0, c0, a), negate(c1), c), 1));
let val_b = extract_h(l_shl(l_mac(l_mac(0, c1, a), c0, c), 1));
let val_c = extract_h(l_shl(l_mac(l_mac(0, c2, b), c3, d), 1));
let val_d = extract_h(l_shl(l_mac(l_mac(0, c3, b), negate(c2), d), 1));
dst[dst_front] = val_a;
dst[dst_front + 1] = val_c;
dst[dst_back - 1] = val_b;
dst[dst_back - 2] = val_d;
dst_front += 2;
dst_back -= 2;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_butterfly_16_identity() {
let src = [100i16; 320];
let mut dst = [0i16; 320];
butterfly_16(&src, &mut dst, 320, 1);
for i in 0..80 {
assert_eq!(dst[i], 200, "front {} should be 200", i);
}
for i in 80..160 {
assert_eq!(dst[i], 0, "back {} should be 0", i);
}
}
#[test]
fn test_inverse_zeros() {
let input = [0i16; 320];
let mut output = [0i16; 320];
inverse(&input, &mut output, 320);
for (i, &s) in output.iter().enumerate() {
assert_eq!(s, 0, "sample {} should be 0", i);
}
}
#[test]
fn test_butterfly_stage0_32bit() {
let mut input = [0i16; 320];
input[0] = 10000;
input[1] = 5000;
let mut buf = [0i16; 320];
let a = 10000i32;
let b = 5000i32;
let expected_sum = extract_l(l_shr(l_add(a, b), 1)); let expected_diff = extract_l(l_shr(l_add(a, -b), 1));
let front = 0;
let mut back = 320;
let s = extract_l(l_shr(l_add(input[0] as i32, input[1] as i32), 1));
let d = extract_l(l_shr(l_add(input[0] as i32, -(input[1] as i32)), 1));
back -= 1;
buf[front] = s;
buf[back] = d;
assert_eq!(buf[0], expected_sum);
assert_eq!(buf[319], expected_diff);
}
#[test]
fn test_forward_inverse_roundtrip() {
let mut input = [0i16; 320];
for i in 0..320 {
let t = i as f64 / 16000.0;
input[i] = (5000.0 * (2.0 * std::f64::consts::PI * 1000.0 * t).sin()) as i16;
}
let mut subbands = [0i16; 320];
forward(&input, &mut subbands, 320);
let sub_max = subbands.iter().map(|&s| (s as i32).abs()).max().unwrap();
let sub_nz = subbands.iter().filter(|&&s| s != 0).count();
eprintln!("forward: max={} nonzero={}/320", sub_max, sub_nz);
let mut output = [0i16; 320];
inverse(&subbands, &mut output, 320);
let out_max = output.iter().map(|&s| (s as i32).abs()).max().unwrap();
eprintln!("inverse: max={}", out_max);
let mut cov = 0.0f64;
let mut var_in = 0.0f64;
let mut var_out = 0.0f64;
for i in 0..320 {
let a = input[i] as f64;
let b = output[i] as f64;
cov += a * b;
var_in += a * a;
var_out += b * b;
}
let corr = cov / (var_in.sqrt() * var_out.sqrt() + 1e-10);
let ratio = (var_out / var_in).sqrt();
eprintln!("correlation={:.4} rms_ratio={:.4}", corr, ratio);
assert!(sub_nz > 0, "forward produced all zeros");
}
#[test]
fn test_forward_zeros() {
let input = [0i16; 320];
let mut output = [0i16; 320];
forward(&input, &mut output, 320);
for (i, &s) in output.iter().enumerate() {
assert_eq!(s, 0, "sample {} should be 0", i);
}
}
}