sim_lib_stream_file/
wav.rs1use std::path::Path;
2
3use sim_kernel::{Error, Result};
4use sim_lib_stream_audio::{MemoryPcmSink, MemoryPcmSource, PcmBuffer, PcmPumpSummary, PcmSpec};
5use sim_lib_stream_core::{StreamMetadata, StreamValue};
6
7use crate::effect_io::{read_file_with_effect, write_file_with_effect};
8
9pub struct WavStream {
11 spec: PcmSpec,
12 stream: StreamValue,
13}
14
15impl WavStream {
16 pub fn spec(&self) -> PcmSpec {
18 self.spec
19 }
20
21 pub fn stream(&self) -> &StreamValue {
23 &self.stream
24 }
25
26 pub fn into_stream(self) -> StreamValue {
28 self.stream
29 }
30}
31
32pub fn read_wav_stream(
37 cx: &mut sim_kernel::Cx,
38 path: impl AsRef<Path>,
39 frames_per_packet: usize,
40 metadata: StreamMetadata,
41) -> Result<WavStream> {
42 let bytes = read_file_with_effect(cx, path)?;
43 wav_bytes_to_stream(&bytes, frames_per_packet, metadata)
44}
45
46pub fn wav_bytes_to_stream(
50 bytes: &[u8],
51 frames_per_packet: usize,
52 metadata: StreamMetadata,
53) -> Result<WavStream> {
54 let (spec, buffers) = wav_bytes_to_buffers(bytes, frames_per_packet)?;
55 let mut source = MemoryPcmSource::new(spec, buffers)?;
56 let stream = sim_lib_stream_audio::pcm_source_to_stream(&mut source, metadata)?;
57 Ok(WavStream { spec, stream })
58}
59
60pub fn write_wav_stream(
65 cx: &mut sim_kernel::Cx,
66 path: impl AsRef<Path>,
67 stream: &StreamValue,
68 spec: PcmSpec,
69) -> Result<PcmPumpSummary> {
70 let mut sink = MemoryPcmSink::new(spec);
71 let summary = sim_lib_stream_audio::stream_to_pcm_sink(stream, &mut sink)?;
72 let bytes = pcm_buffers_to_wav_bytes(spec, sink.buffers())?;
73 write_file_with_effect(cx, path, bytes)?;
74 Ok(summary)
75}
76
77pub fn stream_to_wav_bytes(
81 stream: &StreamValue,
82 spec: PcmSpec,
83) -> Result<(Vec<u8>, PcmPumpSummary)> {
84 let mut sink = MemoryPcmSink::new(spec);
85 let summary = sim_lib_stream_audio::stream_to_pcm_sink(stream, &mut sink)?;
86 Ok((pcm_buffers_to_wav_bytes(spec, sink.buffers())?, summary))
87}
88
89pub fn pcm_buffers_to_wav_bytes(spec: PcmSpec, buffers: &[PcmBuffer]) -> Result<Vec<u8>> {
105 let mut samples = Vec::new();
106 for buffer in buffers {
107 if buffer.spec() != spec {
108 return Err(Error::Eval(
109 "WAV writer received a PCM buffer with a mismatched spec".to_owned(),
110 ));
111 }
112 samples.extend_from_slice(buffer.samples_i16());
113 }
114 encode_wav_i16(spec, &samples)
115}
116
117fn wav_bytes_to_buffers(
118 bytes: &[u8],
119 frames_per_packet: usize,
120) -> Result<(PcmSpec, Vec<PcmBuffer>)> {
121 if frames_per_packet == 0 {
122 return Err(Error::Eval(
123 "WAV frames-per-packet must be greater than zero".to_owned(),
124 ));
125 }
126 let parsed = parse_wav_i16(bytes)?;
127 let channels = parsed.spec.channels();
128 let samples_per_packet = channels
129 .checked_mul(frames_per_packet)
130 .ok_or_else(|| Error::Eval("WAV packet sample count overflow".to_owned()))?;
131 let mut buffers = Vec::new();
132 for chunk in parsed.samples.chunks(samples_per_packet) {
133 if chunk.is_empty() {
134 continue;
135 }
136 if !chunk.len().is_multiple_of(channels) {
137 return Err(Error::Eval(
138 "malformed WAV file: PCM data ends mid-frame".to_owned(),
139 ));
140 }
141 buffers.push(PcmBuffer::i16(
142 parsed.spec,
143 chunk.len() / channels,
144 chunk.to_vec(),
145 )?);
146 }
147 Ok((parsed.spec, buffers))
148}
149
150struct ParsedWav {
151 spec: PcmSpec,
152 samples: Vec<i16>,
153}
154
155fn parse_wav_i16(bytes: &[u8]) -> Result<ParsedWav> {
156 if bytes.len() < 12 || &bytes[0..4] != b"RIFF" || &bytes[8..12] != b"WAVE" {
157 return Err(Error::Eval(
158 "malformed WAV file: missing RIFF/WAVE header".to_owned(),
159 ));
160 }
161 let mut pos = 12usize;
162 let mut fmt: Option<(usize, u32, u16)> = None;
163 let mut data: Option<&[u8]> = None;
164 while pos + 8 <= bytes.len() {
165 let chunk_id = &bytes[pos..pos + 4];
166 let len = read_u32_le(bytes, pos + 4)? as usize;
167 let start = pos + 8;
168 let end = start
169 .checked_add(len)
170 .ok_or_else(|| Error::Eval("malformed WAV file: chunk length overflow".to_owned()))?;
171 if end > bytes.len() {
172 return Err(Error::Eval(
173 "malformed WAV file: chunk extends past end of file".to_owned(),
174 ));
175 }
176 match chunk_id {
177 b"fmt " => fmt = Some(parse_fmt_chunk(&bytes[start..end])?),
178 b"data" => data = Some(&bytes[start..end]),
179 _ => {}
180 }
181 pos = end + usize::from(!len.is_multiple_of(2));
182 }
183 let (channels, sample_rate, bits_per_sample) =
184 fmt.ok_or_else(|| Error::Eval("malformed WAV file: missing fmt chunk".to_owned()))?;
185 if bits_per_sample != 16 {
186 return Err(Error::Eval(format!(
187 "malformed WAV file: unsupported PCM bit depth {bits_per_sample}"
188 )));
189 }
190 let data =
191 data.ok_or_else(|| Error::Eval("malformed WAV file: missing data chunk".to_owned()))?;
192 if !data.len().is_multiple_of(2) {
193 return Err(Error::Eval(
194 "malformed WAV file: PCM16 data has odd byte length".to_owned(),
195 ));
196 }
197 let spec = PcmSpec::i16(channels, sample_rate)?;
198 let samples = data
199 .chunks_exact(2)
200 .map(|bytes| i16::from_le_bytes([bytes[0], bytes[1]]))
201 .collect::<Vec<_>>();
202 if !samples.len().is_multiple_of(channels) {
203 return Err(Error::Eval(
204 "malformed WAV file: PCM data ends mid-frame".to_owned(),
205 ));
206 }
207 Ok(ParsedWav { spec, samples })
208}
209
210fn parse_fmt_chunk(bytes: &[u8]) -> Result<(usize, u32, u16)> {
211 if bytes.len() < 16 {
212 return Err(Error::Eval(
213 "malformed WAV file: fmt chunk is too short".to_owned(),
214 ));
215 }
216 let audio_format = read_u16_le(bytes, 0)?;
217 if audio_format != 1 {
218 return Err(Error::Eval(format!(
219 "malformed WAV file: unsupported audio format {audio_format}"
220 )));
221 }
222 let channels = usize::from(read_u16_le(bytes, 2)?);
223 let sample_rate = read_u32_le(bytes, 4)?;
224 let block_align = usize::from(read_u16_le(bytes, 12)?);
225 let bits_per_sample = read_u16_le(bytes, 14)?;
226 if block_align != channels.saturating_mul(2) {
227 return Err(Error::Eval(
228 "malformed WAV file: PCM16 block alignment mismatch".to_owned(),
229 ));
230 }
231 Ok((channels, sample_rate, bits_per_sample))
232}
233
234fn encode_wav_i16(spec: PcmSpec, samples: &[i16]) -> Result<Vec<u8>> {
235 if !samples.len().is_multiple_of(spec.channels()) {
236 return Err(Error::Eval(
237 "WAV writer received samples that end mid-frame".to_owned(),
238 ));
239 }
240 let channels = u16::try_from(spec.channels())
241 .map_err(|_| Error::Eval("WAV channel count exceeds u16".to_owned()))?;
242 let data_len = samples
243 .len()
244 .checked_mul(2)
245 .ok_or_else(|| Error::Eval("WAV data length overflow".to_owned()))?;
246 let data_len_u32 =
247 u32::try_from(data_len).map_err(|_| Error::Eval("WAV data exceeds u32".to_owned()))?;
248 let riff_len = 36u32
249 .checked_add(data_len_u32)
250 .ok_or_else(|| Error::Eval("WAV RIFF length overflow".to_owned()))?;
251 let block_align = channels
252 .checked_mul(2)
253 .ok_or_else(|| Error::Eval("WAV block alignment overflow".to_owned()))?;
254 let byte_rate = spec
255 .sample_rate_hz()
256 .checked_mul(u32::from(block_align))
257 .ok_or_else(|| Error::Eval("WAV byte rate overflow".to_owned()))?;
258
259 let mut out = Vec::with_capacity(44 + data_len);
260 out.extend_from_slice(b"RIFF");
261 out.extend_from_slice(&riff_len.to_le_bytes());
262 out.extend_from_slice(b"WAVEfmt ");
263 out.extend_from_slice(&16u32.to_le_bytes());
264 out.extend_from_slice(&1u16.to_le_bytes());
265 out.extend_from_slice(&channels.to_le_bytes());
266 out.extend_from_slice(&spec.sample_rate_hz().to_le_bytes());
267 out.extend_from_slice(&byte_rate.to_le_bytes());
268 out.extend_from_slice(&block_align.to_le_bytes());
269 out.extend_from_slice(&16u16.to_le_bytes());
270 out.extend_from_slice(b"data");
271 out.extend_from_slice(&data_len_u32.to_le_bytes());
272 for sample in samples {
273 out.extend_from_slice(&sample.to_le_bytes());
274 }
275 Ok(out)
276}
277
278fn read_u16_le(bytes: &[u8], offset: usize) -> Result<u16> {
279 let end = offset + 2;
280 let slice = bytes.get(offset..end).ok_or_else(|| {
281 Error::Eval(format!(
282 "malformed WAV file: unexpected end at byte {offset}"
283 ))
284 })?;
285 Ok(u16::from_le_bytes([slice[0], slice[1]]))
286}
287
288fn read_u32_le(bytes: &[u8], offset: usize) -> Result<u32> {
289 let end = offset + 4;
290 let slice = bytes.get(offset..end).ok_or_else(|| {
291 Error::Eval(format!(
292 "malformed WAV file: unexpected end at byte {offset}"
293 ))
294 })?;
295 Ok(u32::from_le_bytes([slice[0], slice[1], slice[2], slice[3]]))
296}