#[cfg(feature = "file-decode")]
use anyhow::Context;
use anyhow::Result;
use rubato::{Resampler, Resizable};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SampleRate(pub u32);
impl SampleRate {
pub fn new(rate: u32) -> Result<Self, String> {
if rate == 0 {
return Err("sample rate must be > 0".into());
}
Ok(SampleRate(rate))
}
pub fn get(self) -> u32 {
self.0
}
}
pub fn resample(samples: &[f32], from_rate: SampleRate, to_rate: SampleRate) -> Result<Vec<f32>> {
if samples.is_empty() || from_rate.0 == 0 || to_rate.0 == 0 {
return Ok(Vec::new());
}
if from_rate == to_rate {
return Ok(samples.to_vec());
}
let samples: Vec<f32> = samples
.iter()
.map(|&s| if s.is_finite() { s } else { 0.0 })
.collect();
use rubato::audioadapter_buffers::direct::SequentialSliceOfVecs;
use rubato::{
Async, FixedAsync, SincInterpolationParameters, SincInterpolationType, WindowFunction,
};
let params = SincInterpolationParameters {
sinc_len: 256,
f_cutoff: Some(0.95),
interpolation: SincInterpolationType::Linear,
oversampling_factor: 256,
window: WindowFunction::BlackmanHarris2,
};
let ratio = to_rate.0 as f64 / from_rate.0 as f64;
let chunk = samples.len();
let mut resampler = Async::<f32>::new_sinc(ratio, 2.0, ¶ms, chunk, 1, FixedAsync::Input)
.map_err(|e| anyhow::anyhow!("Resampler init failed: {e}"))?;
let input_data = [samples];
let out_frames = resampler.output_frames_next();
let mut output_data = [vec![0.0f32; out_frames]];
{
let input = SequentialSliceOfVecs::new(&input_data, 1, chunk)
.map_err(|e| anyhow::anyhow!("Resampler input adapter failed: {e}"))?;
let mut output = SequentialSliceOfVecs::new_mut(&mut output_data, 1, out_frames)
.map_err(|e| anyhow::anyhow!("Resampler output adapter failed: {e}"))?;
resampler
.process_into_buffer(&input, &mut output, None)
.map_err(|e| anyhow::anyhow!("Resampling failed: {e}"))?;
}
let [out_vec] = output_data;
Ok(out_vec)
}
const MIN_RESAMPLER_CAPACITY: usize = 4096;
pub fn resample_with_cache(
mut samples: Vec<f32>,
from_rate: SampleRate,
to_rate: SampleRate,
cache: &mut Option<rubato::Async<f32>>,
out_buf: &mut Vec<f32>,
) -> anyhow::Result<()> {
if samples.is_empty() || from_rate.0 == 0 || to_rate.0 == 0 {
out_buf.clear();
return Ok(());
}
if from_rate == to_rate {
*out_buf = samples;
return Ok(());
}
for s in &mut samples {
if !s.is_finite() {
*s = 0.0;
}
}
if cache.is_none() {
use rubato::{
Async, FixedAsync, SincInterpolationParameters, SincInterpolationType, WindowFunction,
};
let params = SincInterpolationParameters {
sinc_len: 256,
f_cutoff: Some(0.95),
interpolation: SincInterpolationType::Linear,
oversampling_factor: 256,
window: WindowFunction::BlackmanHarris2,
};
let ratio = to_rate.0 as f64 / from_rate.0 as f64;
let capacity = samples.len().max(MIN_RESAMPLER_CAPACITY);
let r = Async::<f32>::new_sinc(ratio, 2.0, ¶ms, capacity, 1, FixedAsync::Input)
.map_err(|e| anyhow::anyhow!("Resampler init failed: {e}"))?;
*cache = Some(r);
}
let resampler = match cache.as_mut() {
Some(r) => r,
None => anyhow::bail!("Resampler cache is None after initialization"),
};
out_buf.clear();
let max_input = resampler.input_frames_max();
if samples.len() <= max_input {
process_cached_chunk(resampler, samples, out_buf)?;
} else {
let mut piece_out = Vec::new();
for piece in samples.chunks(max_input) {
process_cached_chunk(resampler, piece.to_vec(), &mut piece_out)?;
out_buf.extend_from_slice(&piece_out);
}
}
Ok(())
}
#[cfg(feature = "file-decode")]
pub(super) const RESAMPLE_STAGING_FRAMES: usize = 48_000;
#[cfg(feature = "file-decode")]
pub(super) struct ResampleTo16k {
from_rate: SampleRate,
stage: Vec<f32>,
out: Vec<f32>,
cache: Option<rubato::Async<f32>>,
scratch: Vec<f32>,
}
#[cfg(feature = "file-decode")]
impl ResampleTo16k {
pub(super) fn new(from_rate: SampleRate, source_frames_hint: Option<usize>) -> Self {
if from_rate.0 == 16_000 {
return Self {
from_rate,
stage: match source_frames_hint {
Some(n) => Vec::with_capacity(n),
None => Vec::new(),
},
out: Vec::new(),
cache: None,
scratch: Vec::new(),
};
}
let out = match source_frames_hint {
Some(n) => {
Vec::with_capacity((n as u64 * 16_000 / u64::from(from_rate.0.max(1))) as usize)
}
None => Vec::new(),
};
Self {
from_rate,
stage: Vec::with_capacity(RESAMPLE_STAGING_FRAMES),
out,
cache: None,
scratch: Vec::new(),
}
}
pub(super) fn stage(&mut self) -> &mut Vec<f32> {
&mut self.stage
}
pub(super) fn flush_full(&mut self) -> Result<()> {
if self.from_rate.0 == 16_000 || self.stage.len() < RESAMPLE_STAGING_FRAMES {
return Ok(());
}
self.drain()
}
pub(super) fn finish(mut self) -> Result<Vec<f32>> {
if self.from_rate.0 == 16_000 {
return Ok(self.stage);
}
self.drain()?;
Ok(self.out)
}
pub(super) fn drain_ready_into(&mut self, dst: &mut Vec<f32>) {
let ready = if self.from_rate.0 == 16_000 {
&mut self.stage
} else {
&mut self.out
};
dst.append(ready);
}
pub(super) fn finish_into(&mut self, dst: &mut Vec<f32>) -> Result<()> {
if self.from_rate.0 == 16_000 {
dst.append(&mut self.stage);
return Ok(());
}
self.drain()?;
dst.append(&mut self.out);
Ok(())
}
fn drain(&mut self) -> Result<()> {
if self.stage.is_empty() {
return Ok(());
}
let chunk = std::mem::replace(&mut self.stage, Vec::with_capacity(RESAMPLE_STAGING_FRAMES));
resample_with_cache(
chunk,
self.from_rate,
SampleRate(16_000),
&mut self.cache,
&mut self.scratch,
)
.context("Resampling failed")?;
self.out.extend_from_slice(&self.scratch);
Ok(())
}
}
fn process_cached_chunk(
resampler: &mut rubato::Async<f32>,
samples: Vec<f32>,
dst: &mut Vec<f32>,
) -> anyhow::Result<()> {
use rubato::audioadapter_buffers::direct::SequentialSliceOfVecs;
let chunk = samples.len();
resampler
.set_chunk_size(chunk)
.map_err(|e| anyhow::anyhow!("Resampler chunk resize failed: {e}"))?;
let needed = resampler.output_frames_next();
dst.clear();
dst.resize(needed, 0.0);
let input_data = [samples];
let input = SequentialSliceOfVecs::new(&input_data, 1, chunk)
.map_err(|e| anyhow::anyhow!("Resampler input adapter failed: {e}"))?;
let mut output = SequentialSliceOfVecs::new_mut(std::slice::from_mut(dst), 1, needed)
.map_err(|e| anyhow::anyhow!("Resampler output adapter failed: {e}"))?;
resampler
.process_into_buffer(&input, &mut output, None)
.map_err(|e| anyhow::anyhow!("Resampling failed: {e}"))?;
Ok(())
}