#![no_std]
extern crate alloc;
use alloc::vec;
use alloc::vec::Vec;
use core::array::TryFromSliceError;
use core::convert::TryInto;
#[derive(Debug, Clone)]
pub enum Error {
UnknownChunkID([u8; 4]),
CantParseSamples(TryFromSliceError),
CantReadChunkID,
CantReadChunkSize,
CantReadNumChannels,
CantReadSampleRate,
CantReadBitDepth,
NoRiffChunkFound,
NoDataChunkFound,
NoFmtChunkFound,
UnsupportedBitDepth,
}
#[derive(Debug, PartialEq)]
enum ChunkID {
RIFF,
FMT,
LIST,
DATA,
JUNK,
}
#[derive(Debug, PartialEq)]
pub enum Samples {
BitDepth8(Vec<u8>),
BitDepth16(Vec<i16>),
BitDepth24(Vec<i32>),
}
#[derive(Debug, Clone)]
pub struct WaveFormat {
pub sample_rate: u32,
pub num_channels: u16,
pub bit_depth: u16,
}
#[derive(Debug)]
pub struct Wave {
pub format: WaveFormat,
pub data: Samples,
}
impl Wave {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
let riff = bytes[0..4]
.try_into()
.map_err(|_| Error::CantReadChunkID)
.and_then(|b| parse_chunk_id(b))?;
let file_size = bytes[4..8]
.try_into()
.map_err(|_| Error::CantReadChunkSize)
.map(|b| u32::from_le_bytes(b))?;
if riff != ChunkID::RIFF {
return Err(Error::NoRiffChunkFound);
}
parse_chunks(&bytes[12..file_size as usize + 8])
}
}
fn parse_chunk_id(id: [u8; 4]) -> Result<ChunkID, Error> {
match id {
[b'R', b'I', b'F', b'F'] => Ok(ChunkID::RIFF),
[b'f', b'm', b't', b' '] => Ok(ChunkID::FMT),
[b'L', b'I', b'S', b'T'] => Ok(ChunkID::LIST),
[b'd', b'a', b't', b'a'] => Ok(ChunkID::DATA),
[b'J', b'U', b'N', b'K'] => Ok(ChunkID::JUNK),
_ => Err(Error::UnknownChunkID(id)),
}
}
fn parse_fmt_chunk(bytes: &[u8]) -> Result<WaveFormat, Error> {
let num_channels = bytes[2..4]
.try_into()
.map_err(|_| Error::CantReadNumChannels)
.map(|b| u16::from_le_bytes(b))?;
let sample_rate = bytes[4..8]
.try_into()
.map_err(|_| Error::CantReadSampleRate)
.map(|b| u32::from_le_bytes(b))?;
let bit_depth = bytes[14..16]
.try_into()
.map_err(|_| Error::CantReadBitDepth)
.map(|b| u16::from_le_bytes(b))?;
Ok(WaveFormat {
num_channels,
sample_rate,
bit_depth,
})
}
fn read_samples<T, F: Fn(&[u8]) -> Result<T, TryFromSliceError>>(
format: &WaveFormat,
bytes: &[u8],
read_sample: F,
) -> Result<Vec<T>, TryFromSliceError> {
let num_bytes = (format.bit_depth / 8) as usize;
let mut pos = 0;
let mut samples = vec![];
loop {
if pos + num_bytes > bytes.len() {
break;
}
samples.push(read_sample(&bytes[pos..pos + num_bytes])?);
pos += num_bytes;
}
Ok(samples)
}
fn parse_data_chunk(format: &WaveFormat, bytes: &[u8]) -> Result<Samples, Error> {
match format.bit_depth {
8 => {
let samples = read_samples(&format, bytes, |b| {
b.try_into().map(|b| u8::from_le_bytes(b))
});
samples
.map_err(|e| Error::CantParseSamples(e))
.map(|s| Samples::BitDepth8(s))
}
16 => {
let samples = read_samples(&format, bytes, |b| {
b.try_into().map(|b| i16::from_le_bytes(b))
});
samples
.map_err(|e| Error::CantParseSamples(e))
.map(|s| Samples::BitDepth16(s))
}
24 => {
let samples = read_samples(&format, bytes, |b| {
[0, b[0], b[1], b[2]][0..4]
.try_into()
.map(|b| i32::from_le_bytes(b))
});
samples
.map_err(|e| Error::CantParseSamples(e))
.map(|s| Samples::BitDepth24(s))
}
_ => Err(Error::UnsupportedBitDepth),
}
}
fn parse_chunks(bytes: &[u8]) -> Result<Wave, Error> {
let mut pos = 0;
let mut format = Err(Error::NoFmtChunkFound);
let mut data = Err(Error::NoDataChunkFound);
loop {
if pos + 8 > bytes.len() {
break;
}
let chunk_id = bytes[pos..pos + 4]
.try_into()
.map_err(|_| Error::CantReadChunkID)
.and_then(|b| parse_chunk_id(b))?;
let chunk_size = bytes[pos + 4..pos + 8]
.try_into()
.map_err(|_| Error::CantReadChunkSize)
.map(|b| u32::from_le_bytes(b))?;
let start = pos + 8;
let end = pos + 8 + chunk_size as usize;
match chunk_id {
ChunkID::FMT => format = parse_fmt_chunk(&bytes[start..end]),
ChunkID::DATA => data = parse_data_chunk(&format.clone()?, &bytes[start..end]),
_ => (),
};
pos += chunk_size as usize + 8;
}
let wave = Wave {
format: format?,
data: data?,
};
Ok(wave)
}
#[cfg(test)]
mod tests {
#![allow(overflowing_literals)]
use super::*;
#[test]
fn test_parse_wave_16_bit_stereo() {
let bytes: [u8; 60] = [
0x52, 0x49, 0x46, 0x46,
0x34, 0x00, 0x00, 0x00,
0x57, 0x41, 0x56, 0x45,
0x66, 0x6d, 0x74, 0x20,
0x10, 0x00, 0x00, 0x00,
0x01, 0x00,
0x02, 0x00,
0x22, 0x56, 0x00, 0x00,
0x88, 0x58, 0x01, 0x00,
0x04, 0x00,
0x10, 0x00,
0x64, 0x61, 0x74, 0x61,
0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x24, 0x17, 0x1e, 0xf3,
0x3c, 0x13, 0x3c, 0x14,
0x16, 0xf9, 0x18, 0xf9,
];
let wave = Wave::from_bytes(&bytes).unwrap();
assert_eq!(wave.format.sample_rate, 22050);
assert_eq!(wave.format.bit_depth, 16);
assert_eq!(wave.format.num_channels, 2);
assert_eq!(
wave.data,
Samples::BitDepth16(vec![
0x0000, 0x0000,
0x1724, 0xf31e,
0x133c, 0x143c,
0xf916, 0xf918,
])
);
}
#[test]
fn test_parse_wave_24_bit_mono() {
let bytes: [u8; 56] = [
0x52, 0x49, 0x46, 0x46,
0x30, 0x00, 0x00, 0x00,
0x57, 0x41, 0x56, 0x45,
0x66, 0x6d, 0x74, 0x20,
0x10, 0x00, 0x00, 0x00,
0x01, 0x00,
0x01, 0x00,
0x44, 0xac, 0x00, 0x00,
0x88, 0x58, 0x01, 0x00,
0x04, 0x00,
0x18, 0x00,
0x64, 0x61, 0x74, 0x61,
0x0c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
0x00, 0x24, 0x17,
0x1e, 0xf3, 0x3c,
0x13, 0x3c, 0x14,
];
let wave = Wave::from_bytes(&bytes).unwrap();
assert_eq!(wave.format.sample_rate, 44100);
assert_eq!(wave.format.bit_depth, 24);
assert_eq!(wave.format.num_channels, 1);
assert_eq!(
wave.data,
Samples::BitDepth24(vec![
0x00000000,
0x17240000,
0x3cf31e00,
0x143c1300,
])
);
}
}