use scirs2_core::ndarray::{Array1, ArrayView1};
use scirs2_core::simd_ops::SimdUnifiedOps;
pub struct SimdAudioProcessor;
impl SimdAudioProcessor {
pub fn mix_simd(samples: &mut [f32], other: &[f32], mix_factor: f32) {
let len = samples.len().min(other.len());
let samples_array = ArrayView1::from(&samples[..len]);
let other_array = ArrayView1::from(&other[..len]);
let scaled_other = f32::simd_scalar_mul(&other_array, mix_factor);
let result = f32::simd_add(&samples_array, &scaled_other.view());
samples[..len].copy_from_slice(result.as_slice().expect("value should be present"));
}
pub fn scale_simd(samples: &mut [f32], scale_factor: f32) {
let samples_array = ArrayView1::from(&samples[..]);
let result = f32::simd_scalar_mul(&samples_array, scale_factor);
samples.copy_from_slice(result.as_slice().expect("value should be present"));
}
pub fn compute_rms_simd(samples: &[f32]) -> f32 {
if samples.is_empty() {
return 0.0;
}
let samples_array = ArrayView1::from(samples);
let sum_squares = f32::simd_sum_squares(&samples_array);
(sum_squares / samples.len() as f32).sqrt()
}
pub fn compute_sum_simd(samples: &[f32]) -> f32 {
if samples.is_empty() {
return 0.0;
}
let samples_array = ArrayView1::from(samples);
f32::simd_sum(&samples_array)
}
pub fn compute_mean_simd(samples: &[f32]) -> f32 {
if samples.is_empty() {
return 0.0;
}
let samples_array = ArrayView1::from(samples);
f32::simd_mean(&samples_array)
}
pub fn apply_gain_with_clipping(samples: &mut [f32], gain: f32, threshold: f32) {
Self::scale_simd(samples, gain);
for sample in samples.iter_mut() {
*sample = sample.clamp(-threshold, threshold);
}
}
pub fn compute_peak_simd(samples: &[f32]) -> f32 {
if samples.is_empty() {
return 0.0;
}
let samples_array = ArrayView1::from(samples);
let abs_samples = f32::simd_abs(&samples_array);
f32::simd_max_element(&abs_samples.view())
}
pub fn normalize_simd(samples: &mut [f32], target_peak: f32) {
let current_peak = Self::compute_peak_simd(samples);
if current_peak > 0.0 {
let scale_factor = target_peak / current_peak;
Self::scale_simd(samples, scale_factor);
}
}
pub fn multiply_simd(samples: &mut [f32], other: &[f32]) {
let len = samples.len().min(other.len());
let samples_array = ArrayView1::from(&samples[..len]);
let other_array = ArrayView1::from(&other[..len]);
let result = f32::simd_mul(&samples_array, &other_array);
samples[..len].copy_from_slice(result.as_slice().expect("value should be present"));
}
pub fn fused_multiply_add(samples: &mut [f32], a: &[f32], b: &[f32], c: &[f32]) {
let len = samples.len().min(a.len()).min(b.len()).min(c.len());
let a_array = ArrayView1::from(&a[..len]);
let b_array = ArrayView1::from(&b[..len]);
let c_array = ArrayView1::from(&c[..len]);
let result = f32::simd_fma(&a_array, &b_array, &c_array);
samples[..len].copy_from_slice(result.as_slice().expect("value should be present"));
}
}
pub struct SimdCapabilities;
impl SimdCapabilities {
pub fn detect() -> String {
if f32::simd_available() {
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx512f") {
return "AVX512 (16-wide) via SciRS2-Core".to_string();
} else if is_x86_feature_detected!("avx2") {
return "AVX2 (8-wide) via SciRS2-Core".to_string();
} else if is_x86_feature_detected!("sse2") {
return "SSE2 (4-wide) via SciRS2-Core".to_string();
}
}
#[cfg(target_arch = "aarch64")]
{
return "NEON (4-wide) via SciRS2-Core".to_string();
}
#[allow(unreachable_code)]
"SIMD Available via SciRS2-Core".to_string()
} else {
"Scalar (no SIMD)".to_string()
}
}
pub fn vector_width() -> usize {
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx512f") {
return 16;
} else if is_x86_feature_detected!("avx2") {
return 8;
} else if is_x86_feature_detected!("sse2") {
return 4;
}
}
#[cfg(target_arch = "aarch64")]
{
return 4;
}
#[allow(unreachable_code)]
1
}
pub fn is_available() -> bool {
f32::simd_available()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mix_simd_basic() {
let mut dest = vec![1.0f32; 1024];
let src = vec![0.5f32; 1024];
SimdAudioProcessor::mix_simd(&mut dest, &src, 0.5);
for &sample in &dest {
assert!((sample - 1.25).abs() < 1e-5);
}
}
#[test]
fn test_scale_simd_basic() {
let mut samples = vec![0.5f32; 1024];
SimdAudioProcessor::scale_simd(&mut samples, 2.0);
for &sample in &samples {
assert!((sample - 1.0).abs() < 1e-5);
}
}
#[test]
fn test_rms_simd_basic() {
let samples = vec![0.5f32; 1024];
let rms = SimdAudioProcessor::compute_rms_simd(&samples);
assert!((rms - 0.5).abs() < 1e-5);
}
#[test]
fn test_rms_simd_zero() {
let samples = vec![0.0f32; 1024];
let rms = SimdAudioProcessor::compute_rms_simd(&samples);
assert!((rms - 0.0).abs() < 1e-9);
}
#[test]
fn test_sum_simd() {
let samples = vec![0.5f32; 1024];
let sum = SimdAudioProcessor::compute_sum_simd(&samples);
assert!((sum - 512.0).abs() < 1e-3);
}
#[test]
fn test_mean_simd() {
let samples = vec![0.5f32; 1024];
let mean = SimdAudioProcessor::compute_mean_simd(&samples);
assert!((mean - 0.5).abs() < 1e-5);
}
#[test]
fn test_peak_simd() {
let samples = vec![-0.8f32, 0.5, -0.3, 0.9, 0.2];
let peak = SimdAudioProcessor::compute_peak_simd(&samples);
assert!((peak - 0.9).abs() < 1e-5);
}
#[test]
fn test_normalize_simd() {
let mut samples = vec![0.5f32; 1024];
SimdAudioProcessor::normalize_simd(&mut samples, 0.9);
let peak = SimdAudioProcessor::compute_peak_simd(&samples);
assert!((peak - 0.9).abs() < 1e-3);
}
#[test]
fn test_multiply_simd() {
let mut samples = vec![2.0f32; 1024];
let other = vec![0.5f32; 1024];
SimdAudioProcessor::multiply_simd(&mut samples, &other);
for &sample in &samples {
assert!((sample - 1.0).abs() < 1e-5);
}
}
#[test]
fn test_fma_simd() {
let mut samples = vec![0.0f32; 1024];
let a = vec![2.0f32; 1024];
let b = vec![0.5f32; 1024];
let c = vec![0.5f32; 1024];
SimdAudioProcessor::fused_multiply_add(&mut samples, &a, &b, &c);
for &sample in &samples {
assert!((sample - 1.5).abs() < 1e-5); }
}
#[test]
fn test_gain_with_clipping() {
let mut samples = vec![0.5f32; 1024];
SimdAudioProcessor::apply_gain_with_clipping(&mut samples, 2.0, 0.8);
for &sample in &samples {
assert!(sample.abs() <= 0.8 + 1e-5);
}
}
#[test]
fn test_simd_detection() {
let caps = SimdCapabilities::detect();
assert!(!caps.is_empty());
let width = SimdCapabilities::vector_width();
assert!(width >= 1);
assert!(width <= 16);
let available = SimdCapabilities::is_available();
println!("SIMD available: {}", available);
}
#[test]
fn test_mix_simd_unequal_lengths() {
let mut dest = vec![1.0f32; 100];
let src = vec![0.5f32; 50];
SimdAudioProcessor::mix_simd(&mut dest, &src, 1.0);
for &sample in &dest[0..50] {
assert!((sample - 1.5).abs() < 1e-5);
}
for &sample in &dest[50..100] {
assert!((sample - 1.0).abs() < 1e-5);
}
}
#[test]
fn test_mix_simd_non_aligned() {
for len in [1, 3, 7, 13, 17, 31, 33, 127] {
let mut dest = vec![1.0f32; len];
let src = vec![0.5f32; len];
SimdAudioProcessor::mix_simd(&mut dest, &src, 0.5);
for &sample in &dest {
assert!((sample - 1.25).abs() < 1e-5);
}
}
}
}