1use byteorder::{ByteOrder, LittleEndian};
4use core::str;
5
6#[derive(Debug)]
9pub enum WavError {
10 WriteHeaderBufferTooSmall,
11 ReadHeaderInvalidHeaderLength,
12 ReadHeaderChunkIdNotRIFF,
13 ReadHeaderFormatNotWAVE,
14 ReadHeaderSubChunk1IdNotFmt,
15 ReadHeaderInvalidPcmHeaderLength,
16 ReadHeaderAudioFormatNotPcm,
17 ReadHeaderMissingDataSection,
18}
19
20pub const RIFF_HEADER_ONLY_LEN: usize = 8;
21pub const FULL_WAV_HEADER_LEN: usize = 44;
22
23const PCM_HEADER_LENGTH: u32 = 16;
24const AUDIO_FORMAT_PCM: u16 = 1;
25
26#[derive(Debug)]
27pub struct WavHeader {
28 pub num_channels: usize, pub sample_rate: usize, pub byte_rate: usize, pub block_align: usize, pub bits_per_sample: usize, pub data_size: usize, pub data_start_position: usize, pub data_with_header_size: usize, }
44
45pub fn write_header(header: &WavHeader, buf: &mut [u8]) -> Result<usize, WavError> {
46 if buf.len() < FULL_WAV_HEADER_LEN {
47 return Err(WavError::WriteHeaderBufferTooSmall);
48 }
49
50 buf[..4].copy_from_slice(b"RIFF"); LittleEndian::write_u32(&mut buf[4..8], header.data_with_header_size as u32); buf[8..12].copy_from_slice(b"WAVE"); buf[12..16].copy_from_slice(b"fmt "); LittleEndian::write_u32(&mut buf[16..20], PCM_HEADER_LENGTH); LittleEndian::write_u16(&mut buf[20..22], AUDIO_FORMAT_PCM); LittleEndian::write_u16(&mut buf[22..24], header.num_channels as u16);
57 LittleEndian::write_u32(&mut buf[24..28], header.sample_rate as u32);
58 LittleEndian::write_u32(&mut buf[28..32], header.byte_rate as u32);
59 LittleEndian::write_u16(&mut buf[32..34], header.block_align as u16);
60 LittleEndian::write_u16(&mut buf[34..36], header.bits_per_sample as u16);
61 buf[36..40].copy_from_slice(b"data"); LittleEndian::write_u32(&mut buf[40..44], header.data_size as u32);
63
64 Ok(FULL_WAV_HEADER_LEN)
65}
66
67pub fn read_header(buf: &[u8]) -> Result<WavHeader, WavError> {
68 if buf.len() < FULL_WAV_HEADER_LEN {
69 return Err(WavError::ReadHeaderInvalidHeaderLength);
70 }
71
72 if str::from_utf8(&buf[..4]).map_err(|_| WavError::ReadHeaderChunkIdNotRIFF)? != "RIFF" {
73 return Err(WavError::ReadHeaderChunkIdNotRIFF);
74 }
75
76 if str::from_utf8(&buf[8..12]).map_err(|_| WavError::ReadHeaderFormatNotWAVE)? != "WAVE" {
77 return Err(WavError::ReadHeaderFormatNotWAVE);
78 }
79
80 if str::from_utf8(&buf[12..16]).map_err(|_| WavError::ReadHeaderSubChunk1IdNotFmt)? != "fmt " {
81 return Err(WavError::ReadHeaderSubChunk1IdNotFmt);
82 }
83
84 if LittleEndian::read_u32(&buf[16..20]) != 16 {
85 return Err(WavError::ReadHeaderInvalidPcmHeaderLength);
86 }
87
88 if LittleEndian::read_u16(&buf[20..22]) != 1 {
89 return Err(WavError::ReadHeaderAudioFormatNotPcm);
90 }
91
92 let data_with_header_size = LittleEndian::read_u32(&buf[4..8]) as usize;
93 let num_channels = LittleEndian::read_u16(&buf[22..24]) as usize;
94 let sample_rate = LittleEndian::read_u32(&buf[24..28]) as usize;
95 let byte_rate = LittleEndian::read_u32(&buf[28..32]) as usize;
96 let block_align = LittleEndian::read_u16(&buf[32..34]) as usize;
97 let bits_per_sample = LittleEndian::read_u16(&buf[34..36]) as usize;
98
99 let section = str::from_utf8(&buf[36..40]).map_err(|_| WavError::ReadHeaderMissingDataSection)?;
100 let (data_size, data_start_position) = match section {
101 "data" => (LittleEndian::read_u32(&buf[40..44]) as usize, FULL_WAV_HEADER_LEN),
102 "LIST" => {
103 let size = LittleEndian::read_u32(&buf[40..44]) as usize;
104 LittleEndian::read_u32(&buf[40..44]); (size, FULL_WAV_HEADER_LEN + 4)
106 }
107 _section => {
108 return Err(WavError::ReadHeaderMissingDataSection);
110 }
111 };
112
113 Ok(WavHeader {
114 data_with_header_size,
115 num_channels,
116 sample_rate,
117 byte_rate,
118 block_align,
119 bits_per_sample,
120 data_size,
121 data_start_position,
122 })
123}
124
125#[cfg(test)]
126mod tests {
127 extern crate std;
128 use super::*;
129
130 #[test]
131 fn can_read_pcm_wav_header() {
132 let buffer = [
133 0x52, 0x49, 0x46, 0x46, 0x16, 0x29, 0x0B, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74, 0x20, 0x10, 0x00,
134 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x44, 0xAC, 0x00, 0x00, 0x10, 0xB1, 0x02, 0x00, 0x04, 0x00, 0x10, 0x00,
135 0x64, 0x61, 0x74, 0x61, 0x70, 0x28, 0x0B, 0x00, 0x00,
136 ];
137
138 let header = read_header(&buffer).unwrap();
139
140 assert_eq!(header.num_channels, 2);
141 assert_eq!(header.sample_rate, 44100);
142 assert_eq!(header.byte_rate, 176400);
143 assert_eq!(header.block_align, 4);
144 assert_eq!(header.bits_per_sample, 16);
145 assert_eq!(header.data_size, 731248);
146 assert_eq!(header.data_start_position, 44);
147 }
148}