use crate::common::diagnostics::NamErrorCode;
use crate::math::common::{
Avx2Math, Avx512Math, Avx512VnniBf16Math, InstructionSet, SimdMath, effective_instruction_set,
};
use super::super::sinc_kernel::{NUM_PHASES, PolyphaseBank};
use super::delay_line::DelayLine;
pub(crate) struct ResamplerCore {
pub bank: PolyphaseBank,
pub state_l: DelayLine,
pub state_r: DelayLine,
pub phase_accum: u64,
pub phase_step: u64,
pub group_delay: f64,
}
impl ResamplerCore {
pub fn new(from_rate: u32, to_rate: u32, bank: PolyphaseBank) -> Result<Self, NamErrorCode> {
let group_delay = bank.group_delay;
let phase_step_f = (from_rate as f64 / to_rate as f64) * NUM_PHASES as f64;
let phase_step = (phase_step_f * ((1u64 << 40) as f64)).round() as u64;
Ok(Self {
bank,
state_l: DelayLine::new()?,
state_r: DelayLine::new()?,
phase_accum: (NUM_PHASES as u64) << 40,
phase_step,
group_delay,
})
}
#[inline]
pub fn group_delay(&self) -> f64 {
self.group_delay
}
fn process_internal<M: SimdMath>(
&mut self,
in_l: &[f32],
in_r: &[f32],
out_l: &mut [f32],
out_r: &mut [f32],
) -> usize {
let n_in = in_l.len().min(in_r.len());
let n_out_max = out_l.len().min(out_r.len());
let mut in_idx = 0usize;
let mut out_idx = 0usize;
let num_phases_fp = (NUM_PHASES as u64) << 40;
while out_idx < n_out_max {
while self.phase_accum >= num_phases_fp {
if in_idx >= n_in {
return out_idx;
}
unsafe {
self.state_l.push(*in_l.get_unchecked(in_idx));
self.state_r.push(*in_r.get_unchecked(in_idx));
}
self.phase_accum -= num_phases_fp;
in_idx += 1;
}
let phase_idx = (self.phase_accum >> 40) as usize;
const FRAC_MASK: u64 = (1u64 << 40) - 1;
let frac_bits = self.phase_accum & FRAC_MASK;
let frac = ((frac_bits >> 9) as i32 as f32) * (1.0 / (1u32 << 31) as f32);
let phase_next = if phase_idx + 1 >= NUM_PHASES {
0
} else {
phase_idx + 1
};
let (y_l, y_r) = unsafe {
let c0 = self.bank.phase_ptr(phase_idx);
let c1 = self.bank.phase_ptr(phase_next);
let x_l = self.state_l.window_ptr();
let x_r = self.state_r.window_ptr();
let taps = self.bank.taps_per_phase;
let ((y0_l, y0_r), (y1_l, y1_r)) = M::convolve_stereo_dual(c0, c1, x_l, x_r, taps);
(y0_l + frac * (y1_l - y0_l), y0_r + frac * (y1_r - y0_r))
};
unsafe {
*out_l.get_unchecked_mut(out_idx) = y_l;
*out_r.get_unchecked_mut(out_idx) = y_r;
}
out_idx += 1;
self.phase_accum += self.phase_step;
}
while self.phase_accum >= num_phases_fp && in_idx < n_in {
unsafe {
self.state_l.push(*in_l.get_unchecked(in_idx));
self.state_r.push(*in_r.get_unchecked(in_idx));
}
self.phase_accum -= num_phases_fp;
in_idx += 1;
}
out_idx
}
fn process_internal_mono<M: SimdMath>(
&mut self,
in_l: &[f32],
out_l: &mut [f32],
out_r: &mut [f32],
) -> usize {
let n_in = in_l.len();
let n_out_max = out_l.len().min(out_r.len());
let mut in_idx = 0usize;
let mut out_idx = 0usize;
let num_phases_fp = (NUM_PHASES as u64) << 40;
while out_idx < n_out_max {
while self.phase_accum >= num_phases_fp {
if in_idx >= n_in {
return out_idx;
}
unsafe {
self.state_l.push(*in_l.get_unchecked(in_idx));
}
self.phase_accum -= num_phases_fp;
in_idx += 1;
}
let phase_idx = (self.phase_accum >> 40) as usize;
const FRAC_MASK: u64 = (1u64 << 40) - 1;
let frac_bits = self.phase_accum & FRAC_MASK;
let frac = ((frac_bits >> 9) as i32 as f32) * (1.0 / (1u32 << 31) as f32);
let phase_next = if phase_idx + 1 >= NUM_PHASES {
0
} else {
phase_idx + 1
};
let y_l = unsafe {
let c0 = self.bank.phase_ptr(phase_idx);
let c1 = self.bank.phase_ptr(phase_next);
let x_l = self.state_l.window_ptr();
let taps = self.bank.taps_per_phase;
let (y0_l, y1_l) = M::convolve_mono_dual(c0, c1, x_l, taps);
y0_l + frac * (y1_l - y0_l)
};
unsafe {
*out_l.get_unchecked_mut(out_idx) = y_l;
*out_r.get_unchecked_mut(out_idx) = y_l;
}
out_idx += 1;
self.phase_accum += self.phase_step;
}
while self.phase_accum >= num_phases_fp && in_idx < n_in {
unsafe {
self.state_l.push(*in_l.get_unchecked(in_idx));
}
self.phase_accum -= num_phases_fp;
in_idx += 1;
}
out_idx
}
#[inline(always)]
pub(crate) fn process_static_stereo(
&mut self,
in_l: &[f32],
in_r: &[f32],
out_l: &mut [f32],
out_r: &mut [f32],
) -> usize {
match effective_instruction_set() {
InstructionSet::Avx2 => self.process_internal::<Avx2Math>(in_l, in_r, out_l, out_r),
InstructionSet::Avx512 => self.process_internal::<Avx512Math>(in_l, in_r, out_l, out_r),
InstructionSet::Avx512VnniBf16 => {
self.process_internal::<Avx512VnniBf16Math>(in_l, in_r, out_l, out_r)
}
}
}
#[inline(always)]
pub(crate) fn process_static_mono(
&mut self,
in_l: &[f32],
out_l: &mut [f32],
out_r: &mut [f32],
) -> usize {
match effective_instruction_set() {
InstructionSet::Avx2 => self.process_internal_mono::<Avx2Math>(in_l, out_l, out_r),
InstructionSet::Avx512 => self.process_internal_mono::<Avx512Math>(in_l, out_l, out_r),
InstructionSet::Avx512VnniBf16 => {
self.process_internal_mono::<Avx512VnniBf16Math>(in_l, out_l, out_r)
}
}
}
}