1use super::header::{WavHeader, WavSampleFormat};
4use super::writer::{DitherMode, WavWriteOptions};
5use crate::engine::{AudioBlock, AudioSpec, EngineError};
6use std::fs::File;
7use std::io::{self, BufReader, BufWriter, Read, Seek, SeekFrom, Write};
8use std::path::Path;
9
10pub struct WavStreamReader<R> {
12 reader: R,
13 header: WavHeader,
14 samples_read_per_channel: usize,
15 data_chunk_offset: u64,
16}
17
18impl WavStreamReader<BufReader<File>> {
19 pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> {
21 let file = File::open(path)?;
22 let reader = BufReader::new(file);
23 Self::new(reader)
24 }
25}
26
27impl<R: Read + Seek> WavStreamReader<R> {
28 pub fn new(mut reader: R) -> io::Result<Self> {
30 let mut fourcc = [0u8; 4];
31
32 reader.read_exact(&mut fourcc)?;
34 if &fourcc != b"RIFF" {
35 return Err(io::Error::new(
36 io::ErrorKind::InvalidData,
37 "Not a valid RIFF file",
38 ));
39 }
40
41 let mut riff_size_bytes = [0u8; 4];
42 reader.read_exact(&mut riff_size_bytes)?;
43
44 reader.read_exact(&mut fourcc)?;
45 if &fourcc != b"WAVE" {
46 return Err(io::Error::new(
47 io::ErrorKind::InvalidData,
48 "Not a WAVE format",
49 ));
50 }
51
52 let mut audio_format: u16 = 0;
53 let mut num_channels: u16 = 0;
54 let mut sample_rate: u32 = 0;
55 let mut bits_per_sample: u16 = 0;
56 let mut data_chunk_size: u32 = 0;
57 let mut fmt_found = false;
58
59 loop {
61 if reader.read_exact(&mut fourcc).is_err() {
62 break;
63 }
64
65 let mut chunk_size_bytes = [0u8; 4];
66 reader.read_exact(&mut chunk_size_bytes)?;
67 let chunk_size = u32::from_le_bytes(chunk_size_bytes);
68
69 match &fourcc {
70 b"fmt " => {
71 let mut format_tag_bytes = [0u8; 2];
72 let mut channels_bytes = [0u8; 2];
73 let mut samplerate_bytes = [0u8; 4];
74 let mut byterate_bytes = [0u8; 4];
75 let mut blockalign_bytes = [0u8; 2];
76 let mut bitspersample_bytes = [0u8; 2];
77
78 reader.read_exact(&mut format_tag_bytes)?;
79 reader.read_exact(&mut channels_bytes)?;
80 reader.read_exact(&mut samplerate_bytes)?;
81 reader.read_exact(&mut byterate_bytes)?;
82 reader.read_exact(&mut blockalign_bytes)?;
83 reader.read_exact(&mut bitspersample_bytes)?;
84
85 audio_format = u16::from_le_bytes(format_tag_bytes);
86 num_channels = u16::from_le_bytes(channels_bytes);
87 sample_rate = u32::from_le_bytes(samplerate_bytes);
88 bits_per_sample = u16::from_le_bytes(bitspersample_bytes);
89
90 if chunk_size > 16 {
91 reader.seek(SeekFrom::Current((chunk_size - 16) as i64))?;
92 }
93 fmt_found = true;
94 }
95 b"data" => {
96 data_chunk_size = chunk_size;
97 break;
98 }
99 _ => {
100 reader.seek(SeekFrom::Current(chunk_size as i64))?;
101 }
102 }
103 }
104
105 if !fmt_found {
106 return Err(io::Error::new(
107 io::ErrorKind::InvalidData,
108 "Missing 'fmt ' chunk in WAV file",
109 ));
110 }
111
112 let sample_format = match (audio_format, bits_per_sample) {
113 (1, 16) => WavSampleFormat::Pcm16Bit,
114 (1, 24) => WavSampleFormat::Pcm24Bit,
115 (3, 32) => WavSampleFormat::Pcm32BitFloat,
116 _ => {
117 return Err(io::Error::new(
118 io::ErrorKind::Unsupported,
119 format!("Unsupported WAV format: tag={audio_format}, bits={bits_per_sample}"),
120 ));
121 }
122 };
123
124 let bytes_per_sample = (bits_per_sample / 8) as usize;
125 let total_samples = (data_chunk_size as usize) / bytes_per_sample;
126 let samples_per_channel = if num_channels > 0 {
127 total_samples / (num_channels as usize)
128 } else {
129 0
130 };
131
132 let data_chunk_offset = reader.stream_position()?;
133 let header = WavHeader::new(
134 num_channels,
135 sample_rate,
136 sample_format,
137 samples_per_channel,
138 );
139
140 Ok(Self {
141 reader,
142 header,
143 samples_read_per_channel: 0,
144 data_chunk_offset,
145 })
146 }
147
148 pub fn header(&self) -> &WavHeader {
149 &self.header
150 }
151
152 pub fn spec(&self) -> Result<AudioSpec, EngineError> {
153 AudioSpec::new(
154 self.header.sample_rate as f32,
155 self.header.num_channels as usize,
156 )
157 }
158
159 pub fn total_frames(&self) -> usize {
160 self.header.num_samples_per_channel
161 }
162
163 pub fn frames_read(&self) -> usize {
164 self.samples_read_per_channel
165 }
166
167 pub fn frames_remaining(&self) -> usize {
168 self.total_frames()
169 .saturating_sub(self.samples_read_per_channel)
170 }
171
172 pub fn is_finished(&self) -> bool {
173 self.frames_remaining() == 0
174 }
175
176 pub fn read_block(&mut self, block: &mut AudioBlock<'_>) -> io::Result<usize> {
179 if block.channels() != self.header.num_channels as usize {
180 return Err(io::Error::new(
181 io::ErrorKind::InvalidInput,
182 format!(
183 "AudioBlock channels ({}) does not match WAV channels ({})",
184 block.channels(),
185 self.header.num_channels
186 ),
187 ));
188 }
189
190 let frames_to_read = block.frames().min(self.frames_remaining());
191 if frames_to_read == 0 {
192 return Ok(0);
193 }
194
195 let num_channels = self.header.num_channels as usize;
196 for frame in 0..frames_to_read {
197 for ch in 0..num_channels {
198 let sample_val = match self.header.format {
199 WavSampleFormat::Pcm16Bit => {
200 let mut b = [0u8; 2];
201 self.reader.read_exact(&mut b)?;
202 let i = i16::from_le_bytes(b);
203 if i < 0 {
204 i as f32 / 32768.0
205 } else {
206 i as f32 / 32767.0
207 }
208 }
209 WavSampleFormat::Pcm24Bit => {
210 let mut b = [0u8; 3];
211 self.reader.read_exact(&mut b)?;
212 let sign = if b[2] & 0x80 != 0 { 0xFF } else { 0x00 };
213 let i = i32::from_le_bytes([b[0], b[1], b[2], sign]);
214 if i < 0 {
215 i as f32 / 8388608.0
216 } else {
217 i as f32 / 8388607.0
218 }
219 }
220 WavSampleFormat::Pcm32BitFloat => {
221 let mut b = [0u8; 4];
222 self.reader.read_exact(&mut b)?;
223 f32::from_le_bytes(b)
224 }
225 };
226
227 let channel_slice = block.channel_mut(ch).expect("validated channel count");
228 channel_slice[frame] = sample_val;
229 }
230 }
231
232 self.samples_read_per_channel += frames_to_read;
233 Ok(frames_to_read)
234 }
235
236 pub fn rewind(&mut self) -> io::Result<()> {
238 self.reader.seek(SeekFrom::Start(self.data_chunk_offset))?;
239 self.samples_read_per_channel = 0;
240 Ok(())
241 }
242}
243
244pub struct WavStreamWriter<W> {
246 writer: W,
247 options: WavWriteOptions,
248 num_channels: u16,
249 sample_rate: u32,
250 frames_written: usize,
251 dither_state: u32,
252}
253
254impl WavStreamWriter<BufWriter<File>> {
255 pub fn create<P: AsRef<Path>>(
257 path: P,
258 num_channels: u16,
259 sample_rate: u32,
260 options: WavWriteOptions,
261 ) -> io::Result<Self> {
262 let file = File::create(path)?;
263 let writer = BufWriter::new(file);
264 Self::new(writer, num_channels, sample_rate, options)
265 }
266}
267
268impl<W: Write + Seek> WavStreamWriter<W> {
269 pub fn new(
271 mut writer: W,
272 num_channels: u16,
273 sample_rate: u32,
274 options: WavWriteOptions,
275 ) -> io::Result<Self> {
276 if num_channels == 0 {
277 return Err(io::Error::new(
278 io::ErrorKind::InvalidInput,
279 "Channels must be at least 1",
280 ));
281 }
282
283 let header = WavHeader::new(num_channels, sample_rate, options.format, 0);
285 let bits_per_sample = header.bits_per_sample();
286 let block_align = num_channels * (bits_per_sample / 8);
287 let byte_rate = sample_rate * (block_align as u32);
288
289 writer.write_all(b"RIFF")?;
291 writer.write_all(&0u32.to_le_bytes())?; writer.write_all(b"WAVE")?;
293
294 writer.write_all(b"fmt ")?;
296 writer.write_all(&16u32.to_le_bytes())?;
297 writer.write_all(&header.audio_format_tag().to_le_bytes())?;
298 writer.write_all(&num_channels.to_le_bytes())?;
299 writer.write_all(&sample_rate.to_le_bytes())?;
300 writer.write_all(&byte_rate.to_le_bytes())?;
301 writer.write_all(&block_align.to_le_bytes())?;
302 writer.write_all(&bits_per_sample.to_le_bytes())?;
303
304 writer.write_all(b"data")?;
306 writer.write_all(&0u32.to_le_bytes())?; Ok(Self {
309 writer,
310 options,
311 num_channels,
312 sample_rate,
313 frames_written: 0,
314 dither_state: 0xA5A5_1F3D,
315 })
316 }
317
318 pub fn frames_written(&self) -> usize {
319 self.frames_written
320 }
321
322 fn uniform_random(&mut self) -> f32 {
323 self.dither_state = self
324 .dither_state
325 .wrapping_mul(1_664_525)
326 .wrapping_add(1_013_904_223);
327 self.dither_state as f32 / u32::MAX as f32
328 }
329
330 fn apply_dither(&mut self, sample: f32, scale: f32) -> f32 {
331 if self.options.dither == DitherMode::Tpdf {
332 (sample + (self.uniform_random() - self.uniform_random()) / scale).clamp(-1.0, 1.0)
333 } else {
334 sample
335 }
336 }
337
338 pub fn write_block(&mut self, block: &AudioBlock<'_>) -> io::Result<()> {
340 self.write_block_frames(block, block.frames())
341 }
342
343 pub fn write_block_frames(
345 &mut self,
346 block: &AudioBlock<'_>,
347 frame_count: usize,
348 ) -> io::Result<()> {
349 if block.channels() != self.num_channels as usize {
350 return Err(io::Error::new(
351 io::ErrorKind::InvalidInput,
352 format!(
353 "AudioBlock channels ({}) does not match writer channels ({})",
354 block.channels(),
355 self.num_channels
356 ),
357 ));
358 }
359
360 let frames = frame_count.min(block.frames());
361 let num_ch = self.num_channels as usize;
362
363 for frame in 0..frames {
364 for ch in 0..num_ch {
365 let sample_val =
368 block.channel(ch).expect("validated channel count")[frame].clamp(-1.0, 1.0);
369
370 match self.options.format {
371 WavSampleFormat::Pcm16Bit => {
372 let dithered = self.apply_dither(sample_val, 32768.0);
373 let val: i16 = if dithered < 0.0 {
374 (dithered * 32768.0).round() as i16
375 } else {
376 (dithered * 32767.0).round() as i16
377 };
378 self.writer.write_all(&val.to_le_bytes())?;
379 }
380 WavSampleFormat::Pcm24Bit => {
381 let dithered = self.apply_dither(sample_val, 8388608.0);
382 let val: i32 = if dithered < 0.0 {
383 (dithered * 8388608.0).round() as i32
384 } else {
385 (dithered * 8388607.0).round() as i32
386 };
387 let bytes = val.to_le_bytes();
388 self.writer.write_all(&bytes[0..3])?;
389 }
390 WavSampleFormat::Pcm32BitFloat => {
391 self.writer.write_all(&sample_val.to_le_bytes())?;
392 }
393 }
394 }
395 }
396
397 self.frames_written += frames;
398 Ok(())
399 }
400
401 pub fn write_planar(&mut self, channels: &[&[f32]]) -> io::Result<()> {
403 if channels.len() != self.num_channels as usize {
404 return Err(io::Error::new(
405 io::ErrorKind::InvalidInput,
406 "Channel count mismatch",
407 ));
408 }
409 if channels.is_empty() {
410 return Ok(());
411 }
412
413 let frames = channels[0].len();
414 for ch in channels {
415 if ch.len() != frames {
416 return Err(io::Error::new(
417 io::ErrorKind::InvalidInput,
418 "All channel slices must have the same length",
419 ));
420 }
421 }
422
423 for frame in 0..frames {
424 for channel in channels.iter() {
425 let sample_val = channel[frame].clamp(-1.0, 1.0);
426 match self.options.format {
427 WavSampleFormat::Pcm16Bit => {
428 let dithered = self.apply_dither(sample_val, 32768.0);
429 let val: i16 = if dithered < 0.0 {
430 (dithered * 32768.0).round() as i16
431 } else {
432 (dithered * 32767.0).round() as i16
433 };
434 self.writer.write_all(&val.to_le_bytes())?;
435 }
436 WavSampleFormat::Pcm24Bit => {
437 let dithered = self.apply_dither(sample_val, 8388608.0);
438 let val: i32 = if dithered < 0.0 {
439 (dithered * 8388608.0).round() as i32
440 } else {
441 (dithered * 8388607.0).round() as i32
442 };
443 let bytes = val.to_le_bytes();
444 self.writer.write_all(&bytes[0..3])?;
445 }
446 WavSampleFormat::Pcm32BitFloat => {
447 self.writer.write_all(&sample_val.to_le_bytes())?;
448 }
449 }
450 }
451 }
452
453 self.frames_written += frames;
454 Ok(())
455 }
456
457 pub fn finalize(mut self) -> io::Result<WavHeader> {
459 let header = WavHeader::new(
460 self.num_channels,
461 self.sample_rate,
462 self.options.format,
463 self.frames_written,
464 );
465
466 let bits_per_sample = header.bits_per_sample();
467 let block_align = self.num_channels as u32 * (bits_per_sample as u32 / 8);
468 let data_chunk_size = (self.frames_written as u32) * block_align;
469 let riff_chunk_size = 36 + data_chunk_size;
470
471 self.writer.seek(SeekFrom::Start(4))?;
473 self.writer.write_all(&riff_chunk_size.to_le_bytes())?;
474
475 self.writer.seek(SeekFrom::Start(40))?;
477 self.writer.write_all(&data_chunk_size.to_le_bytes())?;
478
479 self.writer.seek(SeekFrom::End(0))?;
481 self.writer.flush()?;
482
483 Ok(header)
484 }
485}