1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
use crate::parsing::{parse_chunks, Chunk, ChunkID};
use alloc::vec;
use alloc::vec::Vec;
use core::array::TryFromSliceError;
use core::convert::TryInto;

/// Error type for different parsing failures
#[derive(Debug, Clone)]
pub enum Error {
    /// Unknown or unsupported Chunk ID
    UnknownChunkID([u8; 4]),
    /// Failed parsing slice into specific bytes
    CantParseSliceInto(TryFromSliceError),
    /// Failed parsing chunk with given id
    CantParseChunk(ChunkID),
    /// no data chunk found in file
    NoRiffChunkFound,
    /// no data chunk found in file
    NoDataChunkFound,
    /// no fmt/header chunk found in file
    NoFmtChunkFound,
    /// unsupported bit depth
    UnsupportedBitDepth(u16),
}

/// Struct representing the header section of a .wav file
///
/// for more information see [`here`]
///
/// [`here`]: http://soundfile.sapp.org/doc/WaveFormat/
#[derive(Debug, Clone)]
pub struct Header {
    /// sample rate, typical values are `44_100`, `48_000` or `96_000`
    pub sample_rate: u32,
    /// number of audio channels in the sample data, channels are interleaved
    pub num_channels: u16,
    /// bit depth for each sample, typical values are `16` or `24`
    pub bit_depth: u16,
}

impl Header {
    /// Create new [`Header`] instance from a slice of bytes
    ///
    /// # Examples
    ///
    /// ```
    /// use wavv::Header;
    ///
    /// fn main() {
    ///     let bytes = [
    ///         0x01, 0x00, // audio format
    ///         0x01, 0x00, // num channels
    ///         0x44, 0xac, 0x00, 0x00, // sample rate
    ///         0x88, 0x58, 0x01, 0x00, // byte rate
    ///         0x04, 0x00, // block align
    ///         0x18, 0x00, // bits per sample
    ///     ];
    ///
    ///     let header = Header::from_bytes(&bytes).unwrap();
    ///
    ///     assert_eq!(header.num_channels, 1);
    ///     assert_eq!(header.bit_depth, 24);
    ///     assert_eq!(header.sample_rate, 44_100);
    /// }
    /// ```
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
        let num_channels = bytes[2..4]
            .try_into()
            .map_err(|e| Error::CantParseSliceInto(e))
            .map(|b| u16::from_le_bytes(b))?;

        let sample_rate = bytes[4..8]
            .try_into()
            .map_err(|e| Error::CantParseSliceInto(e))
            .map(|b| u32::from_le_bytes(b))?;

        let bit_depth = bytes[14..16]
            .try_into()
            .map_err(|e| Error::CantParseSliceInto(e))
            .map(|b| u16::from_le_bytes(b))?;

        Ok(Header {
            num_channels,
            sample_rate,
            bit_depth,
        })
    }
}

/// Enum to hold samples for different bit depth
#[derive(Debug, PartialEq)]
pub enum Samples {
    /// 8 bit audio
    BitDepth8(Vec<u8>),
    /// 16 bit audio
    BitDepth16(Vec<i16>),
    /// 24 bit audio
    BitDepth24(Vec<i32>),
}

impl Samples {
    /// Create new [`Samples`] instance from a slice of bytes
    /// this requires a [`Header`] instance to be passed to determine
    /// the sample size and channel data etc.
    ///
    /// # Examples
    ///
    /// ```
    /// use wavv::{Header, Samples};
    ///
    /// fn main() {
    ///     let bytes = [
    ///         0x01, 0x00, // audio format
    ///         0x01, 0x00, // num channels
    ///         0x44, 0xac, 0x00, 0x00, // sample rate
    ///         0x88, 0x58, 0x01, 0x00, // byte rate
    ///         0x04, 0x00, // block align
    ///         0x18, 0x00, // bits per sample
    ///     ];
    ///
    ///     let header = Header::from_bytes(&bytes).unwrap();
    ///
    ///     assert_eq!(header.num_channels, 1);
    ///     assert_eq!(header.bit_depth, 24);
    ///     assert_eq!(header.sample_rate, 44_100);
    ///
    ///     let bytes = [
    ///         0x00, 0x00, 0x00, // sample 1
    ///         0x00, 0x24, 0x17, // sample 2
    ///         0x1e, 0xf3, 0x3c, // sample 3
    ///         0x13, 0x3c, 0x14, // sample 4
    ///     ];
    ///
    ///     let samples = Samples::from_bytes(&header, &bytes).unwrap();
    ///
    ///     assert_eq!(
    ///         samples,
    ///         Samples::BitDepth24(vec![
    ///     	    0x00000000, // sample 1
    ///     	    0x17240000, // sample 2
    ///     	    0x3cf31e00, // sample 3
    ///     	    0x143c1300, // sample 4
    ///         ])
    ///     )
    /// }
    /// ```
    pub fn from_bytes(header: &Header, bytes: &[u8]) -> Result<Self, Error> {
        let mut samples = match header.bit_depth {
            8 => Ok(Samples::BitDepth8(vec![])),
            16 => Ok(Samples::BitDepth16(vec![])),
            24 => Ok(Samples::BitDepth24(vec![])),
            _ => Err(Error::UnsupportedBitDepth(header.bit_depth)),
        }?;

        let num_bytes = (header.bit_depth / 8) as usize;
        let mut pos = 0;

        loop {
            if pos + num_bytes > bytes.len() {
                break;
            }
            let slice = &bytes[pos..pos + num_bytes];

            match samples {
                Samples::BitDepth8(ref mut v) => {
                    let sample = slice
                        .try_into()
                        .map_err(|e| Error::CantParseSliceInto(e))
                        .map(u8::from_le_bytes)?;

                    v.push(sample)
                }
                Samples::BitDepth16(ref mut v) => {
                    let sample = slice
                        .try_into()
                        .map_err(|e| Error::CantParseSliceInto(e))
                        .map(i16::from_le_bytes)?;

                    v.push(sample)
                }
                Samples::BitDepth24(ref mut v) => {
                    let sample = [0, slice[0], slice[1], slice[2]][0..4]
                        .try_into()
                        .map_err(|e| Error::CantParseSliceInto(e))
                        .map(i32::from_le_bytes)?;

                    v.push(sample)
                }
            }

            pos += num_bytes;
        }

        Ok(samples)
    }
}

