1use super::{AudioBuffer, Error, Result};
2
3fn invalid(what: impl std::fmt::Display) -> Error {
4 Error::Invalid(format!("WAV: {what}"))
5}
6
7fn le16(b: &[u8], at: usize) -> Result<u16> {
8 b.get(at..at + 2)
9 .map(|s| u16::from_le_bytes([s[0], s[1]]))
10 .ok_or_else(|| invalid("the fmt chunk is too short"))
11}
12
13fn le32(b: &[u8], at: usize) -> Result<u32> {
14 b.get(at..at + 4)
15 .map(|s| u32::from_le_bytes([s[0], s[1], s[2], s[3]]))
16 .ok_or_else(|| invalid("the file ends inside a chunk header"))
17}
18
19#[derive(Clone, Copy, PartialEq, Eq)]
20enum Sample {
21 Int,
22 Float,
23}
24
25pub fn decode(data: &[u8]) -> Result<AudioBuffer> {
26 if data.get(..4) != Some(b"RIFF") || data.get(8..12) != Some(b"WAVE") {
27 return Err(invalid("not a RIFF WAVE file"));
28 }
29 let mut format = None;
30 let mut samples = None;
31 let mut at = 12usize;
32 while at + 8 <= data.len() {
33 let len = le32(data, at + 4)? as usize;
34 let start = at + 8;
35 let end = start.saturating_add(len).min(data.len());
36 let body = &data[start..end];
37 match &data[at..at + 4] {
38 b"fmt " => {
39 let tag = le16(body, 0)?;
40 let channels = le16(body, 2)?;
41 let rate = le32(body, 4).map_err(|_| invalid("the fmt chunk is too short"))?;
42 let align = le16(body, 12)?;
43 let bits = le16(body, 14)?;
44 let tag = if tag == 0xfffe { le16(body, 24)? } else { tag };
45 let kind = match (tag, bits) {
46 (1, 8 | 16 | 24 | 32) => Sample::Int,
47 (3, 32 | 64) => Sample::Float,
48 _ => {
49 return Err(Error::Unsupported(format!(
50 "WAV format {tag} with {bits}-bit samples; PCM 8, 16, 24 or 32-bit and float 32 or 64-bit are read"
51 )));
52 }
53 };
54 if channels == 0 || rate == 0 {
55 return Err(invalid(format!("{channels} channels at {rate} Hz")));
56 }
57 if usize::from(align) != usize::from(channels) * usize::from(bits / 8) {
58 return Err(invalid(format!(
59 "a block of {align} bytes for {channels} channels of {bits} bits"
60 )));
61 }
62 format = Some((kind, channels, rate, bits));
63 }
64 b"data" => {
65 samples = Some(body);
66 if format.is_some() {
67 break;
68 }
69 }
70 _ => {}
71 }
72 at = start.saturating_add(len).saturating_add(len & 1);
73 }
74 let (kind, channels, rate, bits) = format.ok_or_else(|| invalid("no fmt chunk"))?;
75 let body = samples.ok_or_else(|| invalid("no data chunk"))?;
76 let width = usize::from(bits / 8);
77 let frame = width * usize::from(channels);
78 let body = &body[..body.len() - body.len() % frame];
79 let out: Vec<f32> = match (kind, bits) {
80 (Sample::Int, 8) => body
81 .iter()
82 .map(|&b| (f32::from(b) - 128.0) / 128.0)
83 .collect(),
84 (Sample::Int, 16) => body
85 .chunks_exact(2)
86 .map(|s| f32::from(i16::from_le_bytes([s[0], s[1]])) / 32768.0)
87 .collect(),
88 (Sample::Int, 24) => body
89 .chunks_exact(3)
90 .map(|s| (i32::from_le_bytes([0, s[0], s[1], s[2]]) >> 8) as f32 / 8_388_608.0)
91 .collect(),
92 (Sample::Int, _) => body
93 .chunks_exact(4)
94 .map(|s| i32::from_le_bytes([s[0], s[1], s[2], s[3]]) as f32 / 2_147_483_648.0)
95 .collect(),
96 (Sample::Float, 32) => body
97 .chunks_exact(4)
98 .map(|s| clean(f32::from_le_bytes([s[0], s[1], s[2], s[3]])))
99 .collect(),
100 (Sample::Float, _) => body
101 .chunks_exact(8)
102 .map(|s| {
103 let mut b = [0u8; 8];
104 b.copy_from_slice(s);
105 clean(f64::from_le_bytes(b) as f32)
106 })
107 .collect(),
108 };
109 Ok(AudioBuffer {
110 samples: out,
111 rate,
112 channels,
113 })
114}
115
116fn clean(v: f32) -> f32 {
117 if v.is_finite() { v } else { 0.0 }
118}