1#![warn(missing_docs)]
24#![warn(clippy::all)]
25#![warn(clippy::pedantic)]
26
27#![deprecated = "This project is no longer maintained, use the `hound` crate"]
28
29use std::{
30 convert::TryFrom,
31 io::{self, Read, Write},
32};
33
34pub mod header;
35pub use header::{Header, WAV_FORMAT_IEEE_FLOAT, WAV_FORMAT_PCM};
36
37pub mod bit_depth;
38pub use bit_depth::BitDepth;
39
40mod tuple_iterator;
41use tuple_iterator::{PairIter, QuadrupletIter, TripletIter};
42
43#[allow(clippy::similar_names)]
55pub fn read<R>(reader: &mut R) -> io::Result<(Header, BitDepth)>
56where
57 R: Read + io::Seek,
58{
59 let header = read_header(reader)?;
60 Ok((header, read_data(reader, &header)?))
61}
62
63pub fn write<W>(header: Header, track: &BitDepth, writer: &mut W) -> std::io::Result<()>
77where
78 W: Write + io::Seek,
79{
80 const WAVE_ID: riff::ChunkId = riff::ChunkId {
81 value: [b'W', b'A', b'V', b'E'],
82 };
83 const HEADER_ID: riff::ChunkId = riff::ChunkId {
84 value: [b'f', b'm', b't', b' '],
85 };
86 const DATA_ID: riff::ChunkId = riff::ChunkId {
87 value: [b'd', b'a', b't', b'a'],
88 };
89
90 let h_vec: [u8; 16] = header.into();
91 let h_dat = riff::ChunkContents::Data(HEADER_ID, Vec::from(h_vec));
92
93 let d_vec = match track {
94 BitDepth::Eight(v) => v.clone(),
95 BitDepth::Sixteen(v) => v
96 .iter()
97 .flat_map(|s| {
98 let v = s.to_le_bytes();
99 PairIter::new((v[0], v[1]))
100 })
101 .collect::<Vec<_>>(),
102 BitDepth::TwentyFour(v) => v
103 .iter()
104 .flat_map(|s| {
105 let v = s.to_le_bytes().split_at(1).1.to_owned();
106 TripletIter::new((v[0], v[1], v[2]))
107 })
108 .collect::<Vec<_>>(),
109 BitDepth::ThirtyTwoFloat(v) => v
110 .iter()
111 .flat_map(|s| {
112 let v = s.to_le_bytes().to_owned();
113 QuadrupletIter::new((v[0], v[1], v[2], v[3]))
114 })
115 .collect::<Vec<_>>(),
116 _ => {
117 return Err(std::io::Error::new(
118 std::io::ErrorKind::Other,
119 "Empty audio data given",
120 ))
121 }
122 };
123 let d_dat = riff::ChunkContents::Data(DATA_ID, d_vec);
124
125 let r = riff::ChunkContents::Children(riff::RIFF_ID.clone(), WAVE_ID, vec![h_dat, d_dat]);
126
127 r.write(writer)?;
128
129 Ok(())
130}
131
132#[allow(clippy::similar_names)]
133fn read_header<R>(reader: &mut R) -> io::Result<Header>
134where
135 R: Read + io::Seek,
136{
137 let wav = verify_wav_file(reader)?;
138
139 for c in wav.iter(reader) {
140 if c.id().as_str() == "fmt " {
141 let header_bytes = c.read_contents(reader)?;
143 let header = Header::try_from(header_bytes.as_slice())
144 .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
145
146 match header.audio_format {
148 WAV_FORMAT_PCM | WAV_FORMAT_IEEE_FLOAT => return Ok(header),
149 _ => {
150 return Err(io::Error::new(
151 io::ErrorKind::Other,
152 "Unsupported data format, data is not in uncompressed PCM format, aborting",
153 ))
154 }
155 };
156 }
157 }
158
159 Err(io::Error::new(
160 io::ErrorKind::InvalidData,
161 "RIFF data is missing the \"fmt \" chunk, aborting",
162 ))
163}
164
165#[allow(clippy::similar_names)]
166fn read_data<R>(reader: &mut R, header: &Header) -> io::Result<BitDepth>
167where
168 R: Read + io::Seek,
169{
170 let wav = verify_wav_file(reader)?;
171
172 for c in wav.iter(reader) {
173 if c.id().as_str() == "data" {
174 let data_bytes = c.read_contents(reader)?;
176
177 let wav_data = match header.audio_format {
178 WAV_FORMAT_PCM => match header.bits_per_sample {
179 8 => Ok(BitDepth::Eight(data_bytes)),
180 16 => Ok(BitDepth::Sixteen({
181 let mut tmpv = Vec::with_capacity(data_bytes.len() / 2);
182 tmpv.extend(
183 data_bytes
184 .chunks_exact(2)
185 .map(|i| i16::from_le_bytes([i[0], i[1]])),
186 );
187 tmpv
188 })),
189 24 => Ok(BitDepth::TwentyFour({
190 let mut tmpv = Vec::with_capacity(data_bytes.len() / 3);
191 tmpv.extend(
192 data_bytes
193 .chunks_exact(3)
194 .map(|i| i32::from_le_bytes([0, i[0], i[1], i[2]])),
195 );
196 tmpv
197 })),
198 _ => Err(io::Error::new(
199 io::ErrorKind::Other,
200 "Unsupported PCM bit depth",
201 )),
202 },
203 WAV_FORMAT_IEEE_FLOAT => match header.bits_per_sample {
204 32 => Ok(BitDepth::ThirtyTwoFloat({
205 let mut tmpv = Vec::with_capacity(data_bytes.len() / 4);
206 tmpv.extend(
207 data_bytes
208 .chunks_exact(4)
209 .map(|f| f32::from_le_bytes([f[0], f[1], f[2], f[3]])),
210 );
211 tmpv
212 })),
213 _ => Err(io::Error::new(
214 io::ErrorKind::Other,
215 "Unsupported IEEE Float bit depth",
216 )),
217 },
218 _ => Err(io::Error::new(
219 io::ErrorKind::Other,
220 "Unsupported WAV format",
221 )),
222 };
223
224 return wav_data;
225 }
226 }
227
228 Err(io::Error::new(
229 io::ErrorKind::Other,
230 "Could not parse audio data",
231 ))
232}
233
234fn verify_wav_file<R>(reader: &mut R) -> io::Result<riff::Chunk>
235where
236 R: Read + io::Seek,
237{
238 let wav = riff::Chunk::read(reader, 0)?;
239
240 let form_type = wav.read_type(reader)?;
241
242 if form_type.as_str() == "WAVE" {
243 Ok(wav)
244 } else {
245 Err(io::Error::new(
246 io::ErrorKind::Other,
247 "RIFF file type not \"WAVE\"",
248 ))
249 }
250}