Skip to main content

riff_wave_core/
demux.rs

1//! RIFF/WAVE demux — parses a complete in-memory file.
2
3#![forbid(unsafe_code)]
4
5use bytes::Bytes;
6
7use crate::error::Error;
8use crate::types::{SampleFormat, WaveFormat};
9
10/// Parse a complete RIFF/WAVE byte buffer into its format and raw PCM `data` payload.
11///
12/// RIFF has no fragmented/streamable profile in scope here (mirrors [`crate::Muxer`]'s
13/// buffer-until-`finish()` design) — the whole file must be available up front.
14/// Unknown chunks (`LIST`, `fact`, …) are skipped.
15pub fn parse(data: &[u8]) -> Result<(WaveFormat, Bytes), Error> {
16    if data.len() < 12 || &data[0..4] != b"RIFF" || &data[8..12] != b"WAVE" {
17        return Err(Error::NotRiffWave);
18    }
19
20    let mut format = None;
21    let mut payload = None;
22    let mut pos = 12;
23    while pos + 8 <= data.len() {
24        let tag = &data[pos..pos + 4];
25        let size = u32::from_le_bytes([data[pos + 4], data[pos + 5], data[pos + 6], data[pos + 7]])
26            as usize;
27        let body_start = pos + 8;
28        let body_end = body_start.saturating_add(size).min(data.len());
29        let body = &data[body_start..body_end];
30
31        match tag {
32            b"fmt " => format = Some(parse_fmt_chunk(body)?),
33            b"data" => payload = Some(Bytes::copy_from_slice(body)),
34            _ => {}
35        }
36
37        pos = body_end + (size % 2); // word-aligned: 1 pad byte after an odd-sized chunk
38    }
39
40    let format = format.ok_or(Error::MissingFmtChunk)?;
41    let payload = payload.unwrap_or_default();
42    Ok((format, payload))
43}
44
45fn parse_fmt_chunk(body: &[u8]) -> Result<WaveFormat, Error> {
46    if body.len() < 16 {
47        return Err(Error::TruncatedFmtChunk);
48    }
49    let tag = u16::from_le_bytes([body[0], body[1]]);
50    let sample_format = SampleFormat::from_tag(tag).ok_or(Error::UnsupportedFormatTag(tag))?;
51    let channels = u16::from_le_bytes([body[2], body[3]]);
52    let sample_rate = u32::from_le_bytes([body[4], body[5], body[6], body[7]]);
53    let bits_per_sample = u16::from_le_bytes([body[14], body[15]]);
54    Ok(WaveFormat {
55        sample_format,
56        channels,
57        sample_rate,
58        bits_per_sample,
59    })
60}
61
62#[cfg(test)]
63#[path = "demux_tests.rs"]
64mod tests;