use anyhow::{Result, bail};
use log::info;
use std::ptr;
const MIN_RATE: u32 = 4_000;
const MAX_RATE: u32 = 384_000;
use super::sinc_kernel::{generate_polyphase_bank, generate_polyphase_bank_linear};
mod core;
mod delay_line;
use core::ResamplerCore;
pub struct NamResampler {
inner: Option<ResamplerCore>,
outer: Option<ResamplerCore>,
pw_rate: u32,
nam_rate: u32,
}
impl NamResampler {
#[cold]
pub fn new(pw_rate: u32, nam_rate: u32, _chunk_size: usize) -> Result<Self> {
if !(MIN_RATE..=MAX_RATE).contains(&pw_rate) || !(MIN_RATE..=MAX_RATE).contains(&nam_rate) {
bail!(
"NamResampler: sample rates must be in range {}-{}, got pw={} nam={}",
MIN_RATE,
MAX_RATE,
pw_rate,
nam_rate
);
}
if pw_rate == nam_rate {
info!(
"[Resampler] Bypass: pw_rate={}, nam_rate={} (match)",
pw_rate, nam_rate
);
return Ok(Self {
inner: None,
outer: None,
pw_rate,
nam_rate,
});
}
let inner = ResamplerCore::new(
pw_rate,
nam_rate,
generate_polyphase_bank(pw_rate, nam_rate).map_err(|e| anyhow::anyhow!("{e}"))?,
)?;
let outer = ResamplerCore::new(
nam_rate,
pw_rate,
generate_polyphase_bank(nam_rate, pw_rate).map_err(|e| anyhow::anyhow!("{e}"))?,
)?;
info!(
"[Resampler] Minimum-phase resampler built: pw_rate={}, nam_rate={}",
pw_rate, nam_rate
);
Ok(Self {
inner: Some(inner),
outer: Some(outer),
pw_rate,
nam_rate,
})
}
#[cold]
pub fn new_linear(pw_rate: u32, nam_rate: u32, _chunk_size: usize) -> Result<Self> {
if !(MIN_RATE..=MAX_RATE).contains(&pw_rate) || !(MIN_RATE..=MAX_RATE).contains(&nam_rate) {
bail!(
"NamResampler: sample rates must be in range {}-{}, got pw={} nam={}",
MIN_RATE,
MAX_RATE,
pw_rate,
nam_rate
);
}
if pw_rate == nam_rate {
info!(
"[Resampler] Linear-phase bypass: pw_rate={}, nam_rate={} (match)",
pw_rate, nam_rate
);
return Ok(Self {
inner: None,
outer: None,
pw_rate,
nam_rate,
});
}
let inner = ResamplerCore::new(
pw_rate,
nam_rate,
generate_polyphase_bank_linear(pw_rate, nam_rate)
.map_err(|e| anyhow::anyhow!("{e}"))?,
)?;
let outer = ResamplerCore::new(
nam_rate,
pw_rate,
generate_polyphase_bank_linear(nam_rate, pw_rate)
.map_err(|e| anyhow::anyhow!("{e}"))?,
)?;
info!(
"[Resampler] Linear-phase resampler built: pw_rate={}, nam_rate={}",
pw_rate, nam_rate
);
Ok(Self {
inner: Some(inner),
outer: Some(outer),
pw_rate,
nam_rate,
})
}
#[inline]
pub fn is_bypass(&self) -> bool {
self.inner.is_none()
}
#[inline]
pub fn pw_rate(&self) -> u32 {
self.pw_rate
}
#[inline]
pub fn nam_rate(&self) -> u32 {
self.nam_rate
}
pub fn latency_samples(&self, _host_rate: u32) -> u32 {
if self.is_bypass() {
return 0;
}
let delay_in = match self.inner {
Some(ref core) => core.group_delay(),
None => 0.0,
};
let delay_out = match self.outer {
Some(ref core) => core.group_delay() * (self.pw_rate as f64 / self.nam_rate as f64),
None => 0.0,
};
(delay_in + delay_out).round() as u32
}
pub fn process_input(
&mut self,
in_l: &[f32],
in_r: &[f32],
out_l: &mut [f32],
out_r: &mut [f32],
) -> usize {
let Some(ref mut core) = self.inner else {
let n = in_l.len().min(in_r.len()).min(out_l.len()).min(out_r.len());
unsafe {
ptr::copy_nonoverlapping(in_l.as_ptr(), out_l.as_mut_ptr(), n);
ptr::copy_nonoverlapping(in_r.as_ptr(), out_r.as_mut_ptr(), n);
}
return n;
};
core.process_static_stereo(in_l, in_r, out_l, out_r)
}
pub fn process_output(
&mut self,
in_l: &[f32],
in_r: &[f32],
out_l: &mut [f32],
out_r: &mut [f32],
) -> usize {
let Some(ref mut core) = self.outer else {
let n = in_l.len().min(in_r.len()).min(out_l.len()).min(out_r.len());
unsafe {
ptr::copy_nonoverlapping(in_l.as_ptr(), out_l.as_mut_ptr(), n);
ptr::copy_nonoverlapping(in_r.as_ptr(), out_r.as_mut_ptr(), n);
}
return n;
};
core.process_static_stereo(in_l, in_r, out_l, out_r)
}
pub fn process_input_mono(
&mut self,
in_l: &[f32],
out_l: &mut [f32],
out_r: &mut [f32],
) -> usize {
let Some(ref mut core) = self.inner else {
let n = in_l.len().min(out_l.len()).min(out_r.len());
unsafe {
ptr::copy_nonoverlapping(in_l.as_ptr(), out_l.as_mut_ptr(), n);
ptr::copy_nonoverlapping(in_l.as_ptr(), out_r.as_mut_ptr(), n);
}
return n;
};
core.process_static_mono(in_l, out_l, out_r)
}
pub fn process_output_mono(
&mut self,
in_l: &[f32],
out_l: &mut [f32],
out_r: &mut [f32],
) -> usize {
let Some(ref mut core) = self.outer else {
let n = in_l.len().min(out_l.len()).min(out_r.len());
unsafe {
ptr::copy_nonoverlapping(in_l.as_ptr(), out_l.as_mut_ptr(), n);
ptr::copy_nonoverlapping(in_l.as_ptr(), out_r.as_mut_ptr(), n);
}
return n;
};
core.process_static_mono(in_l, out_l, out_r)
}
}
#[cfg(test)]
#[path = "../resampler_test.rs"]
mod resampler_test;