use super::stage::X2Stage;
use crate::common::diagnostics::NamErrorCode;
use crate::math::common::AlignedVec;
use log::info;
pub(crate) const HB_TAPS: usize = 25;
pub(crate) const HB_DELAY: usize = HB_TAPS / 2;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
pub enum OversampleFactor {
#[default]
Off,
X2,
X4,
}
impl OversampleFactor {
pub fn from_f32(val: f32) -> Self {
match val.round() as i32 {
1 => Self::X2,
2 => Self::X4,
_ => Self::Off,
}
}
pub fn to_f32(self) -> f32 {
match self {
Self::Off => 0.0,
Self::X2 => 1.0,
Self::X4 => 2.0,
}
}
#[inline]
pub const fn multiplier(self) -> usize {
match self {
Self::Off => 1,
Self::X2 => 2,
Self::X4 => 4,
}
}
#[inline]
pub const fn stage_count(self) -> usize {
match self {
Self::Off => 0,
Self::X2 => 1,
Self::X4 => 2,
}
}
}
enum OsStages {
Off,
X2 {
stage1: X2Stage,
},
X4 {
stage1: X2Stage,
stage2: Box<X2Stage>,
},
}
pub struct OversampleEngine {
factor: OversampleFactor,
stages: OsStages,
inter_buf: AlignedVec<f32>,
max_samples: usize,
}
impl OversampleEngine {
pub fn new(factor: OversampleFactor, max_input_samples: usize) -> Result<Self, NamErrorCode> {
let inter_size = if factor.stage_count() >= 2 {
max_input_samples * 2
} else {
1
};
let stages = match factor {
OversampleFactor::Off => OsStages::Off,
OversampleFactor::X2 => OsStages::X2 {
stage1: X2Stage::new()?,
},
OversampleFactor::X4 => OsStages::X4 {
stage1: X2Stage::new()?,
stage2: Box::new(X2Stage::new()?),
},
};
let latency = match factor {
OversampleFactor::Off => 0,
OversampleFactor::X2 => HB_DELAY,
OversampleFactor::X4 => 2 * HB_DELAY,
};
info!(
"[Oversample] Engine built: factor={:?}, max_input={}, latency={} samples",
factor, max_input_samples, latency
);
Ok(Self {
factor,
stages,
inter_buf: AlignedVec::new(inter_size, 0.0f32)?,
max_samples: max_input_samples,
})
}
#[inline]
pub fn factor(&self) -> OversampleFactor {
self.factor
}
#[inline]
pub fn is_bypass(&self) -> bool {
matches!(self.stages, OsStages::Off)
}
#[inline]
pub fn latency_samples(&self) -> usize {
match self.factor {
OversampleFactor::Off => 0,
OversampleFactor::X2 => HB_DELAY,
OversampleFactor::X4 => 2 * HB_DELAY,
}
}
pub fn upsample(&mut self, input: &[f32], output: &mut [f32]) -> usize {
let n_in = input.len().min(self.max_samples);
let input = &input[..n_in];
debug_assert!(input.len() <= self.max_samples);
debug_assert!(
output.len() >= input.len() * self.factor.multiplier(),
"oversample: output buffer too small for upsampling factor"
);
match &mut self.stages {
OsStages::Off => {
let n = input.len().min(output.len());
unsafe {
core::ptr::copy_nonoverlapping(input.as_ptr(), output.as_mut_ptr(), n);
}
n
}
OsStages::X2 { stage1 } => stage1.upsample(input, output),
OsStages::X4 { stage1, stage2 } => {
let n_x2 = stage1.upsample(input, &mut self.inter_buf[..input.len() * 2]);
stage2.upsample(&self.inter_buf[..n_x2], output)
}
}
}
pub fn downsample(&mut self, input: &[f32], output: &mut [f32]) -> usize {
let max_os = self.max_samples * self.factor.multiplier();
let n_in = input.len().min(max_os);
let input = &input[..n_in];
debug_assert!(
output.len() >= input.len() / self.factor.multiplier(),
"oversample: output buffer too small for downsampling factor"
);
match &mut self.stages {
OsStages::Off => {
let n = input.len().min(output.len());
unsafe {
core::ptr::copy_nonoverlapping(input.as_ptr(), output.as_mut_ptr(), n);
}
n
}
OsStages::X2 { stage1 } => stage1.downsample(input, output),
OsStages::X4 { stage1, stage2 } => {
let n_x2 = stage2.downsample(input, &mut self.inter_buf[..input.len() / 2]);
stage1.downsample(&self.inter_buf[..n_x2], output)
}
}
}
}
pub struct OsEnginePair {
pub l: Box<OversampleEngine>,
pub r: Box<OversampleEngine>,
}
#[cfg(test)]
#[path = "oversample_test.rs"]
mod oversample_test;