pitch_shift 2.0.0

pitch-shifting using the phase vocoder technique
Documentation
use hound::SampleFormat::Int;
use hound::WavReader;
use hound::WavSpec;
use hound::WavWriter;

use pitch_shift::{Shifter, TOTAL_F32};

use pico_args::Arguments;

const USAGE: &'static str = r#"usage:
    shift-wav -i INPUT_FILE -o OUTPUT_FILE -s SEMITONES

for example, to shift the pitch of my-sample.wav down by one octave:
    shift-wav -i my-sample.wav -o shifted.wav -s -12

note: SEMITONES will be read as a floating point value"#;

fn parse_args(args: &mut Arguments) -> Option<(String, String, f32)> {
    let input_file  = args.value_from_str("-i").ok()?;
    let output_file = args.value_from_str("-o").ok()?;
    let shift       = args.value_from_str("-s").ok()?;
    Some((input_file, output_file, shift))
}

type State = Box<[f32; TOTAL_F32]>;

fn shifter() -> Shifter<State> {
    let state_vec = vec![0.0; TOTAL_F32];
    let state_box: State = state_vec.try_into().unwrap();
    Shifter::new(state_box)
}

fn main() {
    let mut args = Arguments::from_env();
    let parsed = parse_args(&mut args);
    if let Some((input_file, output_file, shift)) = parsed {
        let (in_b, sample_rate) = read_wav(&input_file);
        let mut wav = Vec::new();
        let mut shifter = shifter();

        for in_c in in_b.chunks_exact(128) {
            let out_b = shifter.shift(in_c, shift, sample_rate as f32);
            wav.extend_from_slice(&out_b);
        }

        save_wav(&output_file, &wav, sample_rate);
    } else {
        println!("{}", USAGE);
    }
}

fn read_wav(path: &str) -> (Vec<f32>, usize) {
    let mut reader = WavReader::open(path).unwrap();
    let spec = reader.spec();
    assert!(spec.sample_format == Int);
    assert!(spec.bits_per_sample == 16);
    let samples_orig = reader
        .samples::<i16>()
        .map(|s| s.unwrap() as f32)
        .collect::<Vec<f32>>();
    let mut s = Vec::with_capacity(samples_orig.len() / (spec.channels as usize));
    let mut i = 0;
    for sample in samples_orig {
        if i == 0 {
            s.push(sample);
        }
        i += 1;
        if i == spec.channels {
            i = 0;
        }
    }
    (s, spec.sample_rate as usize)
}

fn save_wav(path: &str, samples: &[f32], sample_rate: usize) {
    let spec = WavSpec {
        channels: 1,
        sample_rate: sample_rate as u32,
        bits_per_sample: 16,
        sample_format: Int,
    };
    let mut writer = WavWriter::create(path, spec).unwrap();
    for s in samples {
        writer.write_sample(*s as i16).unwrap();
    }
}