Skip to main content

wav_codec/
read.rs

1use std::fmt;
2use std::fs::File;
3use std::io::{self, BufReader, Read, Seek, SeekFrom};
4use std::iter::FusedIterator;
5use std::marker::PhantomData;
6use std::path::Path;
7
8use crate::sample::{decode_block, Codec};
9use crate::{
10    Error, Result, Sample, SampleFormat, WavSpec, WavSpecEx, GUID_PCM, TAG_EXTENSIBLE,
11    TAG_IEEE_FLOAT, TAG_PCM,
12};
13
14/// Size of the block buffer the reader refills in bulk.
15const BLOCK: usize = 64 * 1024;
16
17/// Streaming WAV decoder over any `io::Read`.
18///
19/// Construction parses the RIFF header, the `fmt ` chunk, and every chunk
20/// up to and including the `data` chunk header. Samples are then decoded
21/// from a 64 KiB block buffer that is refilled with bulk reads clamped to
22/// the declared `data` boundary, so the underlying reader is never read
23/// past the end of the audio payload.
24pub struct WavReader<R> {
25    inner: R,
26    spec: WavSpecEx,
27    codec: Codec,
28    /// Declared `data` payload length in bytes.
29    data_len: u32,
30    /// Payload bytes not yet pulled into the block buffer.
31    remaining: u32,
32    buf: Vec<u8>,
33    pos: usize,
34    end: usize,
35}
36
37impl WavReader<BufReader<File>> {
38    /// Open a WAV file from disk.
39    pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
40        let file = File::open(path)?;
41        WavReader::new(BufReader::new(file))
42    }
43}
44
45impl<R: Read> WavReader<R> {
46    /// Parse the headers from `reader` and position it at the first sample.
47    ///
48    /// Fails on a malformed container ([`Error::Format`]), an encoding
49    /// outside the crate's scope ([`Error::Unsupported`]), or a stream that
50    /// ends inside the headers ([`Error::Io`]).
51    pub fn new(mut reader: R) -> Result<Self> {
52        let mut riff = [0u8; 12];
53        reader.read_exact(&mut riff)?;
54        match &riff[0..4] {
55            b"RIFF" => {}
56            b"RF64" | b"BW64" => return Err(Error::Unsupported("RF64 containers (over 4 GiB)")),
57            b"RIFX" => return Err(Error::Unsupported("RIFX big-endian containers")),
58            _ => return Err(Error::Format("not a RIFF file")),
59        }
60        if &riff[8..12] != b"WAVE" {
61            return Err(Error::Format("RIFF form type is not WAVE"));
62        }
63
64        let mut format: Option<(WavSpecEx, Codec)> = None;
65        let (spec, codec, data_len) = loop {
66            let mut header = [0u8; 8];
67            read_chunk_header(&mut reader, &mut header)?;
68            if !header[0..4].iter().all(|b| (0x20..=0x7E).contains(b)) {
69                return Err(Error::Format("chunk id is not printable ASCII"));
70            }
71            let size = u32::from_le_bytes([header[4], header[5], header[6], header[7]]);
72            match &header[0..4] {
73                b"fmt " => {
74                    if format.is_some() {
75                        return Err(Error::Format("duplicate fmt chunk"));
76                    }
77                    format = Some(parse_fmt(&mut reader, size)?);
78                }
79                b"data" => {
80                    let (spec, codec) = match format {
81                        Some(f) => f,
82                        None => return Err(Error::Format("data chunk before fmt chunk")),
83                    };
84                    let block_align =
85                        u32::from(spec.spec.channels) * u32::from(spec.bytes_per_sample);
86                    if size % block_align != 0 {
87                        return Err(Error::Format(
88                            "data length is not a multiple of the frame size",
89                        ));
90                    }
91                    break (spec, codec, size);
92                }
93                _ => skip_chunk(&mut reader, size)?,
94            }
95        };
96
97        Ok(WavReader {
98            inner: reader,
99            spec,
100            codec,
101            data_len,
102            remaining: data_len,
103            buf: vec![0u8; BLOCK],
104            pos: 0,
105            end: 0,
106        })
107    }
108
109    /// The stream parameters.
110    pub fn spec(&self) -> WavSpec {
111        self.spec.spec
112    }
113
114    /// The stream parameters plus the container width.
115    pub fn spec_ex(&self) -> WavSpecEx {
116        self.spec
117    }
118
119    /// Number of frames (samples per channel) the `data` chunk declares.
120    pub fn duration(&self) -> u32 {
121        self.data_len / self.block_align()
122    }
123
124    /// Total number of samples across all channels the `data` chunk declares.
125    ///
126    /// For a stream the declared count can exceed what exists. The sample
127    /// iterators report that shortfall as an error.
128    pub fn len(&self) -> u32 {
129        self.data_len / u32::from(self.codec.container)
130    }
131
132    /// Whether the `data` chunk declares zero samples.
133    pub fn is_empty(&self) -> bool {
134        self.data_len == 0
135    }
136
137    /// Iterate over the remaining samples, borrowing the reader.
138    ///
139    /// Items are `Result<S>`. The first error fuses the iterator. A type
140    /// that cannot represent the file's samples yields one
141    /// [`Error::TooWide`].
142    pub fn samples<S: Sample>(&mut self) -> Samples<'_, R, S> {
143        let pending = self.check_read::<S>().err();
144        Samples {
145            reader: self,
146            pending,
147            done: false,
148            _sample: PhantomData,
149        }
150    }
151
152    /// Iterate over the remaining samples, consuming the reader.
153    pub fn into_samples<S: Sample>(self) -> IntoSamples<R, S> {
154        let pending = self.check_read::<S>().err();
155        IntoSamples {
156            reader: self,
157            pending,
158            done: false,
159            _sample: PhantomData,
160        }
161    }
162
163    /// Decode samples into `buf` and return how many were written.
164    ///
165    /// The buffer is filled completely unless the `data` chunk ends first,
166    /// so a count below `buf.len()` means the end of the audio, and `Ok(0)`
167    /// on a later call confirms it. A stream that ends before the declared
168    /// `data` size returns `Err` instead of a short count.
169    pub fn read_samples<S: Sample>(&mut self, buf: &mut [S]) -> Result<usize> {
170        self.check_read::<S>()?;
171        let width = self.codec.width();
172        let mut filled = 0;
173        while filled < buf.len() {
174            let available = (self.end - self.pos) / width;
175            if available == 0 {
176                if self.refill()? {
177                    continue;
178                }
179                break;
180            }
181            let n = available.min(buf.len() - filled);
182            let bytes = n * width;
183            decode_block(
184                &self.codec,
185                &self.buf[self.pos..self.pos + bytes],
186                &mut buf[filled..filled + n],
187            );
188            self.pos += bytes;
189            filled += n;
190        }
191        Ok(filled)
192    }
193
194    /// Return the underlying reader.
195    ///
196    /// Bytes pulled into the block buffer but not yet yielded are lost.
197    pub fn into_inner(self) -> R {
198        self.inner
199    }
200
201    fn block_align(&self) -> u32 {
202        u32::from(self.spec.spec.channels) * u32::from(self.codec.container)
203    }
204
205    fn check_read<S: Sample>(&self) -> Result<()> {
206        if <S as crate::sample::SampleOps>::accepts_read(&self.codec) {
207            Ok(())
208        } else {
209            Err(Error::TooWide)
210        }
211    }
212
213    /// Samples declared but not yet yielded.
214    fn samples_remaining(&self) -> usize {
215        let buffered = self.end - self.pos;
216        let declared = self.remaining as usize;
217        (buffered + declared) / self.codec.width()
218    }
219
220    /// Decode the next sample, or `None` at the declared end of the data.
221    #[inline]
222    fn next_sample<S: Sample>(&mut self) -> Option<Result<S>> {
223        let width = self.codec.width();
224        if self.pos + width > self.end {
225            match self.refill() {
226                Ok(true) => {}
227                Ok(false) => return None,
228                Err(e) => return Some(Err(e)),
229            }
230        }
231        let bytes = &self.buf[self.pos..self.pos + width];
232        self.pos += width;
233        Some(Ok(<S as crate::sample::SampleOps>::decode(
234            bytes,
235            &self.codec,
236        )))
237    }
238
239    /// Pull more payload into the block buffer until at least one whole
240    /// sample is available.
241    ///
242    /// Returns `Ok(true)` when a sample can be decoded, `Ok(false)` at the
243    /// clean end of the declared payload, and an error when the stream ends
244    /// short of the declared size or the reader fails.
245    #[inline(never)]
246    fn refill(&mut self) -> Result<bool> {
247        let width = self.codec.width();
248        let leftover = self.end - self.pos;
249        if leftover > 0 && self.pos > 0 {
250            self.buf.copy_within(self.pos..self.end, 0);
251        }
252        self.pos = 0;
253        self.end = leftover;
254        while self.end < width {
255            let room = self.buf.len() - self.end;
256            let want = room.min(self.remaining as usize);
257            if want == 0 {
258                break;
259            }
260            match self.inner.read(&mut self.buf[self.end..self.end + want]) {
261                Ok(0) => {
262                    return Err(Error::Format(
263                        "stream ends before the declared end of the data chunk",
264                    ))
265                }
266                Ok(n) => {
267                    let n = n.min(want);
268                    self.end += n;
269                    self.remaining -= n as u32;
270                }
271                Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
272                Err(e) => return Err(Error::Io(e)),
273            }
274        }
275        if self.end >= width {
276            Ok(true)
277        } else if self.end == 0 {
278            Ok(false)
279        } else {
280            Err(Error::Format(
281                "stream ends before the declared end of the data chunk",
282            ))
283        }
284    }
285}
286
287impl<R: Read + Seek> WavReader<R> {
288    /// Position the reader at `frame` (a frame is one sample per channel).
289    ///
290    /// Seeking advances by the container width, so a 24-in-32 file seeks in
291    /// 4-byte steps. `frame` may equal [`duration`](Self::duration), which
292    /// leaves no samples to read. Anything past that is `InvalidInput`.
293    pub fn seek(&mut self, frame: u32) -> io::Result<()> {
294        if frame > self.duration() {
295            return Err(io::Error::new(
296                io::ErrorKind::InvalidInput,
297                "seek past the end of the data chunk",
298            ));
299        }
300        let target = u64::from(frame) * u64::from(self.block_align());
301        let consumed = u64::from(self.data_len - self.remaining);
302        let current = self.inner.stream_position()?;
303        let data_start = current - consumed;
304        self.inner.seek(SeekFrom::Start(data_start + target))?;
305        self.pos = 0;
306        self.end = 0;
307        self.remaining = self.data_len - target as u32;
308        Ok(())
309    }
310}
311
312/// Read a chunk header, distinguishing a clean end of stream at a chunk
313/// boundary (no `data` chunk at all) from a header cut short.
314fn read_chunk_header<R: Read>(reader: &mut R, header: &mut [u8; 8]) -> Result<()> {
315    let mut got = 0;
316    while got < header.len() {
317        match reader.read(&mut header[got..]) {
318            Ok(0) if got == 0 => return Err(Error::Format("no data chunk")),
319            Ok(0) => {
320                return Err(Error::Io(io::Error::new(
321                    io::ErrorKind::UnexpectedEof,
322                    "stream ends inside a chunk header",
323                )))
324            }
325            Ok(n) => got += n,
326            Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
327            Err(e) => return Err(Error::Io(e)),
328        }
329    }
330    Ok(())
331}
332
333fn skip<R: Read>(reader: &mut R, bytes: u64) -> Result<()> {
334    let copied = io::copy(&mut reader.by_ref().take(bytes), &mut io::sink())?;
335    if copied != bytes {
336        return Err(Error::Io(io::Error::new(
337            io::ErrorKind::UnexpectedEof,
338            "stream ends inside a chunk",
339        )));
340    }
341    Ok(())
342}
343
344/// Skip a chunk payload, then require the RIFF pad byte to be zero when
345/// `size` is odd.
346fn skip_chunk<R: Read>(reader: &mut R, size: u32) -> Result<()> {
347    skip(reader, u64::from(size))?;
348    if size & 1 == 1 {
349        skip_odd_pad(reader)?;
350    }
351    Ok(())
352}
353
354fn skip_odd_pad<R: Read>(reader: &mut R) -> Result<()> {
355    let mut pad = [0u8; 1];
356    loop {
357        match reader.read(&mut pad) {
358            Ok(0) => return Err(Error::Format("missing pad byte after odd-sized chunk")),
359            Ok(_) if pad[0] == 0 => return Ok(()),
360            Ok(_) => return Err(Error::Format("nonzero pad byte after odd-sized chunk")),
361            Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
362            Err(e) => return Err(Error::Io(e)),
363        }
364    }
365}
366
367fn parse_fmt<R: Read>(reader: &mut R, size: u32) -> Result<(WavSpecEx, Codec)> {
368    if size < 16 {
369        return Err(Error::Format("fmt chunk too small"));
370    }
371    let mut head = [0u8; 16];
372    reader.read_exact(&mut head)?;
373    let mut consumed: u64 = 16;
374
375    let tag = u16::from_le_bytes([head[0], head[1]]);
376    let channels = u16::from_le_bytes([head[2], head[3]]);
377    let sample_rate = u32::from_le_bytes([head[4], head[5], head[6], head[7]]);
378    // head[8..12] is byte_rate. Recalculate it from the other fields.
379    let block_align = u16::from_le_bytes([head[12], head[13]]);
380    let bits_per_sample = u16::from_le_bytes([head[14], head[15]]);
381
382    let mut extension: Option<[u8; 22]> = None;
383    if size >= 18 {
384        let mut cb = [0u8; 2];
385        reader.read_exact(&mut cb)?;
386        consumed += 2;
387        let cb_size = u16::from_le_bytes(cb);
388        if tag == TAG_EXTENSIBLE {
389            if size < 40 || cb_size < 22 {
390                return Err(Error::Format(
391                    "fmt chunk too small for WAVE_FORMAT_EXTENSIBLE",
392                ));
393            }
394            let mut ext = [0u8; 22];
395            reader.read_exact(&mut ext)?;
396            consumed += 22;
397            extension = Some(ext);
398        }
399    } else if tag == TAG_EXTENSIBLE {
400        return Err(Error::Format(
401            "fmt chunk too small for WAVE_FORMAT_EXTENSIBLE",
402        ));
403    }
404    skip(reader, u64::from(size) - consumed)?;
405    if size & 1 == 1 {
406        skip_odd_pad(reader)?;
407    }
408
409    if channels == 0 {
410        return Err(Error::Format("zero channels"));
411    }
412    if sample_rate == 0 {
413        return Err(Error::Format("zero sample rate"));
414    }
415    if block_align == 0 || block_align % channels != 0 {
416        return Err(Error::Format(
417            "block align is not a nonzero multiple of the channel count",
418        ));
419    }
420    let container = block_align / channels;
421    // u32: a hostile block_align of 0xFF01 over one channel overflows u16 here.
422    let container_bits = u32::from(container) * 8;
423
424    // The WAVEFORMATEX bits_per_sample field is always bound by the
425    // container, including WAVE_FORMAT_EXTENSIBLE when valid_bits is set.
426    if u32::from(bits_per_sample) > container_bits {
427        return Err(Error::Format("bits per sample exceed the container width"));
428    }
429
430    let (format, bits) = match tag {
431        TAG_PCM => (SampleFormat::Int, bits_per_sample),
432        TAG_IEEE_FLOAT => (SampleFormat::Float, bits_per_sample),
433        TAG_EXTENSIBLE => {
434            let ext = match extension {
435                Some(e) => e,
436                None => {
437                    return Err(Error::Format(
438                        "fmt chunk too small for WAVE_FORMAT_EXTENSIBLE",
439                    ))
440                }
441            };
442            let valid_bits = u16::from_le_bytes([ext[0], ext[1]]);
443            // ext[2..6] is the channel mask, carried but not interpreted.
444            let guid = &ext[6..22];
445            let format = match (guid[0], guid[1], guid[2..16] == GUID_PCM[2..]) {
446                (0x01, 0x00, true) => SampleFormat::Int,
447                (0x03, 0x00, true) => SampleFormat::Float,
448                _ => return Err(Error::Unsupported("sub-format GUID")),
449            };
450            let bits = if valid_bits == 0 {
451                bits_per_sample
452            } else {
453                valid_bits
454            };
455            (format, bits)
456        }
457        _ => return Err(Error::Unsupported("compressed or unknown format tag")),
458    };
459
460    if bits == 0 {
461        return Err(Error::Format("zero bits per sample"));
462    }
463    if u32::from(bits) > container_bits {
464        return Err(Error::Format("bits per sample exceed the container width"));
465    }
466    match format {
467        SampleFormat::Int => {
468            if container > 4 {
469                return Err(Error::Unsupported("PCM samples wider than 32 bits"));
470            }
471            // One-byte samples are offset binary, which only has an 8-bit form.
472            if container == 1 && bits != 8 {
473                return Err(Error::Format("8-bit PCM must declare 8 valid bits"));
474            }
475        }
476        SampleFormat::Float => {
477            if !((bits == 32 && container == 4) || (bits == 64 && container == 8)) {
478                return Err(Error::Unsupported(
479                    "float samples other than 32-bit and 64-bit",
480                ));
481            }
482        }
483    }
484
485    let spec = WavSpecEx {
486        spec: WavSpec {
487            channels,
488            sample_rate,
489            bits_per_sample: bits,
490            sample_format: format,
491        },
492        bytes_per_sample: container,
493    };
494    Ok((spec, Codec::new(format, bits, container)))
495}
496
497/// Borrowing sample iterator from [`WavReader::samples`].
498pub struct Samples<'a, R, S> {
499    reader: &'a mut WavReader<R>,
500    pending: Option<Error>,
501    done: bool,
502    _sample: PhantomData<S>,
503}
504
505impl<'a, R: Read, S: Sample> Iterator for Samples<'a, R, S> {
506    type Item = Result<S>;
507
508    #[inline]
509    fn next(&mut self) -> Option<Result<S>> {
510        if self.done {
511            return None;
512        }
513        if let Some(e) = self.pending.take() {
514            self.done = true;
515            return Some(Err(e));
516        }
517        let item = self.reader.next_sample::<S>();
518        if let Some(Err(_)) = item {
519            self.done = true;
520        }
521        item
522    }
523
524    fn size_hint(&self) -> (usize, Option<usize>) {
525        if self.done {
526            (0, Some(0))
527        } else {
528            (0, Some(self.reader.samples_remaining()))
529        }
530    }
531}
532
533impl<'a, R: Read, S: Sample> FusedIterator for Samples<'a, R, S> {}
534
535/// Owning sample iterator from [`WavReader::into_samples`].
536pub struct IntoSamples<R, S> {
537    reader: WavReader<R>,
538    pending: Option<Error>,
539    done: bool,
540    _sample: PhantomData<S>,
541}
542
543impl<R, S> IntoSamples<R, S> {
544    /// Return the reader, positioned after the last sample yielded.
545    pub fn into_inner(self) -> WavReader<R> {
546        self.reader
547    }
548}
549
550impl<R: Read, S: Sample> Iterator for IntoSamples<R, S> {
551    type Item = Result<S>;
552
553    #[inline]
554    fn next(&mut self) -> Option<Result<S>> {
555        if self.done {
556            return None;
557        }
558        if let Some(e) = self.pending.take() {
559            self.done = true;
560            return Some(Err(e));
561        }
562        let item = self.reader.next_sample::<S>();
563        if let Some(Err(_)) = item {
564            self.done = true;
565        }
566        item
567    }
568
569    fn size_hint(&self) -> (usize, Option<usize>) {
570        if self.done {
571            (0, Some(0))
572        } else {
573            (0, Some(self.reader.samples_remaining()))
574        }
575    }
576}
577
578impl<R: Read, S: Sample> FusedIterator for IntoSamples<R, S> {}
579
580impl<R> fmt::Debug for WavReader<R> {
581    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
582        f.debug_struct("WavReader")
583            .field("spec", &self.spec)
584            .field("data_len", &self.data_len)
585            .field("remaining", &self.remaining)
586            .finish_non_exhaustive()
587    }
588}
589
590impl<R, S> fmt::Debug for Samples<'_, R, S> {
591    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
592        f.debug_struct("Samples")
593            .field("pending", &self.pending.is_some())
594            .field("done", &self.done)
595            .finish_non_exhaustive()
596    }
597}
598
599impl<R, S> fmt::Debug for IntoSamples<R, S> {
600    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
601        f.debug_struct("IntoSamples")
602            .field("pending", &self.pending.is_some())
603            .field("done", &self.done)
604            .finish_non_exhaustive()
605    }
606}