1#![allow(dead_code)]
15
16use alloc::vec::Vec;
17
18use crate::{
19 audio::{AudioConfig, SampleFormat},
20 error::KernelError,
21};
22
23const RIFF_MAGIC: [u8; 4] = [b'R', b'I', b'F', b'F'];
29
30const WAVE_MAGIC: [u8; 4] = [b'W', b'A', b'V', b'E'];
32
33const FMT_CHUNK_ID: [u8; 4] = [b'f', b'm', b't', b' '];
35
36const DATA_CHUNK_ID: [u8; 4] = [b'd', b'a', b't', b'a'];
38
39const AUDIO_FORMAT_PCM: u16 = 1;
41
42#[derive(Debug, Clone, Copy)]
48pub struct WavHeader {
49 pub riff_magic: [u8; 4],
51 pub file_size: u32,
53 pub wave_magic: [u8; 4],
55}
56
57#[derive(Debug, Clone, Copy)]
59pub struct FmtChunk {
60 pub chunk_id: [u8; 4],
62 pub chunk_size: u32,
64 pub audio_format: u16,
66 pub num_channels: u16,
68 pub sample_rate: u32,
70 pub byte_rate: u32,
72 pub block_align: u16,
74 pub bits_per_sample: u16,
76}
77
78#[derive(Debug, Clone)]
84pub struct WavFile {
85 pub num_channels: u16,
87 pub sample_rate: u32,
89 pub bits_per_sample: u16,
91 pub audio_format: u16,
93 pub data_offset: usize,
95 pub data_size: usize,
97 pub block_align: u16,
99}
100
101impl WavFile {
102 pub fn parse(data: &[u8]) -> Result<WavFile, KernelError> {
107 if data.len() < 44 {
109 return Err(KernelError::InvalidArgument {
110 name: "wav_data",
111 value: "file too small for WAV header",
112 });
113 }
114
115 if data[0..4] != RIFF_MAGIC {
117 return Err(KernelError::InvalidArgument {
118 name: "wav_data",
119 value: "missing RIFF magic",
120 });
121 }
122
123 if data[8..12] != WAVE_MAGIC {
124 return Err(KernelError::InvalidArgument {
125 name: "wav_data",
126 value: "missing WAVE magic",
127 });
128 }
129
130 let mut pos = 12;
132 let mut fmt_found = false;
133 let mut num_channels: u16 = 0;
134 let mut sample_rate: u32 = 0;
135 let mut bits_per_sample: u16 = 0;
136 let mut audio_format: u16 = 0;
137 let mut block_align: u16 = 0;
138 let mut data_offset: usize = 0;
139 let mut data_size: usize = 0;
140 let mut data_found = false;
141
142 while pos + 8 <= data.len() {
143 let chunk_id = [data[pos], data[pos + 1], data[pos + 2], data[pos + 3]];
144 let chunk_size = read_u32_le(&data[pos + 4..pos + 8]) as usize;
145
146 if chunk_id == FMT_CHUNK_ID {
147 if pos + 8 + 16 > data.len() {
148 return Err(KernelError::InvalidArgument {
149 name: "wav_data",
150 value: "fmt chunk truncated",
151 });
152 }
153
154 let fmt_data = &data[pos + 8..];
155 audio_format = read_u16_le(&fmt_data[0..2]);
156 num_channels = read_u16_le(&fmt_data[2..4]);
157 sample_rate = read_u32_le(&fmt_data[4..8]);
158 block_align = read_u16_le(&fmt_data[12..14]);
160 bits_per_sample = read_u16_le(&fmt_data[14..16]);
161
162 if audio_format != AUDIO_FORMAT_PCM {
163 return Err(KernelError::InvalidArgument {
164 name: "audio_format",
165 value: "only PCM format supported",
166 });
167 }
168
169 fmt_found = true;
170 } else if chunk_id == DATA_CHUNK_ID {
171 data_offset = pos + 8;
172 data_size = chunk_size.min(data.len() - data_offset);
173 data_found = true;
174 }
175
176 let padded_size = (chunk_size + 1) & !1;
178 pos += 8 + padded_size;
179 }
180
181 if !fmt_found {
182 return Err(KernelError::InvalidArgument {
183 name: "wav_data",
184 value: "missing fmt chunk",
185 });
186 }
187
188 if !data_found {
189 return Err(KernelError::InvalidArgument {
190 name: "wav_data",
191 value: "missing data chunk",
192 });
193 }
194
195 Ok(WavFile {
196 num_channels,
197 sample_rate,
198 bits_per_sample,
199 audio_format,
200 data_offset,
201 data_size,
202 block_align,
203 })
204 }
205
206 pub fn sample_data<'a>(&self, source: &'a [u8]) -> &'a [u8] {
208 let end = (self.data_offset + self.data_size).min(source.len());
209 &source[self.data_offset..end]
210 }
211
212 pub fn to_audio_config(&self) -> AudioConfig {
214 let format = match self.bits_per_sample {
215 8 => SampleFormat::U8,
216 16 => SampleFormat::S16Le,
217 24 => SampleFormat::S24Le,
218 32 => SampleFormat::S32Le,
219 _ => SampleFormat::S16Le, };
221
222 AudioConfig {
223 sample_rate: self.sample_rate,
224 channels: self.num_channels as u8,
225 format,
226 buffer_frames: 1024,
227 }
228 }
229
230 pub fn duration_ms(&self) -> u64 {
232 if self.sample_rate == 0 || self.block_align == 0 {
233 return 0;
234 }
235 let total_frames = self.data_size as u64 / self.block_align as u64;
236 (total_frames * 1000) / self.sample_rate as u64
237 }
238
239 pub fn total_samples(&self) -> usize {
241 if self.bits_per_sample == 0 {
242 return 0;
243 }
244 let bytes_per_sample = self.bits_per_sample as usize / 8;
245 if bytes_per_sample == 0 {
246 return 0;
247 }
248 self.data_size / bytes_per_sample
249 }
250}
251
252pub fn convert_u8_to_s16(data: &[u8]) -> Vec<i16> {
261 let mut output = Vec::with_capacity(data.len());
262 for &sample in data {
263 let signed = (sample as i16) - 128;
266 output.push(signed << 8); }
268 output
269}
270
271pub fn convert_s24_to_s16(data: &[u8]) -> Vec<i16> {
276 let num_samples = data.len() / 3;
277 let mut output = Vec::with_capacity(num_samples);
278
279 for i in 0..num_samples {
280 let offset = i * 3;
281 if offset + 2 >= data.len() {
282 break;
283 }
284
285 let low = data[offset] as i32;
288 let mid = data[offset + 1] as i32;
289 let high = data[offset + 2] as i32;
290 let sample_24 = low | (mid << 8) | (high << 16);
291
292 let signed = if sample_24 & 0x800000 != 0 {
294 sample_24 | !0xFFFFFF_u32 as i32
295 } else {
296 sample_24
297 };
298
299 output.push((signed >> 8) as i16);
301 }
302 output
303}
304
305pub fn convert_s32_to_s16(data: &[u8]) -> Vec<i16> {
309 let num_samples = data.len() / 4;
310 let mut output = Vec::with_capacity(num_samples);
311
312 for i in 0..num_samples {
313 let offset = i * 4;
314 if offset + 3 >= data.len() {
315 break;
316 }
317
318 let sample_32 = read_i32_le(&data[offset..offset + 4]);
319 output.push((sample_32 >> 16) as i16);
321 }
322 output
323}
324
325#[inline]
330fn read_u16_le(data: &[u8]) -> u16 {
331 u16::from_le_bytes([data[0], data[1]])
332}
333
334#[inline]
335fn read_u32_le(data: &[u8]) -> u32 {
336 u32::from_le_bytes([data[0], data[1], data[2], data[3]])
337}
338
339#[inline]
340fn read_i32_le(data: &[u8]) -> i32 {
341 i32::from_le_bytes([data[0], data[1], data[2], data[3]])
342}
343
344#[cfg(test)]
349mod tests {
350 use super::*;
351
352 fn build_test_wav(
354 channels: u16,
355 sample_rate: u32,
356 bits_per_sample: u16,
357 pcm_data: &[u8],
358 ) -> Vec<u8> {
359 let block_align = channels * (bits_per_sample / 8);
360 let byte_rate = sample_rate * block_align as u32;
361 let data_size = pcm_data.len() as u32;
362 let fmt_chunk_size: u32 = 16;
363 let file_size = 4 + (8 + fmt_chunk_size) + (8 + data_size);
364
365 let mut wav = Vec::new();
366
367 wav.extend_from_slice(b"RIFF");
369 wav.extend_from_slice(&file_size.to_le_bytes());
370 wav.extend_from_slice(b"WAVE");
371
372 wav.extend_from_slice(b"fmt ");
374 wav.extend_from_slice(&fmt_chunk_size.to_le_bytes());
375 wav.extend_from_slice(&1u16.to_le_bytes()); wav.extend_from_slice(&channels.to_le_bytes());
377 wav.extend_from_slice(&sample_rate.to_le_bytes());
378 wav.extend_from_slice(&byte_rate.to_le_bytes());
379 wav.extend_from_slice(&block_align.to_le_bytes());
380 wav.extend_from_slice(&bits_per_sample.to_le_bytes());
381
382 wav.extend_from_slice(b"data");
384 wav.extend_from_slice(&data_size.to_le_bytes());
385 wav.extend_from_slice(pcm_data);
386
387 wav
388 }
389
390 #[test]
391 fn test_parse_valid_wav() {
392 let pcm = [0u8; 100]; let wav_data = build_test_wav(1, 44100, 16, &pcm);
394
395 let wav = WavFile::parse(&wav_data).unwrap();
396 assert_eq!(wav.num_channels, 1);
397 assert_eq!(wav.sample_rate, 44100);
398 assert_eq!(wav.bits_per_sample, 16);
399 assert_eq!(wav.audio_format, 1);
400 assert_eq!(wav.data_size, 100);
401 }
402
403 #[test]
404 fn test_parse_stereo_wav() {
405 let pcm = [0u8; 200]; let wav_data = build_test_wav(2, 48000, 16, &pcm);
407
408 let wav = WavFile::parse(&wav_data).unwrap();
409 assert_eq!(wav.num_channels, 2);
410 assert_eq!(wav.sample_rate, 48000);
411 assert_eq!(wav.total_samples(), 100); }
413
414 #[test]
415 fn test_parse_invalid_magic() {
416 let mut wav_data = build_test_wav(1, 44100, 16, &[0u8; 10]);
417 wav_data[0] = b'X'; let result = WavFile::parse(&wav_data);
420 assert!(result.is_err());
421 }
422
423 #[test]
424 fn test_parse_invalid_wave_magic() {
425 let mut wav_data = build_test_wav(1, 44100, 16, &[0u8; 10]);
426 wav_data[8] = b'X'; let result = WavFile::parse(&wav_data);
429 assert!(result.is_err());
430 }
431
432 #[test]
433 fn test_parse_too_small() {
434 let data = [0u8; 20]; let result = WavFile::parse(&data);
436 assert!(result.is_err());
437 }
438
439 #[test]
440 fn test_sample_data() {
441 let pcm = [1u8, 2, 3, 4, 5, 6, 7, 8];
442 let wav_data = build_test_wav(1, 44100, 16, &pcm);
443 let wav = WavFile::parse(&wav_data).unwrap();
444 let samples = wav.sample_data(&wav_data);
445 assert_eq!(samples, &pcm);
446 }
447
448 #[test]
449 fn test_to_audio_config() {
450 let pcm = [0u8; 16];
451 let wav_data = build_test_wav(2, 48000, 16, &pcm);
452 let wav = WavFile::parse(&wav_data).unwrap();
453 let config = wav.to_audio_config();
454 assert_eq!(config.sample_rate, 48000);
455 assert_eq!(config.channels, 2);
456 assert_eq!(config.format, SampleFormat::S16Le);
457 }
458
459 #[test]
460 fn test_duration_ms() {
461 let pcm = [0u8; 88200];
464 let wav_data = build_test_wav(1, 44100, 16, &pcm);
465 let wav = WavFile::parse(&wav_data).unwrap();
466 assert_eq!(wav.duration_ms(), 1000);
467 }
468
469 #[test]
470 fn test_convert_u8_to_s16() {
471 let data = [128u8, 0, 255]; let result = convert_u8_to_s16(&data);
473 assert_eq!(result[0], 0); assert_eq!(result[1], -128 << 8); assert_eq!(result[2], 127 << 8); }
477
478 #[test]
479 fn test_convert_s24_to_s16() {
480 let data = [0xFF, 0x7F, 0x00]; let result = convert_s24_to_s16(&data);
483 assert_eq!(result[0], 0x7F); }
485
486 #[test]
487 fn test_total_samples() {
488 let pcm = [0u8; 40]; let wav_data = build_test_wav(2, 44100, 16, &pcm);
490 let wav = WavFile::parse(&wav_data).unwrap();
491 assert_eq!(wav.total_samples(), 20);
493 }
494}