#![allow(unsafe_code)]
#![allow(clippy::missing_safety_doc)]
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
use crate::features::{FRAME_LENGTH_SAMPLES, NUM_MEL_BINS, scalar};
#[inline]
#[target_feature(enable = "avx2,fma")]
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 = _mm256_setzero_ps();
let mut i = 0usize;
while i + 8 <= n {
let v = _mm256_loadu_ps(window.as_ptr().add(i));
acc = _mm256_add_ps(acc, v);
i += 8;
}
let lo = _mm256_castps256_ps128(acc);
let hi = _mm256_extractf128_ps::<1>(acc);
let s = _mm_add_ps(lo, hi);
let s = _mm_hadd_ps(s, s);
let s = _mm_hadd_ps(s, s);
let mut sum = _mm_cvtss_f32(s);
for k in i..n {
sum += *window.get_unchecked(k);
}
let mean = sum / FRAME_LENGTH_SAMPLES as f32;
let mean_v = _mm256_set1_ps(mean);
let mut i = 0usize;
while i + 8 <= n {
let v = _mm256_loadu_ps(window.as_ptr().add(i));
_mm256_storeu_ps(out.as_mut_ptr().add(i), _mm256_sub_ps(v, mean_v));
i += 8;
}
debug_assert_eq!(i, n);
}
}
#[inline]
#[target_feature(enable = "avx2,fma")]
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 + 8 <= n {
let s = _mm256_loadu_ps(samples.as_ptr().add(i));
let w = _mm256_loadu_ps(window.as_ptr().add(i));
_mm256_storeu_ps(samples.as_mut_ptr().add(i), _mm256_mul_ps(s, w));
i += 8;
}
debug_assert_eq!(i, n);
}
}
#[inline]
#[target_feature(enable = "avx2,fma")]
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 + 8 <= n {
let a = _mm256_loadu_ps(base.add(i * 2));
let b = _mm256_loadu_ps(base.add(i * 2 + 8));
let re = _mm256_shuffle_ps::<0b10_00_10_00>(a, b);
let im = _mm256_shuffle_ps::<0b11_01_11_01>(a, b);
let p_swizzled = _mm256_fmadd_ps(re, re, _mm256_mul_ps(im, im));
let permuted = _mm256_castpd_ps(_mm256_permute4x64_pd::<0b11_01_10_00>(_mm256_castps_pd(
p_swizzled,
)));
_mm256_storeu_ps(out.as_mut_ptr().add(i), permuted);
i += 8;
}
if i < n {
scalar::power_spectrum(&complex[i..], &mut out[i..]);
}
}
}
#[inline]
#[target_feature(enable = "avx2,fma")]
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 + 8 <= n {
let f = _mm256_loadu_ps(feature.as_ptr().add(i));
let m = _mm256_loadu_ps(means.as_ptr().add(i));
let s = _mm256_loadu_ps(istd.as_ptr().add(i));
let r = _mm256_mul_ps(_mm256_sub_ps(f, m), s);
_mm256_storeu_ps(feature.as_mut_ptr().add(i), r);
i += 8;
}
debug_assert_eq!(i, n);
}
}
#[inline]
#[target_feature(enable = "avx2,fma")]
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 = _mm256_setzero_ps();
while i + 8 <= n {
let p = _mm256_loadu_ps(power_slice.as_ptr().add(i));
let w = _mm256_loadu_ps(weights.as_ptr().add(i));
acc = _mm256_fmadd_ps(p, w, acc);
i += 8;
}
let lo = _mm256_castps256_ps128(acc);
let hi = _mm256_extractf128_ps::<1>(acc);
let s = _mm_add_ps(lo, hi);
let s = _mm_hadd_ps(s, s);
let s = _mm_hadd_ps(s, s);
energy += _mm_cvtss_f32(s);
for k in i..n {
energy += *power_slice.get_unchecked(k) * *weights.get_unchecked(k);
}
}
energy.max(crate::features::LOG_FLOOR).ln()
}