100 lines
3.2 KiB
Rust
Executable File
100 lines
3.2 KiB
Rust
Executable File
use crate::I24;
|
|
|
|
/// Applique un gain (en dB) sur des échantillons stéréo interleavés `[L,R]`
|
|
/// codés sur 24 bits signés (`I24`).
|
|
pub fn apply_gain_stereo_i24(samples: &mut [[I24; 2]], gain_db: f64) {
|
|
let gain = 10f64.powf(gain_db / 20.0);
|
|
// Q23 scaling
|
|
let g_q23 = (gain * (1u64 << 23) as f64).round() as i32;
|
|
|
|
#[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
|
|
unsafe {
|
|
apply_gain_stereo_i24_neon(samples, g_q23);
|
|
return;
|
|
}
|
|
#[cfg(all(target_arch = "x86_64", target_feature = "avx2"))]
|
|
unsafe {
|
|
apply_gain_stereo_i24_avx2(samples, g_q23);
|
|
return;
|
|
}
|
|
|
|
// Fallback scalaire
|
|
#[cfg(not(any(
|
|
all(target_arch = "aarch64", target_feature = "neon"),
|
|
all(target_arch = "x86_64", target_feature = "avx2")
|
|
)))]
|
|
{
|
|
apply_gain_stereo_i24_scalar(samples, g_q23);
|
|
}
|
|
}
|
|
|
|
#[cfg(not(any(
|
|
all(target_arch = "aarch64", target_feature = "neon"),
|
|
all(target_arch = "x86_64", target_feature = "avx2")
|
|
)))]
|
|
#[inline(always)]
|
|
fn apply_gain_stereo_i24_scalar(samples: &mut [[I24; 2]], g_q23: i32) {
|
|
for frame in samples.iter_mut() {
|
|
// L
|
|
let prod_l = (frame[0].as_i32() as i64 * g_q23 as i64 + (1 << 22)) >> 23;
|
|
let clamped_l = prod_l.clamp(I24::MIN_VALUE as i64, I24::MAX_VALUE as i64) as i32;
|
|
frame[0] = I24::new_clamped(clamped_l);
|
|
|
|
// R
|
|
let prod_r = (frame[1].as_i32() as i64 * g_q23 as i64 + (1 << 22)) >> 23;
|
|
let clamped_r = prod_r.clamp(I24::MIN_VALUE as i64, I24::MAX_VALUE as i64) as i32;
|
|
frame[1] = I24::new_clamped(clamped_r);
|
|
}
|
|
}
|
|
|
|
#[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
|
|
#[inline(always)]
|
|
unsafe fn apply_gain_stereo_i24_neon(samples: &mut [[I24; 2]], g_q23: i32) {
|
|
use core::arch::aarch64::*;
|
|
let gvec = vdupq_n_s32(g_q23);
|
|
let mut i = 0;
|
|
let n = samples.len() * 2;
|
|
let ptr = samples.as_mut_ptr() as *mut i32;
|
|
|
|
while i + 4 <= n {
|
|
let v = vld1q_s32(ptr.add(i));
|
|
let res = vqdmulhq_s32(v, gvec); // Q23 multiply high
|
|
vst1q_s32(ptr.add(i), res);
|
|
i += 4;
|
|
}
|
|
|
|
// reste scalaire
|
|
let slice = std::slice::from_raw_parts_mut(ptr.add(i), n - i);
|
|
apply_gain_i24_scalar(slice, g_q23);
|
|
}
|
|
|
|
#[cfg(all(target_arch = "x86_64", target_feature = "avx2"))]
|
|
#[inline(always)]
|
|
unsafe fn apply_gain_stereo_i24_avx2(samples: &mut [[I24; 2]], g_q23: i32) {
|
|
use core::arch::x86_64::*;
|
|
let g = _mm256_set1_epi32(g_q23);
|
|
let mut i = 0;
|
|
let n = samples.len() * 2;
|
|
let ptr = samples.as_mut_ptr() as *mut i32;
|
|
|
|
while i + 8 <= n {
|
|
let x = _mm256_loadu_si256(ptr.add(i) as *const __m256i);
|
|
let hi = _mm256_mulhi_epi32(x, g);
|
|
_mm256_storeu_si256(ptr.add(i) as *mut __m256i, hi);
|
|
i += 8;
|
|
}
|
|
|
|
// reste scalaire
|
|
let slice = std::slice::from_raw_parts_mut(ptr.add(i), n - i);
|
|
apply_gain_i24_scalar(slice, g_q23);
|
|
}
|
|
|
|
/// Version mono utilisée pour le reste scalaire.
|
|
#[inline(always)]
|
|
fn apply_gain_i24_scalar(samples: &mut [i32], g_q23: i32) {
|
|
for s in samples.iter_mut() {
|
|
let prod = (*s as i64 * g_q23 as i64 + (1 << 22)) >> 23;
|
|
*s = prod.clamp(I24::MIN_VALUE as i64, I24::MAX_VALUE as i64) as i32;
|
|
}
|
|
}
|