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