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 rill_core::traits::SignalSlab;
6
7/// Errors that can occur during WAV loading.
8#[derive(Debug)]
9pub enum WavError {
10    /// An I/O error occurred while reading the file.
11    Io(std::io::Error),
12    /// The WAV file could not be decoded by the `hound` crate.
13    Hound(String),
14    /// The WAV format is unsupported (not 16-bit PCM or not mono/stereo).
15    Format(String),
16}
17
18impl std::fmt::Display for WavError {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        match self {
21            WavError::Io(e) => write!(f, "IO error: {}", e),
22            WavError::Hound(s) => write!(f, "WAV decode error: {}", s),
23            WavError::Format(s) => write!(f, "Invalid WAV: {}", s),
24        }
25    }
26}
27
28impl std::error::Error for WavError {}
29
30impl From<std::io::Error> for WavError {
31    fn from(e: std::io::Error) -> Self {
32        WavError::Io(e)
33    }
34}
35
36impl From<hound::Error> for WavError {
37    fn from(e: hound::Error) -> Self {
38        WavError::Hound(e.to_string())
39    }
40}
41
42/// Load a WAV file into a [`SignalSlab`] ready for sampler hot-swap.
43///
44/// Performs all file I/O and allocations on the calling thread.
45/// The resulting `SignalSlab` can be sent to a sampler node via
46/// `ParamValue::SignalSlab` and consumed with zero allocations on
47/// the real-time I/O thread.
48pub fn load_slab(path: &str) -> Result<SignalSlab, WavError> {
49    let mut reader = hound::WavReader::open(path)?;
50    let spec = reader.spec();
51
52    let channels = spec.channels;
53    let sample_rate = spec.sample_rate as f32;
54    let bits = spec.bits_per_sample;
55
56    let num_frames = reader.duration() as usize;
57
58    let f32_samples: Vec<f32> = match bits {
59        16 => reader
60            .samples::<i16>()
61            .map(|r| r.map(|s| s as f32 / 32768.0))
62            .collect::<Result<Vec<_>, _>>()
63            .map_err(|e| WavError::Format(format!("Sample read error: {}", e)))?,
64        24 => {
65            const SCALE: f32 = 1.0 / 8388608.0;
66            reader
67                .samples::<i32>()
68                .map(|r| r.map(|s| s as f32 * SCALE))
69                .collect::<Result<Vec<_>, _>>()
70                .map_err(|e| WavError::Format(format!("Sample read error: {}", e)))?
71        }
72        other => {
73            return Err(WavError::Format(format!(
74                "Only 16/24-bit supported, got {}-bit",
75                other
76            )))
77        }
78    };
79
80    let mut slab_channels: Vec<Box<[f32]>> = Vec::with_capacity(channels as usize);
81    if channels == 1 {
82        slab_channels.push(f32_samples.into_boxed_slice());
83    } else {
84        let ch = channels as usize;
85        let mut per_channel: Vec<Vec<f32>> =
86            (0..ch).map(|_| Vec::with_capacity(num_frames)).collect();
87        for chunk in f32_samples.chunks(ch) {
88            for (i, &s) in chunk.iter().enumerate() {
89                per_channel[i].push(s);
90            }
91        }
92        for v in per_channel {
93            slab_channels.push(v.into_boxed_slice());
94        }
95    }
96
97    Ok(SignalSlab {
98        channels: slab_channels,
99        sample_rate,
100        num_frames,
101    })
102}