#![allow(clippy::missing_safety_doc)]
#![allow(unsafe_code)]
use core::arch::aarch64::*;
use crate::features::{FRAME_LENGTH_SAMPLES, NUM_MEL_BINS, scalar};
#[inline]
#[target_feature(enable = "neon")]
pub(crate) unsafe fn dc_remove(window: &[f32], out: &mut [f32]) {
debug_assert_eq!(window.len(), FRAME_LENGTH_SAMPLES);
debug_assert_eq!(out.len(), FRAME_LENGTH_SAMPLES);
let n = FRAME_LENGTH_SAMPLES;
unsafe {
let mut acc = vdupq_n_f32(0.0);
let mut i = 0usize;
while i + 4 <= n {
let v = vld1q_f32(window.as_ptr().add(i));
acc = vaddq_f32(acc, v);
i += 4;
}
let mut sum = vaddvq_f32(acc);
if i < n {
for k in i..n {
sum += *window.get_unchecked(k);
}
}
let mean = sum / FRAME_LENGTH_SAMPLES as f32;
let mean_v = vdupq_n_f32(mean);
let mut i = 0usize;
while i + 4 <= n {
let v = vld1q_f32(window.as_ptr().add(i));
let r = vsubq_f32(v, mean_v);
vst1q_f32(out.as_mut_ptr().add(i), r);
i += 4;
}
for k in i..n {
*out.get_unchecked_mut(k) = *window.get_unchecked(k) - mean;
}
}
}
#[inline]
#[target_feature(enable = "neon")]
pub(crate) unsafe fn window_apply(samples: &mut [f32], window: &[f32]) {
debug_assert_eq!(samples.len(), FRAME_LENGTH_SAMPLES);
debug_assert_eq!(window.len(), FRAME_LENGTH_SAMPLES);
let n = FRAME_LENGTH_SAMPLES;
unsafe {
let mut i = 0usize;
while i + 4 <= n {
let s = vld1q_f32(samples.as_ptr().add(i));
let w = vld1q_f32(window.as_ptr().add(i));
vst1q_f32(samples.as_mut_ptr().add(i), vmulq_f32(s, w));
i += 4;
}
debug_assert_eq!(i, n);
}
}
#[inline]
#[target_feature(enable = "neon")]
pub(crate) unsafe fn power_spectrum(
complex: &[rustfft::num_complex::Complex<f32>],
out: &mut [f32],
) {
debug_assert_eq!(complex.len(), out.len());
let n = complex.len();
unsafe {
let mut i = 0usize;
let base = complex.as_ptr() as *const f32;
while i + 4 <= n {
let pair = vld2q_f32(base.add(i * 2));
let re = pair.0;
let im = pair.1;
let p = vfmaq_f32(vmulq_f32(re, re), im, im);
vst1q_f32(out.as_mut_ptr().add(i), p);
i += 4;
}
if i < n {
scalar::power_spectrum(&complex[i..], &mut out[i..]);
}
}
}
#[inline]
#[target_feature(enable = "neon")]
pub(crate) unsafe fn cmvn_apply(feature: &mut [f32], means: &[f32], istd: &[f32]) {
debug_assert_eq!(feature.len(), NUM_MEL_BINS);
debug_assert_eq!(means.len(), NUM_MEL_BINS);
debug_assert_eq!(istd.len(), NUM_MEL_BINS);
let n = NUM_MEL_BINS;
unsafe {
let mut i = 0usize;
while i + 4 <= n {
let f = vld1q_f32(feature.as_ptr().add(i));
let m = vld1q_f32(means.as_ptr().add(i));
let s = vld1q_f32(istd.as_ptr().add(i));
let r = vmulq_f32(vsubq_f32(f, m), s);
vst1q_f32(feature.as_mut_ptr().add(i), r);
i += 4;
}
debug_assert_eq!(i, n);
}
}
#[inline]
#[target_feature(enable = "neon")]
pub(crate) unsafe fn mel_dot_log(power_slice: &[f32], weights: &[f32]) -> f32 {
debug_assert_eq!(power_slice.len(), weights.len());
let n = power_slice.len();
let mut energy = 0.0f32;
unsafe {
let mut i = 0usize;
let mut acc = vdupq_n_f32(0.0);
while i + 4 <= n {
let p = vld1q_f32(power_slice.as_ptr().add(i));
let w = vld1q_f32(weights.as_ptr().add(i));
acc = vfmaq_f32(acc, p, w);
i += 4;
}
energy += vaddvq_f32(acc);
for k in i..n {
energy += *power_slice.get_unchecked(k) * *weights.get_unchecked(k);
}
}
energy.max(crate::features::LOG_FLOOR).ln()
}