use std::io::{Cursor, Write}; use anyhow::Result; use byteorder::{LittleEndian, WriteBytesExt}; use log::{debug, trace}; #[rustfmt::skip] const COEFFS: &[CoefSet] = &[ (256, 0), (512, -256), ( 0, 0), (192, 64), (240, 0), (460, -208), (392, -232), ]; trait WaveChunk { fn to_chunk(&self) -> Vec; } type CoefSet = (i16, i16); pub struct WaveFormat { // w_format_tag = 2 pub n_channels: u16, pub n_samples_per_sec: u32, pub n_avg_bytes_per_sec: u32, pub n_block_align: u16, // w_bits_per_sample = 4 // cb_size = 32 pub n_samples_per_block: u16, // w_num_coeff = 7 // a_coeff = COEFFS } impl WaveChunk for WaveFormat { fn to_chunk(&self) -> Vec { let mut buf = Cursor::new(Vec::new()); write!(buf, "fmt ").unwrap(); buf.write_u32::(2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 + 2 + 4 * COEFFS.len() as u32) .unwrap(); buf.write_u16::(2).unwrap(); // WAVE_FORMAT_ADPCM buf.write_u16::(self.n_channels).unwrap(); buf.write_u32::(self.n_samples_per_sec) .unwrap(); buf.write_u32::(self.n_avg_bytes_per_sec) .unwrap(); buf.write_u16::(self.n_block_align).unwrap(); buf.write_u16::(4).unwrap(); // wBitsPerSample buf.write_u16::(32).unwrap(); // cbSize buf.write_u16::(self.n_samples_per_block) .unwrap(); buf.write_u16::(7).unwrap(); // wNumCoeff for coef_set in COEFFS { buf.write_i16::(coef_set.0).unwrap(); buf.write_i16::(coef_set.1).unwrap(); } buf.into_inner() } } struct WaveFact { length_samples: u32, } impl WaveChunk for WaveFact { fn to_chunk(&self) -> Vec { let mut buf = Cursor::new(Vec::new()); write!(buf, "fact").unwrap(); buf.write_u32::(4).unwrap(); buf.write_u32::(self.length_samples).unwrap(); buf.into_inner() } } struct RIFFHeader { file_size: u32, } impl WaveChunk for RIFFHeader { fn to_chunk(&self) -> Vec { let mut buf = Cursor::new(Vec::new()); write!(buf, "RIFF").unwrap(); buf.write_u32::(self.file_size).unwrap(); write!(buf, "WAVE").unwrap(); buf.into_inner() } } pub fn build_wav(format: WaveFormat, data: &[u8]) -> Result> { debug!("Building file"); let riff_header = RIFFHeader { file_size: 82 + data.len() as u32, }; let fact = WaveFact { length_samples: ((data.len() as u32 / format.n_block_align as u32) * ((format.n_block_align - (7 * format.n_channels)) * 8) as u32 / 4) / format.n_channels as u32, }; let mut buf = Cursor::new(Vec::new()); trace!("Building RIFF header"); buf.write_all(&riff_header.to_chunk())?; trace!("Building fmt chunk"); buf.write_all(&format.to_chunk())?; trace!("Building fact chunk"); buf.write_all(&fact.to_chunk())?; write!(buf, "data")?; buf.write_u32::(data.len() as u32).unwrap(); buf.write_all(data)?; Ok(buf.into_inner()) }