wav/
lib.rs

1//! This is a crate for reading in and writing out wave files. It supports uncompressed PCM bit
2//! depths of 8, 16, 24 bits, and 32bit IEEE Float formats, both with any number of channels.
3//! Unfortunately other types of data format (e.g. compressed WAVE files) are not supported. There
4//! is also no support for any metadata chunks or any chunks other than the `"fmt "` and `"data"`
5//! chunks.
6//!
7//! ## Example
8//!
9//! ```rust
10//! # fn main() -> std::io::Result<()> {
11//! use std::fs::File;
12//! use std::path::Path;
13//!
14//! let mut inp_file = File::open(Path::new("data/sine.wav"))?;
15//! let (header, data) = wav::read(&mut inp_file)?;
16//!
17//! let mut out_file = File::create(Path::new("data/output.wav"))?;
18//! wav::write(header, &data, &mut out_file)?;
19//! # Ok(())
20//! # }
21//! ```
22
23#![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/// Reads in the given `reader` and attempts to extract the audio data and header from it.
44///
45/// ## Errors
46///
47/// This function fails under the following circumstances:
48///
49/// * Any error occurring from the `reader` parameter during reading.
50/// * The data isn't RIFF data.
51/// * The wave header specifies a compressed data format.
52/// * The wave header specifies an unsupported bit-depth.
53/// * The wave data is malformed, or otherwise couldn't be parsed into samples.
54#[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
63/// Writes the given wav data to the given `writer`.
64///
65/// ## Notes
66///
67/// Although `track` is a borrowed value, its contents will be formatted into an owned `Vec<u8>` so
68/// that it can be written to the `writer` through [`riff::ChunkContents::write`].
69///
70/// ## Errors
71///
72/// This function fails under the following circumstances:
73///
74/// * Any error occurring from the `writer` parameter during writing.
75/// * The given [`BitDepth`] is [`BitDepth::Empty`].
76pub 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            // Read header contents
142            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            // Return error if not using PCM
147            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            // Read data contents
175            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}