Skip to content

Instantly share code, notes, and snippets.

@rjvdw
Last active March 10, 2019 13:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rjvdw/91e2952ed1c6711d891bb547d632f819 to your computer and use it in GitHub Desktop.
Save rjvdw/91e2952ed1c6711d891bb547d632f819 to your computer and use it in GitHub Desktop.
Using Rust to process wav file
use std::str;
pub fn read_string_from_buffer<'a>(header: &'a [u8], from: usize, length: usize) -> &'a str {
let to = from + length;
str::from_utf8(&header[from .. to]).unwrap()
}
pub fn read_u16_from_buffer(header: &[u8], from: usize) -> u16 {
let to = from + 2;
let slice = &header[from .. to];
slice.iter().rev().fold(0, |acc, &b| (acc << 8) + b as u16)
}
pub fn read_u32_from_buffer(header: &[u8], from: usize) -> u32 {
let to = from + 4;
let slice = &header[from .. to];
slice.iter().rev().fold(0, |acc, &b| (acc << 8) + b as u32)
}
use std::fmt;
use crate::buffer_utils::{read_string_from_buffer, read_u16_from_buffer, read_u32_from_buffer};
pub struct Header<'a> {
pub chunk_id: &'a str,
pub chunk_size: u32,
pub format: &'a str,
pub subchunk1_id: &'a str,
pub subchunk1_size: u32,
pub audio_format: u16,
pub number_of_channels: u16,
pub sample_rate: u32,
pub byte_rate: u32,
pub block_align: u16,
pub bits_per_sample: u16,
pub subchunk2_id: &'a str,
pub subchunk2_size: u32,
}
impl<'a> Header<'a> {
pub fn parse_from(header_buf: &'a [u8]) -> Result<Header, &'static str> {
let header = Header {
chunk_id: read_string_from_buffer(&header_buf, 0, 4),
chunk_size: read_u32_from_buffer(&header_buf, 4),
format: read_string_from_buffer(&header_buf, 8, 4),
subchunk1_id: read_string_from_buffer(&header_buf, 12, 4),
subchunk1_size: read_u32_from_buffer(&header_buf, 16),
audio_format: read_u16_from_buffer(&header_buf, 20),
number_of_channels: read_u16_from_buffer(&header_buf, 22),
sample_rate: read_u32_from_buffer(&header_buf, 24),
byte_rate: read_u32_from_buffer(&header_buf, 28),
block_align: read_u16_from_buffer(&header_buf, 32),
bits_per_sample: read_u16_from_buffer(&header_buf, 34),
subchunk2_id: read_string_from_buffer(&header_buf, 36, 4),
subchunk2_size: read_u32_from_buffer(&header_buf, 40),
};
assert!(header.chunk_id == "RIFF");
assert!(header.format == "WAVE");
assert!(header.subchunk1_id == "fmt ");
assert!(header.subchunk1_size == 16);
assert!(header.audio_format == 1);
assert!(header.byte_rate == header.sample_rate * header.block_align as u32);
assert!(header.block_align == header.number_of_channels * header.bits_per_sample / 8);
assert!(header.subchunk2_id == "data");
assert!(header.subchunk2_size == header.chunk_size - (header_buf.len() as u32 - 8));
assert!(header.bits_per_sample == 16, "For now, assume 16 bits per sample");
Ok(header)
}
}
impl<'a> fmt::Display for Header<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} file of {} bytes (channels: {}, sample rate: {}, bits per sample: {})",
self.format,
self.chunk_size + 8,
self.number_of_channels,
self.sample_rate,
self.bits_per_sample
)
}
}
mod buffer_utils;
mod header;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::str;
use buffer_utils::read_u16_from_buffer;
use header::Header;
fn main() -> io::Result<()> {
process("sample.wav")
}
fn process(file_name: &'static str) -> io::Result<()> {
let mut file = File::open(file_name)?;
let mut header_buf = [0u8; 44];
let bytes_read = file.read(&mut header_buf)?;
if bytes_read != header_buf.len() {
return parse_error("WAV file does not contain a valid header");
}
let header = parse_header(&header_buf)?;
println!("{}", header);
let mut buffer = [0u8; 4096];
let mut channel: u16 = 0;
while parse_buffer(&mut file, &mut buffer, &mut channel, &header)? {}
Ok(())
}
fn parse_header<'a>(header_buf: &'a [u8]) -> io::Result<Header<'a>> {
match Header::parse_from(&header_buf) {
Ok(header) => Ok(header),
Err(err) => parse_error(err)
}
}
fn parse_error<T>(msg: &str) -> io::Result<T> {
Err(io::Error::new(io::ErrorKind::InvalidData, msg))
}
fn parse_buffer(file: &mut File, buffer: &mut [u8], channel: &mut u16, header: &Header) -> io::Result<bool> {
let bytes_per_sample = (header.bits_per_sample/8) as usize;
let bytes_read = file.read(buffer)?;
let mut i = 0;
while i < bytes_read {
*channel = (*channel + 1) % header.number_of_channels;
let sample = read_u16_from_buffer(&buffer, i);
println!("{}:{}", channel, sample);
i += bytes_per_sample;
}
Ok(bytes_read == buffer.len())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment