wav-codec 0.1.0

WAV (RIFF/WAVE) reader and writer over std I/O. PCM and IEEE-float WAV only. Compressed WAV (ADPCM, mu-law, A-law) not decoded.
Documentation
//! Gate (f), defect 3: large-file read throughput, measured as a ratio
//! against an in-test raw decode of the same bytes so the gate holds across
//! CI hardware speeds.
//!
//! The 5.3 MB perf fixture is regenerated in memory from the manifest's
//! LCG description and checked against the manifest sha256 before use, so
//! the tarball does not carry it. Checksum equality against the LCG itself
//! proves the decode is correct in the same test.

mod common;

use std::fs;
use std::io::Write;
use std::time::{Duration, Instant};

use common::{checksum, fixture_bytes, perf_expected_checksum, vector, PERF_FRAMES};
use wav_codec::WavReader;

/// Upper bound on `reader time / raw decode time`.
const MAX_RATIO: f64 = 3.0;
const RUNS: usize = 7;

fn best_of<F: FnMut() -> u64>(mut f: F, expected: u64) -> Duration {
    let mut best = Duration::MAX;
    for _ in 0..RUNS {
        let start = Instant::now();
        let got = f();
        let took = start.elapsed();
        assert_eq!(got, expected, "checksum mismatch");
        best = best.min(took);
    }
    best
}

/// `fs::read` plus a `chunks_exact` decode: the floor any WAV reader sits
/// on top of.
fn raw_decode(path: &std::path::Path) -> u64 {
    let bytes = fs::read(path).unwrap();
    let samples: Vec<i16> = bytes[44..]
        .chunks_exact(2)
        .map(|c| i16::from_le_bytes([c[0], c[1]]))
        .collect();
    checksum(samples)
}

fn via_iterator(path: &std::path::Path) -> u64 {
    let mut reader = WavReader::open(path).unwrap();
    let samples: Vec<i16> = reader.samples::<i16>().map(|s| s.unwrap()).collect();
    checksum(samples)
}

fn via_read_samples(path: &std::path::Path) -> u64 {
    let mut reader = WavReader::open(path).unwrap();
    let mut out: Vec<i16> = Vec::with_capacity(reader.len() as usize);
    let mut buf = [0i16; 4096];
    loop {
        let n = reader.read_samples(&mut buf).unwrap();
        if n == 0 {
            break;
        }
        out.extend_from_slice(&buf[..n]);
    }
    checksum(out)
}

#[test]
fn perf_large_read() {
    let v = vector("P01");
    let bytes = fixture_bytes(&v);
    assert_eq!(bytes.len(), 44 + PERF_FRAMES * 4);
    let expected = perf_expected_checksum();

    let dir = std::env::temp_dir();
    let path = dir.join(format!("wav-codec-perf-{}.wav", std::process::id()));
    {
        let mut f = fs::File::create(&path).unwrap();
        f.write_all(&bytes).unwrap();
    }
    drop(bytes);

    let reader = WavReader::open(&path).unwrap();
    assert_eq!(reader.len() as usize, PERF_FRAMES * 2);
    assert_eq!(reader.duration() as usize, PERF_FRAMES);
    drop(reader);

    // Warm the page cache so every contender reads from memory.
    raw_decode(&path);

    let raw = best_of(|| raw_decode(&path), expected);
    let iter = best_of(|| via_iterator(&path), expected);
    let bulk = best_of(|| via_read_samples(&path), expected);
    let _ = fs::remove_file(&path);

    let raw_s = raw.as_secs_f64().max(1e-9);
    let iter_ratio = iter.as_secs_f64() / raw_s;
    let bulk_ratio = bulk.as_secs_f64() / raw_s;
    let mib = (44.0 + PERF_FRAMES as f64 * 4.0) / (1024.0 * 1024.0);
    eprintln!(
        "perf_large_read: raw {:.3} ms ({:.0} MiB/s), samples() {:.3} ms ({:.2}x), read_samples {:.3} ms ({:.2}x)",
        raw.as_secs_f64() * 1e3,
        mib / raw_s,
        iter.as_secs_f64() * 1e3,
        iter_ratio,
        bulk.as_secs_f64() * 1e3,
        bulk_ratio
    );
    assert!(
        iter_ratio <= MAX_RATIO,
        "samples() is {:.2}x the raw decode, limit {}x",
        iter_ratio,
        MAX_RATIO
    );
    assert!(
        bulk_ratio <= MAX_RATIO,
        "read_samples is {:.2}x the raw decode, limit {}x",
        bulk_ratio,
        MAX_RATIO
    );
}