use assume::assume;
use super::{AudioResampler, ResamplingSpecs};
use crate::Error;
#[derive(Clone)]
struct CubicInterpolator {
input: [f32; 4],
sub_pos: f32,
ratio: f32,
is_initialized: bool,
}
impl CubicInterpolator {
pub fn new(ratio: f32) -> Self {
let input = [0.0, 0.0, 0.0, 0.0];
let sub_pos = 0.0;
let is_initialized = false;
Self {
input,
sub_pos,
ratio,
is_initialized,
}
}
pub fn reset(&mut self) {
self.input.fill(0.0);
self.sub_pos = 0.0;
self.is_initialized = false;
}
pub fn process(
&mut self,
input: &[f32],
output: &mut [f32],
channel_index: usize,
channel_count: usize,
) -> (usize, usize) {
debug_assert!(input.len().is_multiple_of(channel_count));
debug_assert!(output.len().is_multiple_of(channel_count));
debug_assert!(channel_index < channel_count);
let num_in = input.len() / channel_count;
let num_out = output.len() / channel_count;
let mut num_consumed = 0;
let mut num_produced = 0;
if (self.ratio - 1.0).abs() < 0.000001 {
let min = input.len().min(output.len());
output[..min].copy_from_slice(&input[..min]);
return (min, min);
}
if !self.is_initialized && num_in >= 3 {
self.is_initialized = true;
for f in 0..3 {
let index = f * channel_count + channel_index;
assume!(unsafe: index < input.len()); self.push_sample(input[index]);
num_consumed += 1;
}
}
if self.ratio < 1.0 {
while num_produced < num_out {
if self.sub_pos >= 1.0 {
if num_consumed >= num_in {
break;
}
let input_index = num_consumed * channel_count + channel_index;
assume!(unsafe: input_index < input.len()); self.push_sample(input[input_index]);
num_consumed += 1;
self.sub_pos -= 1.0;
}
let output_index = num_produced * channel_count + channel_index;
assume!(unsafe: output_index < output.len()); output[output_index] = self.interpolate(self.sub_pos);
num_produced += 1;
self.sub_pos += self.ratio;
}
} else {
'outer_loop: while num_produced < num_out {
while self.sub_pos < self.ratio {
if num_consumed >= num_in {
break 'outer_loop;
}
let input_index = num_consumed * channel_count + channel_index;
assume!(unsafe: input_index < input.len()); self.push_sample(input[input_index]);
num_consumed += 1;
self.sub_pos += 1.0;
}
self.sub_pos -= self.ratio;
let output_index = num_produced * channel_count + channel_index;
assume!(unsafe: output_index < output.len()); output[output_index] = self.interpolate(1.0 - self.sub_pos);
num_produced += 1;
}
}
(num_consumed * channel_count, num_produced * channel_count)
}
#[inline(always)]
fn push_sample(&mut self, new_value: f32) {
self.input[3] = self.input[2];
self.input[2] = self.input[1];
self.input[1] = self.input[0];
self.input[0] = new_value;
}
#[inline(always)]
fn interpolate(&self, fraction: f32) -> f32 {
debug_assert!((0.0..=1.0).contains(&fraction));
let ym1 = self.input[3];
let y0 = self.input[2];
let y1 = self.input[1];
let y2 = self.input[0];
let c0 = y0;
let c1 = (y1 - ym1) * 0.5;
let c2 = ym1 - y0 * 2.5 + y1 * 2.0 - y2 * 0.5;
let c3 = (y2 - ym1) * 0.5 + (y0 - y1) * 1.5;
((c3 * fraction + c2) * fraction + c1) * fraction + c0
}
}
pub struct CubicResampler {
spec: ResamplingSpecs,
interpolators: Vec<CubicInterpolator>,
}
impl CubicResampler {
pub fn new(spec: ResamplingSpecs) -> Result<Self, Error> {
assert!(
spec.output_ratio() > 0.0 && spec.output_ratio().is_finite(),
"Invalid resampling ratio",
);
Ok(Self {
spec,
interpolators: vec![
CubicInterpolator::new(spec.input_ratio() as f32);
spec.channel_count
],
})
}
}
impl AudioResampler for CubicResampler {
fn required_input_buffer_size(&self) -> Option<usize> {
None
}
fn max_input_buffer_size(&self) -> Option<usize> {
None
}
fn process(&mut self, input: &[f32], output: &mut [f32]) -> Result<(usize, usize), Error> {
let channel_count = self.spec.channel_count;
let mut result = (0, 0);
for (channel_index, interpolator) in self.interpolators.iter_mut().enumerate() {
result = interpolator.process(input, output, channel_index, channel_count);
}
Ok(result)
}
fn update(&mut self, input_rate: u32, output_rate: u32) -> Result<(), Error> {
self.spec.input_rate = input_rate;
self.spec.output_rate = output_rate;
let new_ratio = self.spec.input_ratio() as f32;
assert!(
new_ratio > 0.0 && new_ratio.is_finite(),
"Invalid resampling ratio",
);
for interpolator in self.interpolators.iter_mut() {
interpolator.ratio = new_ratio;
}
Ok(())
}
fn reset(&mut self) {
for interpolator in self.interpolators.iter_mut() {
interpolator.reset();
}
}
}