use audioadapter_buffers::direct::InterleavedSlice;
use clap::{Parser, ValueEnum};
use rubato::{
Async, FixedAsync, Indexing, PolynomialDegree, Resampler, Sample, SincInterpolationParameters,
SincInterpolationType, WindowFunction,
};
#[cfg(feature = "fft_resampler")]
use rubato::{Fft, FixedSync};
use std::fs::File;
use std::io::prelude::{Read, Write};
use std::io::{BufReader, BufWriter};
use std::time::Instant;
const BYTE_PER_SAMPLE: usize = 8;
#[derive(Parser)]
#[command(version)]
struct Options {
input: String,
output: String,
input_rate: usize,
output_rate: usize,
#[arg(short, long, value_enum, ignore_case = true, default_value_t = ResamplerType::SincFixedInput)]
resampler: ResamplerType,
#[arg(short, long, default_value_t = 2)]
channels: usize,
#[arg(short, long, value_enum, ignore_case = true, default_value_t = Precision::F64)]
precision: Precision,
}
#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
enum Precision {
#[value(name = "f32")]
F32,
#[value(name = "f64")]
F64,
}
#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
enum ResamplerType {
#[value(name = "SincFixedInput")]
SincFixedInput,
#[value(name = "SincFixedOutput")]
SincFixedOutput,
#[value(name = "PolyFixedInput")]
PolyFixedInput,
#[value(name = "PolyFixedOutput")]
PolyFixedOutput,
#[cfg(feature = "fft_resampler")]
#[value(name = "FftFixedInput")]
FftFixedInput,
#[cfg(feature = "fft_resampler")]
#[value(name = "FftFixedOutput")]
FftFixedOutput,
#[cfg(feature = "fft_resampler")]
#[value(name = "FftFixedBoth")]
FftFixedBoth,
}
fn read_file<R: Read>(inbuffer: &mut R) -> Vec<f64> {
let mut bytes = Vec::new();
inbuffer.read_to_end(&mut bytes).unwrap();
bytes
.chunks_exact(BYTE_PER_SAMPLE)
.map(|chunk| f64::from_le_bytes(chunk.try_into().unwrap()))
.collect()
}
fn write_file<W: Write>(data: &[f64], output: &mut W) {
for value in data.iter() {
let bytes = value.to_le_bytes();
output.write_all(&bytes).unwrap();
}
}
fn resample<T>(opts: &Options)
where
T: Sample + Into<f64>,
{
let channels = opts.channels;
let (fs_in, fs_out) = (opts.input_rate, opts.output_rate);
println!("Opening files: {}, {}", opts.input, opts.output);
println!("Resampling from {} to {}", fs_in, fs_out);
println!("Copy input file to buffer");
let file_in_disk = File::open(&opts.input).expect("Can't open file");
let mut file_in_reader = BufReader::new(file_in_disk);
let indata: Vec<T> = read_file(&mut file_in_reader)
.into_iter()
.map(T::coerce)
.collect();
let nbr_input_frames = indata.len() / channels;
let f_ratio = fs_out as f64 / fs_in as f64;
let mut outdata =
vec![T::coerce(0.0); 2 * channels * (nbr_input_frames as f64 * f_ratio) as usize];
println!("Creating resampler");
let mut resampler: Box<dyn Resampler<T>> = match opts.resampler {
ResamplerType::SincFixedInput => {
let params = SincInterpolationParameters::new(128, WindowFunction::Blackman2)
.oversampling_factor(256)
.interpolation(SincInterpolationType::Quadratic);
Box::new(
Async::<T>::new_sinc(f_ratio, 1.1, ¶ms, 1024, channels, FixedAsync::Input)
.unwrap(),
)
}
ResamplerType::SincFixedOutput => {
let params = SincInterpolationParameters::new(128, WindowFunction::Blackman2)
.oversampling_factor(512)
.interpolation(SincInterpolationType::Cubic);
Box::new(
Async::<T>::new_sinc(f_ratio, 1.1, ¶ms, 1024, channels, FixedAsync::Output)
.unwrap(),
)
}
ResamplerType::PolyFixedInput => Box::new(
Async::<T>::new_poly(
f_ratio,
1.1,
PolynomialDegree::Septic,
1024,
channels,
FixedAsync::Input,
)
.unwrap(),
),
ResamplerType::PolyFixedOutput => Box::new(
Async::<T>::new_poly(
f_ratio,
1.1,
PolynomialDegree::Septic,
1024,
channels,
FixedAsync::Output,
)
.unwrap(),
),
#[cfg(feature = "fft_resampler")]
ResamplerType::FftFixedInput => {
Box::new(Fft::<T>::new(fs_in, fs_out, 1024, channels, FixedSync::Input).unwrap())
}
#[cfg(feature = "fft_resampler")]
ResamplerType::FftFixedOutput => {
Box::new(Fft::<T>::new(fs_in, fs_out, 1024, channels, FixedSync::Output).unwrap())
}
#[cfg(feature = "fft_resampler")]
ResamplerType::FftFixedBoth => {
Box::new(Fft::<T>::new(fs_in, fs_out, 1024, channels, FixedSync::Both).unwrap())
}
};
let mut input_frames_next = resampler.input_frames_next();
let resampler_delay = resampler.output_delay();
let input_adapter = InterleavedSlice::new(&indata, channels, nbr_input_frames).unwrap();
let outdata_capacity = outdata.len() / channels;
let mut output_adapter =
InterleavedSlice::new_mut(&mut outdata, channels, outdata_capacity).unwrap();
println!("Process all full chunks");
let start = Instant::now();
let mut indexing = Indexing::new();
let mut input_frames_left = nbr_input_frames;
while input_frames_left >= input_frames_next {
let (nbr_in, nbr_out) = resampler
.process_into_buffer(&input_adapter, &mut output_adapter, Some(&indexing))
.unwrap();
indexing.input_offset += nbr_in;
indexing.output_offset += nbr_out;
input_frames_left -= nbr_in;
input_frames_next = resampler.input_frames_next();
}
println!("Process a partial chunk with the last frames.");
indexing.partial_len = Some(input_frames_left);
let (_nbr_in, _nbr_out) = resampler
.process_into_buffer(&input_adapter, &mut output_adapter, Some(&indexing))
.unwrap();
let duration = start.elapsed();
println!("Resampling took: {:?}", duration);
let nbr_output_frames = (nbr_input_frames as f32 * fs_out as f32 / fs_in as f32) as usize;
println!(
"Processed {} input frames into {} output frames",
nbr_input_frames, nbr_output_frames
);
println!("Write output to file, trimming off the silent frames from both ends.");
let first = resampler_delay * channels;
let last = first + nbr_output_frames * channels;
let trimmed: Vec<f64> = outdata[first..last].iter().map(|v| (*v).into()).collect();
let mut file_out_disk = BufWriter::new(File::create(&opts.output).unwrap());
write_file(&trimmed, &mut file_out_disk);
}
fn main() {
env_logger::init();
let opts = Options::parse();
match opts.precision {
Precision::F32 => resample::<f32>(&opts),
Precision::F64 => resample::<f64>(&opts),
}
}