1#![cfg(feature = "std")]
2use std::io::{Cursor, Read};
6
7use hound::{SampleFormat, WavReader};
8
9use crate::error::Error;
10use crate::guard::guarded;
11use crate::pcm::{self, Limits, PcmFormat};
12use crate::source::{Source, room};
13
14const FORMAT: &str = "WAV";
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18enum Kind {
19 Int8,
20 Int16,
21 Int24,
22 Int32,
23 Float32,
24}
25
26pub struct WavSource<B> {
29 reader: WavReader<Cursor<B>>,
30 format: PcmFormat,
31 kind: Kind,
32 frames: u64,
33 left: u64,
35}
36
37fn malformed(error: &hound::Error) -> Error {
38 Error::Malformed {
39 format: FORMAT,
40 reason: error.to_string(),
41 }
42}
43
44impl<B: AsRef<[u8]>> WavSource<B> {
45 pub fn new(bytes: B, limits: &Limits) -> Result<Self, Error> {
48 guarded(FORMAT, || Self::open(bytes, limits))
49 }
50
51 fn open(bytes: B, limits: &Limits) -> Result<Self, Error> {
52 let (start, available) = {
54 let borrowed = Cursor::new(bytes.as_ref());
55 let header = WavReader::new(borrowed).map_err(|e| malformed(&e))?;
56 (header.into_inner().position(), bytes.as_ref().len() as u64)
57 };
58 let reader = WavReader::new(Cursor::new(bytes)).map_err(|e| malformed(&e))?;
59 let spec = reader.spec();
60 let format = PcmFormat::new(spec.sample_rate, u32::from(spec.channels), limits)?;
61 let kind = match (spec.sample_format, spec.bits_per_sample) {
62 (SampleFormat::Int, 8) => Kind::Int8,
63 (SampleFormat::Int, 16) => Kind::Int16,
64 (SampleFormat::Int, 24) => Kind::Int24,
65 (SampleFormat::Int, 32) => Kind::Int32,
66 (SampleFormat::Float, 32) => Kind::Float32,
67 (SampleFormat::Int, bits) => {
68 return Err(Error::SampleKind {
69 kind: "integer",
70 bits,
71 });
72 }
73 (SampleFormat::Float, bits) => {
74 return Err(Error::SampleKind {
75 kind: "float",
76 bits,
77 });
78 }
79 };
80 let samples = u64::from(reader.len());
81 let channels = u64::from(format.channels.count());
82 if !samples.is_multiple_of(channels) {
83 return Err(Error::PartialFrame {
84 samples,
85 channels: format.channels.count(),
86 });
87 }
88 let frames = samples / channels;
89 if frames > limits.max_frames {
90 return Err(Error::TooLong {
91 frames,
92 limit: limits.max_frames,
93 });
94 }
95 let needed = samples * u64::from(spec.bits_per_sample / 8);
98 if needed > available.saturating_sub(start) {
99 return Err(Error::Malformed {
100 format: FORMAT,
101 reason: format!("it says it holds {needed} bytes of samples, and has less"),
102 });
103 }
104 Ok(Self {
105 reader,
106 format,
107 kind,
108 frames,
109 left: frames,
110 })
111 }
112}
113
114fn fill<S: hound::Sample, R: Read>(
116 reader: &mut WavReader<R>,
117 out: &mut [i16],
118 convert: impl Fn(S) -> i16,
119) -> Result<(), Error> {
120 let mut samples = reader.samples::<S>();
121 for slot in out {
122 match samples.next() {
123 Some(Ok(sample)) => *slot = convert(sample),
124 Some(Err(e)) => return Err(malformed(&e)),
125 None => {
126 return Err(Error::Malformed {
127 format: FORMAT,
128 reason: "it ends early".to_owned(),
129 });
130 }
131 }
132 }
133 Ok(())
134}
135
136#[allow(clippy::float_arithmetic)]
139fn from_float(sample: f32) -> i16 {
140 #[allow(clippy::cast_possible_truncation)]
142 let narrow = (sample * 32_767.0).round().clamp(-32_768.0, 32_767.0) as i16;
143 narrow
144}
145
146impl<B: AsRef<[u8]>> Source for WavSource<B> {
147 fn format(&self) -> PcmFormat {
148 self.format
149 }
150
151 fn total_frames(&self) -> Option<u64> {
152 Some(self.frames)
153 }
154
155 fn read(&mut self, out: &mut [i16]) -> Result<usize, Error> {
156 let room = room(self.format, out)?;
157 let frames = usize::try_from(self.left).map_or(room, |left| left.min(room));
159 let count = frames * usize::from(self.format.channels.count());
160 let (kind, reader) = (self.kind, &mut self.reader);
161 let out = &mut out[..count];
162 guarded(FORMAT, || match kind {
163 Kind::Int8 => fill(reader, out, pcm::from_i8),
164 Kind::Int16 => fill(reader, out, |sample: i16| sample),
165 Kind::Int24 => fill(reader, out, pcm::from_i24),
166 Kind::Int32 => fill(reader, out, pcm::from_i32),
167 Kind::Float32 => fill(reader, out, from_float),
168 })?;
169 self.left -= frames as u64;
170 Ok(frames)
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn a_float_sample_rounds_and_clamps() {
180 assert_eq!(from_float(0.0), 0);
181 assert_eq!(from_float(1.0), 32_767);
182 assert_eq!(from_float(-1.0), -32_767);
183 assert_eq!(from_float(0.5), 16_384);
184 assert_eq!(from_float(4.0), 32_767);
185 assert_eq!(from_float(-4.0), -32_768);
186 assert_eq!(from_float(f32::NAN), 0);
187 }
188}