devalang_wasm/utils/
wav_parser.rs1pub fn parse_wav_generic(data: &[u8]) -> Result<(u16, u32, Vec<i16>), String> {
11 if data.len() < 44 {
12 return Err("File too short for WAV header".into());
13 }
14
15 if &data[0..4] != b"RIFF" || &data[8..12] != b"WAVE" {
17 return Err("Invalid RIFF/WAVE header".into());
18 }
19
20 let mut pos = 12; let mut channels = 1u16;
22 let mut sample_rate = 44100u32;
23 let mut bits = 16u16;
24 let mut raw_bytes: Option<Vec<u8>> = None;
25
26 while pos + 8 <= data.len() {
28 let chunk_id = &data[pos..pos + 4];
29 let chunk_size = u32::from_le_bytes(data[pos + 4..pos + 8].try_into().unwrap()) as usize;
30 pos += 8;
31
32 if pos + chunk_size > data.len() {
33 break;
34 }
35
36 match chunk_id {
37 b"fmt " => {
38 if chunk_size < 16 {
39 return Err("fmt chunk too small".into());
40 }
41
42 let audio_format = u16::from_le_bytes(data[pos..pos + 2].try_into().unwrap());
43 channels = u16::from_le_bytes(data[pos + 2..pos + 4].try_into().unwrap());
44 sample_rate = u32::from_le_bytes(data[pos + 4..pos + 8].try_into().unwrap());
45 bits = u16::from_le_bytes(data[pos + 14..pos + 16].try_into().unwrap());
46
47 if audio_format != 1 {
48 return Err("Only uncompressed PCM supported".into());
49 }
50
51 if !(bits == 8 || bits == 16 || bits == 24 || bits == 32) {
52 return Err(format!(
53 "Unsupported bit depth {} (expected 8/16/24/32)",
54 bits
55 ));
56 }
57 }
58 b"data" => {
59 raw_bytes = Some(data[pos..pos + chunk_size].to_vec());
60 }
61 _ => { }
62 }
63
64 pos += chunk_size;
65 }
66
67 let bytes = raw_bytes.ok_or("data chunk not found".to_string())?;
68
69 let mut interleaved_f32: Vec<f32> = Vec::new();
71
72 match bits {
73 8 => {
74 for b in bytes.iter() {
76 interleaved_f32.push((*b as f32 - 128.0) / 128.0);
77 }
78 }
79 16 => {
80 for ch in bytes.chunks_exact(2) {
82 let v = i16::from_le_bytes([ch[0], ch[1]]);
83 interleaved_f32.push(v as f32 / 32768.0);
84 }
85 }
86 24 => {
87 for ch in bytes.chunks_exact(3) {
89 let assembled = (ch[0] as u32) | ((ch[1] as u32) << 8) | ((ch[2] as u32) << 16);
90
91 let signed = if (assembled & 0x800000) != 0 {
93 (assembled | 0xFF000000) as i32
94 } else {
95 assembled as i32
96 };
97
98 interleaved_f32.push(signed as f32 / 8388608.0);
99 }
100 }
101 32 => {
102 for ch in bytes.chunks_exact(4) {
104 let v = i32::from_le_bytes([ch[0], ch[1], ch[2], ch[3]]);
105 interleaved_f32.push(v as f32 / 2147483648.0);
106 }
107 }
108 _ => return Err("Unexpected bit depth".into()),
109 }
110
111 let chn = channels as usize;
112
113 if chn > 1 {
115 let frames = interleaved_f32.len() / chn;
116 let mut mono_f32 = Vec::with_capacity(frames);
117
118 for f in 0..frames {
119 let mut acc = 0.0;
120 for c in 0..chn {
121 acc += interleaved_f32[f * chn + c];
122 }
123 mono_f32.push(acc / chn as f32);
124 }
125
126 let mut out = Vec::with_capacity(mono_f32.len());
128 for s in mono_f32 {
129 out.push((s.clamp(-1.0, 1.0) * 32767.0) as i16);
130 }
131
132 Ok((1, sample_rate, out))
133 } else {
134 let mut out = Vec::with_capacity(interleaved_f32.len());
136 for s in interleaved_f32 {
137 out.push((s.clamp(-1.0, 1.0) * 32767.0) as i16);
138 }
139
140 Ok((1, sample_rate, out))
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn test_invalid_wav() {
150 let data = vec![0u8; 10];
151 assert!(parse_wav_generic(&data).is_err());
152 }
153
154 #[test]
155 fn test_valid_wav_header() {
156 let mut data = vec![0u8; 44];
158 data[0..4].copy_from_slice(b"RIFF");
159 data[8..12].copy_from_slice(b"WAVE");
160 data[12..16].copy_from_slice(b"fmt ");
161 data[16..20].copy_from_slice(&16u32.to_le_bytes()); data[20..22].copy_from_slice(&1u16.to_le_bytes()); data[22..24].copy_from_slice(&1u16.to_le_bytes()); data[24..28].copy_from_slice(&44100u32.to_le_bytes()); data[34..36].copy_from_slice(&16u16.to_le_bytes()); data[36..40].copy_from_slice(b"data");
167 data[40..44].copy_from_slice(&0u32.to_le_bytes()); let result = parse_wav_generic(&data);
170 assert!(result.is_ok());
171 }
172}