mod interpolation;
mod realfft;
mod sinc;
mod synchro;
mod windows;
pub use crate::synchro::{FftFixedIn, FftFixedInOut, FftFixedOut};
pub use crate::windows::WindowFunction;
use crate::interpolation::*;
use crate::sinc::make_sincs;
use num_traits::Float;
use std::error;
use std::fmt;
#[macro_use]
extern crate log;
type Res<T> = Result<T, Box<dyn error::Error>>;
#[derive(Debug)]
pub struct ResamplerError {
desc: String,
}
impl fmt::Display for ResamplerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.desc)
}
}
impl error::Error for ResamplerError {
fn description(&self) -> &str {
&self.desc
}
}
impl ResamplerError {
pub fn new(desc: &str) -> Self {
ResamplerError {
desc: desc.to_owned(),
}
}
}
#[derive(Debug)]
pub struct InterpolationParameters {
pub sinc_len: usize,
pub f_cutoff: f32,
pub oversampling_factor: usize,
pub interpolation: InterpolationType,
pub window: WindowFunction,
}
#[derive(Debug)]
pub enum InterpolationType {
Cubic,
Linear,
Nearest,
}
pub trait Resampler<T> {
fn process(&mut self, wave_in: &[Vec<T>]) -> Res<Vec<Vec<T>>>;
fn set_resample_ratio(&mut self, new_ratio: f64) -> Res<()>;
fn set_resample_ratio_relative(&mut self, rel_ratio: f64) -> Res<()>;
fn nbr_frames_needed(&self) -> usize;
}
pub struct SincFixedIn<T> {
nbr_channels: usize,
chunk_size: usize,
oversampling_factor: usize,
last_index: f64,
resample_ratio: f64,
resample_ratio_original: f64,
sinc_len: usize,
sincs: Vec<Vec<T>>,
buffer: Vec<Vec<T>>,
interpolation: InterpolationType,
}
pub struct SincFixedOut<T> {
nbr_channels: usize,
chunk_size: usize,
needed_input_size: usize,
oversampling_factor: usize,
last_index: f64,
current_buffer_fill: usize,
resample_ratio: f64,
resample_ratio_original: f64,
sinc_len: usize,
sincs: Vec<Vec<T>>,
buffer: Vec<Vec<T>>,
interpolation: InterpolationType,
}
macro_rules! impl_resampler {
($ft:ty, $rt:ty) => {
impl $rt {
fn get_sinc_interpolated(&self, wave: &[$ft], index: usize, subindex: usize) -> $ft {
let wave_cut = &wave[index..(index + self.sincs[subindex].len())];
wave_cut
.chunks(8)
.zip(self.sincs[subindex].chunks(8))
.fold([0.0; 8], |acc, (x, y)| {
[
acc[0] + x[0] * y[0],
acc[1] + x[1] * y[1],
acc[2] + x[2] * y[2],
acc[3] + x[3] * y[3],
acc[4] + x[4] * y[4],
acc[5] + x[5] * y[5],
acc[6] + x[6] * y[6],
acc[7] + x[7] * y[7],
]
})
.iter()
.sum()
}
fn interp_cubic(&self, x: $ft, yvals: &[$ft]) -> $ft {
let a0 = yvals[1];
let a1 =
-(1.0 / 3.0) * yvals[0] - 0.5 * yvals[1] + yvals[2] - (1.0 / 6.0) * yvals[3];
let a2 = 0.5 * (yvals[0] + yvals[2]) - yvals[1];
let a3 = 0.5 * (yvals[1] - yvals[2]) + (1.0 / 6.0) * (yvals[3] - yvals[0]);
a0 + a1 * x + a2 * x.powi(2) + a3 * x.powi(3)
}
fn interp_lin(&self, x: $ft, yvals: &[$ft]) -> $ft {
(1.0 - x) * yvals[0] + x * yvals[1]
}
}
};
}
impl_resampler!(f32, SincFixedIn<f32>);
impl_resampler!(f64, SincFixedIn<f64>);
impl_resampler!(f32, SincFixedOut<f32>);
impl_resampler!(f64, SincFixedOut<f64>);
impl<T: Float> SincFixedIn<T> {
pub fn new(
resample_ratio: f64,
parameters: InterpolationParameters,
chunk_size: usize,
nbr_channels: usize,
) -> Self {
debug!(
"Create new SincFixedIn, ratio: {}, chunk_size: {}, channels: {}, parameters: {:?}",
resample_ratio, chunk_size, nbr_channels, parameters
);
let sinc_cutoff = if resample_ratio >= 1.0 {
parameters.f_cutoff
} else {
parameters.f_cutoff * resample_ratio as f32
};
let sinc_len = 8 * (((parameters.sinc_len as f32) / 8.0).ceil() as usize);
debug!("sinc_len rounded up to {}", sinc_len);
let sincs = make_sincs(
sinc_len,
parameters.oversampling_factor,
sinc_cutoff,
parameters.window,
);
let buffer = vec![vec![T::zero(); chunk_size + 2 * sinc_len]; nbr_channels];
SincFixedIn {
nbr_channels,
chunk_size,
oversampling_factor: parameters.oversampling_factor,
last_index: -((sinc_len / 2) as f64),
resample_ratio,
resample_ratio_original: resample_ratio,
sinc_len,
sincs,
buffer,
interpolation: parameters.interpolation,
}
}
}
macro_rules! resampler_sincfixedin {
($t:ty) => {
impl Resampler<$t> for SincFixedIn<$t> {
fn process(&mut self, wave_in: &[Vec<$t>]) -> Res<Vec<Vec<$t>>> {
if wave_in.len() != self.nbr_channels {
return Err(Box::new(ResamplerError::new(
"Wrong number of channels in input",
)));
}
if wave_in[0].len() != self.chunk_size {
return Err(Box::new(ResamplerError::new(
"Wrong number of frames in input",
)));
}
let end_idx = self.chunk_size as isize - (self.sinc_len as isize + 1);
for wav in self.buffer.iter_mut() {
for idx in 0..(2 * self.sinc_len) {
wav[idx] = wav[idx + self.chunk_size];
}
}
for (chan, wav) in wave_in.iter().enumerate() {
for (idx, sample) in wav.iter().enumerate() {
self.buffer[chan][idx + 2 * self.sinc_len] = *sample;
}
}
let mut idx = self.last_index;
let t_ratio = 1.0 / self.resample_ratio as f64;
let mut wave_out = vec![
vec![
0.0 as $t;
(self.chunk_size as f64 * self.resample_ratio + 10.0)
as usize
];
self.nbr_channels
];
let mut n = 0;
match self.interpolation {
InterpolationType::Cubic => {
let mut points = vec![0.0 as $t; 4];
let mut nearest = vec![(0isize, 0isize); 4];
while idx < end_idx as f64 {
idx += t_ratio;
get_nearest_times_4(
idx,
self.oversampling_factor as isize,
&mut nearest,
);
let frac = idx * self.oversampling_factor as f64
- (idx * self.oversampling_factor as f64).floor();
let frac_offset = frac as $t;
for (chan, buf) in self.buffer.iter().enumerate() {
for (n, p) in nearest.iter().zip(points.iter_mut()) {
*p = self.get_sinc_interpolated(
&buf,
(n.0 + 2 * self.sinc_len as isize) as usize,
n.1 as usize,
);
}
wave_out[chan][n] = self.interp_cubic(frac_offset, &points);
}
n += 1;
}
}
InterpolationType::Linear => {
let mut points = vec![0.0 as $t; 2];
let mut nearest = vec![(0isize, 0isize); 2];
while idx < end_idx as f64 {
idx += t_ratio;
get_nearest_times_2(
idx,
self.oversampling_factor as isize,
&mut nearest,
);
let frac = idx * self.oversampling_factor as f64
- (idx * self.oversampling_factor as f64).floor();
let frac_offset = frac as $t;
for (chan, buf) in self.buffer.iter().enumerate() {
for (n, p) in nearest.iter().zip(points.iter_mut()) {
*p = self.get_sinc_interpolated(
&buf,
(n.0 + 2 * self.sinc_len as isize) as usize,
n.1 as usize,
);
}
wave_out[chan][n] = self.interp_lin(frac_offset, &points);
}
n += 1;
}
}
InterpolationType::Nearest => {
let mut point;
let mut nearest;
while idx < end_idx as f64 {
idx += t_ratio;
nearest = get_nearest_time(idx, self.oversampling_factor as isize);
for (chan, buf) in self.buffer.iter().enumerate() {
point = self.get_sinc_interpolated(
&buf,
(nearest.0 + 2 * self.sinc_len as isize) as usize,
nearest.1 as usize,
);
wave_out[chan][n] = point;
}
n += 1;
}
}
}
self.last_index = idx - self.chunk_size as f64;
for w in wave_out.iter_mut() {
w.truncate(n);
}
trace!(
"Resampling, {} frames in, {} frames out",
wave_in[0].len(),
wave_out[0].len()
);
Ok(wave_out)
}
fn set_resample_ratio(&mut self, new_ratio: f64) -> Res<()> {
trace!("Change resample ratio to {}", new_ratio);
if (new_ratio / self.resample_ratio_original > 0.9)
&& (new_ratio / self.resample_ratio_original < 1.1)
{
self.resample_ratio = new_ratio;
Ok(())
} else {
Err(Box::new(ResamplerError::new(
"New resample ratio is too far off from original",
)))
}
}
fn set_resample_ratio_relative(&mut self, rel_ratio: f64) -> Res<()> {
let new_ratio = self.resample_ratio_original * rel_ratio;
self.set_resample_ratio(new_ratio)
}
fn nbr_frames_needed(&self) -> usize {
self.chunk_size
}
}
};
}
resampler_sincfixedin!(f32);
resampler_sincfixedin!(f64);
impl<T: Float> SincFixedOut<T> {
pub fn new(
resample_ratio: f64,
parameters: InterpolationParameters,
chunk_size: usize,
nbr_channels: usize,
) -> Self {
debug!(
"Create new SincFixedOut, ratio: {}, chunk_size: {}, channels: {}, parameters: {:?}",
resample_ratio, chunk_size, nbr_channels, parameters
);
let sinc_cutoff = if resample_ratio >= 1.0 {
parameters.f_cutoff
} else {
parameters.f_cutoff * resample_ratio as f32
};
let sinc_len = 8 * (((parameters.sinc_len as f32) / 8.0).ceil() as usize);
debug!("sinc_len rounded up to {}", sinc_len);
let sincs = make_sincs(
sinc_len,
parameters.oversampling_factor,
sinc_cutoff,
parameters.window,
);
let needed_input_size =
(chunk_size as f64 / resample_ratio).ceil() as usize + 2 + sinc_len / 2;
let buffer = vec![vec![T::zero(); 3 * needed_input_size / 2 + 2 * sinc_len]; nbr_channels];
SincFixedOut {
nbr_channels,
chunk_size,
needed_input_size,
oversampling_factor: parameters.oversampling_factor,
last_index: -((sinc_len / 2) as f64),
current_buffer_fill: needed_input_size,
resample_ratio,
resample_ratio_original: resample_ratio,
sinc_len,
sincs,
buffer,
interpolation: parameters.interpolation,
}
}
}
macro_rules! resampler_sincfixedout {
($t:ty) => {
impl Resampler<$t> for SincFixedOut<$t> {
fn nbr_frames_needed(&self) -> usize {
self.needed_input_size
}
fn set_resample_ratio(&mut self, new_ratio: f64) -> Res<()> {
trace!("Change resample ratio to {}", new_ratio);
if (new_ratio / self.resample_ratio_original > 0.9)
&& (new_ratio / self.resample_ratio_original < 1.1)
{
self.resample_ratio = new_ratio;
self.needed_input_size = (self.last_index as f32
+ self.chunk_size as f32 / self.resample_ratio as f32
+ self.sinc_len as f32)
.ceil() as usize
+ 2;
Ok(())
} else {
Err(Box::new(ResamplerError::new(
"New resample ratio is too far off from original",
)))
}
}
fn set_resample_ratio_relative(&mut self, rel_ratio: f64) -> Res<()> {
let new_ratio = self.resample_ratio_original * rel_ratio;
self.set_resample_ratio(new_ratio)
}
fn process(&mut self, wave_in: &[Vec<$t>]) -> Res<Vec<Vec<$t>>> {
if wave_in.len() != self.nbr_channels {
return Err(Box::new(ResamplerError::new(
"Wrong number of channels in input",
)));
}
if wave_in[0].len() != self.needed_input_size {
return Err(Box::new(ResamplerError::new(
"Wrong number of frames in input",
)));
}
for wav in self.buffer.iter_mut() {
for idx in 0..(2 * self.sinc_len) {
wav[idx] = wav[idx + self.current_buffer_fill];
}
}
self.current_buffer_fill = wave_in[0].len();
for (chan, wav) in wave_in.iter().enumerate() {
for (idx, sample) in wav.iter().enumerate() {
self.buffer[chan][idx + 2 * self.sinc_len] = *sample;
}
}
let mut idx = self.last_index;
let t_ratio = 1.0 / self.resample_ratio as f64;
let mut wave_out = vec![vec![0.0 as $t; self.chunk_size]; self.nbr_channels];
match self.interpolation {
InterpolationType::Cubic => {
let mut points = vec![0.0 as $t; 4];
let mut nearest = vec![(0isize, 0isize); 4];
for n in 0..self.chunk_size {
idx += t_ratio;
get_nearest_times_4(idx, self.oversampling_factor as isize, &mut nearest);
let frac = idx * self.oversampling_factor as f64
- (idx * self.oversampling_factor as f64).floor();
let frac_offset = frac as $t;
for (chan, buf) in self.buffer.iter().enumerate() {
for (n, p) in nearest.iter().zip(points.iter_mut()) {
*p = self.get_sinc_interpolated(
&buf,
(n.0 + 2 * self.sinc_len as isize) as usize,
n.1 as usize,
);
}
wave_out[chan][n] = self.interp_cubic(frac_offset, &points);
}
}
}
InterpolationType::Linear => {
let mut points = vec![0.0 as $t; 2];
let mut nearest = vec![(0isize, 0isize); 2];
for n in 0..self.chunk_size {
idx += t_ratio;
get_nearest_times_2(idx, self.oversampling_factor as isize, &mut nearest);
let frac = idx * self.oversampling_factor as f64
- (idx * self.oversampling_factor as f64).floor();
let frac_offset = frac as $t;
for (chan, buf) in self.buffer.iter().enumerate() {
for (n, p) in nearest.iter().zip(points.iter_mut()) {
*p = self.get_sinc_interpolated(
&buf,
(n.0 + 2 * self.sinc_len as isize) as usize,
n.1 as usize,
);
}
wave_out[chan][n] = self.interp_lin(frac_offset, &points);
}
}
}
InterpolationType::Nearest => {
let mut point;
let mut nearest;
for n in 0..self.chunk_size {
idx += t_ratio;
nearest = get_nearest_time(idx, self.oversampling_factor as isize);
for (chan, buf) in self.buffer.iter().enumerate() {
point = self.get_sinc_interpolated(
&buf,
(nearest.0 + 2 * self.sinc_len as isize) as usize,
nearest.1 as usize,
);
wave_out[chan][n] = point;
}
}
}
}
self.last_index = idx - self.current_buffer_fill as f64;
self.needed_input_size = (self.last_index as f32
+ self.chunk_size as f32 / self.resample_ratio as f32
+ self.sinc_len as f32)
.ceil() as usize
+ 2;
trace!(
"Resampling, {} frames in, {} frames out. Next needed length: {} frames, last index {}",
wave_in[0].len(),
wave_out[0].len(),
self.needed_input_size,
self.last_index
);
Ok(wave_out)
}
}
}
}
resampler_sincfixedout!(f32);
resampler_sincfixedout!(f64);
#[cfg(test)]
mod tests {
use crate::InterpolationParameters;
use crate::InterpolationType;
use crate::Resampler;
use crate::WindowFunction;
use crate::{SincFixedIn, SincFixedOut};
#[test]
fn int_cubic() {
let params = InterpolationParameters {
sinc_len: 64,
f_cutoff: 0.95,
interpolation: InterpolationType::Cubic,
oversampling_factor: 16,
window: WindowFunction::BlackmanHarris2,
};
let resampler = SincFixedIn::<f64>::new(1.2, params, 1024, 2);
let yvals = vec![0.0f64, 2.0f64, 4.0f64, 6.0f64];
let interp = resampler.interp_cubic(0.5f64, &yvals);
assert_eq!(interp, 3.0f64);
}
#[test]
fn int_lin_32() {
let params = InterpolationParameters {
sinc_len: 64,
f_cutoff: 0.95,
interpolation: InterpolationType::Cubic,
oversampling_factor: 16,
window: WindowFunction::BlackmanHarris2,
};
let resampler = SincFixedIn::<f32>::new(1.2, params, 1024, 2);
let yvals = vec![1.0f32, 5.0f32];
let interp = resampler.interp_lin(0.25f32, &yvals);
assert_eq!(interp, 2.0f32);
}
#[test]
fn int_cubic_32() {
let params = InterpolationParameters {
sinc_len: 64,
f_cutoff: 0.95,
interpolation: InterpolationType::Cubic,
oversampling_factor: 16,
window: WindowFunction::BlackmanHarris2,
};
let resampler = SincFixedIn::<f32>::new(1.2, params, 1024, 2);
let yvals = vec![0.0f32, 2.0f32, 4.0f32, 6.0f32];
let interp = resampler.interp_cubic(0.5f32, &yvals);
assert_eq!(interp, 3.0f32);
}
#[test]
fn int_lin() {
let params = InterpolationParameters {
sinc_len: 64,
f_cutoff: 0.95,
interpolation: InterpolationType::Cubic,
oversampling_factor: 16,
window: WindowFunction::BlackmanHarris2,
};
let resampler = SincFixedIn::<f64>::new(1.2, params, 1024, 2);
let yvals = vec![1.0f64, 5.0f64];
let interp = resampler.interp_lin(0.25f64, &yvals);
assert_eq!(interp, 2.0f64);
}
#[test]
fn make_resampler_fi() {
let params = InterpolationParameters {
sinc_len: 64,
f_cutoff: 0.95,
interpolation: InterpolationType::Cubic,
oversampling_factor: 16,
window: WindowFunction::BlackmanHarris2,
};
let mut resampler = SincFixedIn::<f64>::new(1.2, params, 1024, 2);
let waves = vec![vec![0.0f64; 1024]; 2];
let out = resampler.process(&waves).unwrap();
assert_eq!(out.len(), 2);
assert!(out[0].len() > 1150 && out[0].len() < 1250);
}
#[test]
fn make_resampler_fi_32() {
let params = InterpolationParameters {
sinc_len: 64,
f_cutoff: 0.95,
interpolation: InterpolationType::Cubic,
oversampling_factor: 16,
window: WindowFunction::BlackmanHarris2,
};
let mut resampler = SincFixedIn::<f32>::new(1.2, params, 1024, 2);
let waves = vec![vec![0.0f32; 1024]; 2];
let out = resampler.process(&waves).unwrap();
assert_eq!(out.len(), 2);
assert!(out[0].len() > 1150 && out[0].len() < 1250);
}
#[test]
fn make_resampler_fo() {
let params = InterpolationParameters {
sinc_len: 64,
f_cutoff: 0.95,
interpolation: InterpolationType::Cubic,
oversampling_factor: 16,
window: WindowFunction::BlackmanHarris2,
};
let mut resampler = SincFixedOut::<f64>::new(1.2, params, 1024, 2);
let frames = resampler.nbr_frames_needed();
println!("{}", frames);
assert!(frames > 800 && frames < 900);
let waves = vec![vec![0.0f64; frames]; 2];
let out = resampler.process(&waves).unwrap();
assert_eq!(out.len(), 2);
assert_eq!(out[0].len(), 1024);
}
#[test]
fn make_resampler_fo_32() {
let params = InterpolationParameters {
sinc_len: 64,
f_cutoff: 0.95,
interpolation: InterpolationType::Cubic,
oversampling_factor: 16,
window: WindowFunction::BlackmanHarris2,
};
let mut resampler = SincFixedOut::<f32>::new(1.2, params, 1024, 2);
let frames = resampler.nbr_frames_needed();
println!("{}", frames);
assert!(frames > 800 && frames < 900);
let waves = vec![vec![0.0f32; frames]; 2];
let out = resampler.process(&waves).unwrap();
assert_eq!(out.len(), 2);
assert_eq!(out[0].len(), 1024);
}
}