/// Struct representing a .wav file
#[derive(Debug)]
pub struct Wave {
    /// Contains data from the fmt chunk / header part of the file
    pub header: Header,
    /// Contains audio data as samples of a fixed bit depth
    pub data: Samples,
    /// Contains raw chunk data that is either unimplemented or unknown
    pub unknown_chunks: Option<Vec<Chunk>>,
}

impl Wave {
    /// Create new [`Wave`] instance from a slice of bytes
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs;
    /// use std::path::Path;
    /// use wavv::Wave;
    ///
    /// fn main() {
    ///     let bytes = fs::read(Path::new("./test_files/sine_mono_16_44100.wav")).unwrap();
    ///     let wave = Wave::from_bytes(&bytes).unwrap();
    ///
    ///     assert_eq!(wave.header.num_channels, 1);
    ///     assert_eq!(wave.header.bit_depth, 16);
    ///     assert_eq!(wave.header.sample_rate, 44_100);
    /// }
    /// ```
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
        let (chunks, unknown_chunks) = parse_chunks(bytes)?;

        let mut data = Err(Error::NoDataChunkFound);
        let mut header = Err(Error::NoFmtChunkFound);

        for chunk in chunks {
            match chunk {
                Chunk::FMT(h) => header = Ok(h),
                Chunk::DATA(d) => data = Ok(d),
                // ignore unknown chunks
                Chunk::Unknown(_, _) => (),
            }
        }

        let wave = Wave {
            data: data?,
            header: header?,
            unknown_chunks,
        };

        Ok(wave)
    }
}

#[cfg(test)]
mod tests {
    #![allow(overflowing_literals)]
    use super::*;
    use alloc::vec;

    #[test]
    fn test_parse_wave_16_bit_stereo() {
        let bytes: [u8; 60] = [
            0x52, 0x49, 0x46, 0x46, // RIFF
            0x34, 0x00, 0x00, 0x00, // chunk size
            0x57, 0x41, 0x56, 0x45, // WAVE
            0x66, 0x6d, 0x74, 0x20, // fmt_
            0x10, 0x00, 0x00, 0x00, // chunk size
            0x01, 0x00, // audio format
            0x02, 0x00, // num channels
            0x22, 0x56, 0x00, 0x00, // sample rate
            0x88, 0x58, 0x01, 0x00, // byte rate
            0x04, 0x00, // block align
            0x10, 0x00, // bits per sample
            0x64, 0x61, 0x74, 0x61, // data
            0x10, 0x00, 0x00, 0x00, // chunk size
            0x00, 0x00, 0x00, 0x00, // sample 1 L+R
            0x24, 0x17, 0x1e, 0xf3, // sample 2 L+R
            0x3c, 0x13, 0x3c, 0x14, // sample 3 L+R
            0x16, 0xf9, 0x18, 0xf9, // sample 4 L+R
        ];

        let wave = Wave::from_bytes(&bytes).unwrap();

        assert_eq!(wave.header.sample_rate, 22050);
        assert_eq!(wave.header.bit_depth, 16);
        assert_eq!(wave.header.num_channels, 2);

        assert_eq!(
            wave.data,
            Samples::BitDepth16(vec![
                0x0000, 0x0000, // sample 1 L+R
                0x1724, 0xf31e, // sample 2 L+R
                0x133c, 0x143c, // sample 3 L+R
                0xf916, 0xf918, // sample 4 L+R
            ])
        );
    }

    #[test]
    fn test_parse_wave_24_bit_mono() {
        let bytes: [u8; 56] = [
            0x52, 0x49, 0x46, 0x46, // RIFF
            0x30, 0x00, 0x00, 0x00, // chunk size
            0x57, 0x41, 0x56, 0x45, // WAVE
            0x66, 0x6d, 0x74, 0x20, // fmt_
            0x10, 0x00, 0x00, 0x00, // chunk size
            0x01, 0x00, // audio format
            0x01, 0x00, // num channels
            0x44, 0xac, 0x00, 0x00, // sample rate
            0x88, 0x58, 0x01, 0x00, // byte rate
            0x04, 0x00, // block align
            0x18, 0x00, // bits per sample
            0x64, 0x61, 0x74, 0x61, // data
            0x0c, 0x00, 0x00, 0x00, // chunk size
            0x00, 0x00, 0x00, // sample 1
            0x00, 0x24, 0x17, // sample 2
            0x1e, 0xf3, 0x3c, // sample 3
            0x13, 0x3c, 0x14, // sample 4
        ];

        let wave = Wave::from_bytes(&bytes).unwrap();

        assert_eq!(wave.header.sample_rate, 44100);
        assert_eq!(wave.header.bit_depth, 24);
        assert_eq!(wave.header.num_channels, 1);

        assert_eq!(
            wave.data,
            Samples::BitDepth24(vec![
                0x00000000, // sample 1
                0x17240000, // sample 2
                0x3cf31e00, // sample 3
                0x143c1300, // sample 4
            ])
        );
    }
}