ardftsrc 0.0.6

High-quality audio sample-rate conversion using the ARDFTSRC algorithm.
Documentation
use ardftsrc::{Config, InterleavedResampler};
use std::error::Error;
use std::fs;
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};
use wavers::{Wav, read, write};

const OUTPUT_SAMPLE_RATE_HZ: usize = 48_000;
const INPUT_WAVS: &[&str] = &[
    "../test_wavs/example-pcm16-44k1-stereo.wav",
    "../test_wavs/sweep-pcm16-22k05.wav",
    "../test_wavs/gaplesstest_s-f32-96k.wav",
    "../test_wavs/gaplesstest_m-f32-96k.wav",
];

fn main() -> Result<(), Box<dyn Error>> {
    let output_dir = temp_output_dir()?;
    println!("Writing converted WAVs to {}", output_dir.display());

    for input_path in INPUT_WAVS {
        let input_path = Path::new(input_path);
        let wav = read_wav_f32(input_path)?;
        let config = Config {
            input_sample_rate: wav.sample_rate_hz as usize,
            output_sample_rate: OUTPUT_SAMPLE_RATE_HZ,
            channels: wav.channels,
            ..Config::default()
        };
        let mut resampler = InterleavedResampler::new(config)?;
        let converted = resampler.process_all(&wav.samples)?.interleave();

        let output_path = output_dir.join(format!(
            "{}_to_{}hz.wav",
            input_path
                .file_stem()
                .and_then(|stem| stem.to_str())
                .unwrap_or("converted"),
            OUTPUT_SAMPLE_RATE_HZ
        ));
        write_wav_f32(&output_path, wav.channels, OUTPUT_SAMPLE_RATE_HZ as u32, &converted)?;

        println!(
            "{}: {} Hz -> {} Hz, {} frames -> {} frames",
            input_path.display(),
            wav.sample_rate_hz,
            OUTPUT_SAMPLE_RATE_HZ,
            wav.samples.len() / wav.channels,
            converted.len() / wav.channels
        );
        println!("  wrote {}", output_path.display());
    }

    Ok(())
}

struct WavData {
    samples: Vec<f32>,
    channels: usize,
    sample_rate_hz: u32,
}

fn read_wav_f32(path: &Path) -> Result<WavData, Box<dyn Error>> {
    let wav = Wav::<f32>::from_path(path)?;
    let channels = wav.n_channels() as usize;
    let sample_rate_hz = wav.sample_rate() as u32;
    let (samples, _) = read::<f32, _>(path)?;

    Ok(WavData {
        samples: samples.to_vec(),
        channels,
        sample_rate_hz,
    })
}

fn write_wav_f32(path: &Path, channels: usize, sample_rate_hz: u32, samples: &[f32]) -> Result<(), Box<dyn Error>> {
    write(path, samples, sample_rate_hz as i32, channels as u16)?;
    Ok(())
}

fn temp_output_dir() -> Result<PathBuf, Box<dyn Error>> {
    let ts_millis = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map(|d| d.as_millis())
        .unwrap_or(0);
    let path = std::env::temp_dir().join(format!("ardftsrc_examples_{ts_millis}"));
    fs::create_dir_all(&path)?;
    Ok(path)
}