Skip to main content

rill_sampler/
wav.rs

1//! WAV file loading (feature-gated behind `"wav"`).
2//!
3//! Supports mono and stereo 16-bit and 24-bit PCM WAV files.
4
5use crate::buffer::SampleBuffer;
6use rill_core::prelude::Sample;
7
8/// Errors that can occur during WAV loading.
9#[derive(Debug)]
10pub enum WavError {
11    /// An I/O error occurred while reading the file.
12    Io(std::io::Error),
13    /// The WAV file could not be decoded by the `hound` crate.
14    Hound(String),
15    /// The WAV format is unsupported (not 16-bit PCM or not mono/stereo).
16    Format(String),
17}
18
19impl std::fmt::Display for WavError {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        match self {
22            WavError::Io(e) => write!(f, "IO error: {}", e),
23            WavError::Hound(s) => write!(f, "WAV decode error: {}", s),
24            WavError::Format(s) => write!(f, "Invalid WAV: {}", s),
25        }
26    }
27}
28
29impl std::error::Error for WavError {}
30
31impl From<std::io::Error> for WavError {
32    fn from(e: std::io::Error) -> Self {
33        WavError::Io(e)
34    }
35}
36
37impl From<hound::Error> for WavError {
38    fn from(e: hound::Error) -> Self {
39        WavError::Hound(e.to_string())
40    }
41}
42
43/// Load a WAV file into a `SampleBuffer<Sample>`.
44///
45/// Supports 16-bit and 24-bit PCM, mono and stereo.
46pub fn load_wav(path: &str) -> Result<SampleBuffer<Sample>, WavError> {
47    let mut reader = hound::WavReader::open(path)?;
48    let spec = reader.spec();
49
50    let channels = spec.channels;
51    let sample_rate = spec.sample_rate as f32;
52    let bits_per_sample = spec.bits_per_sample;
53
54    if channels != 1 && channels != 2 {
55        return Err(WavError::Format(format!(
56            "Only mono/stereo supported, got {} channels",
57            channels
58        )));
59    }
60
61    let name = path.rsplit('/').next().unwrap_or(path).to_string();
62
63    match bits_per_sample {
64        16 => {
65            let samples: Vec<i16> = reader
66                .samples::<i16>()
67                .collect::<Result<Vec<_>, _>>()
68                .map_err(|e| WavError::Format(format!("Sample read error: {}", e)))?;
69            Ok(samples_to_buffer(
70                samples.into_iter().map(|s| s as f32 / 32768.0),
71                channels,
72                sample_rate,
73                name,
74            ))
75        }
76        24 => {
77            let samples: Vec<i32> = reader
78                .samples::<i32>()
79                .collect::<Result<Vec<_>, _>>()
80                .map_err(|e| WavError::Format(format!("Sample read error: {}", e)))?;
81            const SCALE: f32 = 1.0 / 8388608.0; // 2^23
82            Ok(samples_to_buffer(
83                samples.into_iter().map(|s| s as f32 * SCALE),
84                channels,
85                sample_rate,
86                name,
87            ))
88        }
89        other => Err(WavError::Format(format!(
90            "Only 16/24-bit PCM supported, got {}-bit",
91            other
92        ))),
93    }
94}
95
96fn samples_to_buffer(
97    samples: impl Iterator<Item = f32>,
98    channels: u16,
99    sample_rate: f32,
100    name: String,
101) -> SampleBuffer<Sample> {
102    let data: Vec<Sample> = samples.collect();
103    if channels == 1 {
104        SampleBuffer::mono(data, sample_rate, name)
105    } else {
106        let mut left = Vec::with_capacity(data.len() / 2);
107        let mut right = Vec::with_capacity(data.len() / 2);
108        for chunk in data.chunks(2) {
109            left.push(chunk[0]);
110            if chunk.len() > 1 {
111                right.push(chunk[1]);
112            }
113        }
114        SampleBuffer::stereo(left, right, sample_rate, name)
115    }
116}