diff --git a/Cargo.lock b/Cargo.lock index b1912ea..5fa4022 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "ansi_term" version = "0.11.0" @@ -32,6 +34,7 @@ version = "1.1.0" dependencies = [ "byteorder", "clap", + "dasp_sample", "encoding", "serde_json", "uuid", @@ -58,6 +61,12 @@ dependencies = [ "vec_map", ] +[[package]] +name = "dasp_sample" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" + [[package]] name = "encoding" version = "0.2.33" diff --git a/Cargo.toml b/Cargo.toml index 5ed401f..177a50d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bwavfile" version = "1.1.0" -authors = ["Jamie Hardt "] +authors = ["Jamie Hardt ", "Ian Hobson "] edition = "2018" license = "MIT" description = "Rust Wave File Reader/Writer with Broadcast-WAV, MBWF and RF64 Support" @@ -15,6 +15,7 @@ keywords = ["audio", "broadcast", "multimedia","smpte"] [dependencies] byteorder = "1.3.4" +dasp_sample = "0.11.0" encoding = "0.2.33" uuid = "0.8.1" clap = "2.33.3" diff --git a/examples/blits.rs b/examples/blits.rs index ad0e67d..fc04a52 100644 --- a/examples/blits.rs +++ b/examples/blits.rs @@ -208,7 +208,7 @@ fn create_blits_file(file_name: &str, sample_rate: u32, bits_per_sample: u16) -> let mut fw = file.audio_frame_writer()?; for frame in frames { let buf = vec![frame.0, frame.1, frame.2, frame.3, frame.4, frame.5]; - fw.write_integer_frames(&buf)?; + fw.write_frames(&buf)?; } fw.end()?; diff --git a/examples/wave-deinter.rs b/examples/wave-deinter.rs index 29fc8f2..a75b4ea 100644 --- a/examples/wave-deinter.rs +++ b/examples/wave-deinter.rs @@ -1,14 +1,17 @@ -//! wave-inter.rs +//! wave-deinter.rs //! (c) 2021 Jamie Hardt. All rights reserved. //! -//! This program demonstrats combining several wave files into a single -//! polyphonic wave file. +//! This program demonstrates splitting a multichannel file into separate monophonic files for each +//! individual channel. -use std::io; +use std::io::{Read, Seek}; use std::path::Path; extern crate bwavfile; -use bwavfile::{ChannelDescriptor, ChannelMask, Error, WaveFmt, WaveReader, WaveWriter}; +use bwavfile::{ + ChannelDescriptor, ChannelMask, CommonFormat, Error, Sample, WaveFmt, WaveReader, WaveWriter, + I24, +}; #[macro_use] extern crate clap; @@ -48,17 +51,25 @@ fn name_suffix( } } -fn process_file(infile: &str, delim: &str, numeric_channel_names: bool) -> Result<(), Error> { - let mut input_file = WaveReader::open(infile)?; +fn deinterleave_file( + mut input_file: WaveReader, + input_format: WaveFmt, + settings: Settings, +) -> Result<(), Error> +where + S: Sample, + R: Read + Seek, +{ + let frames_per_read = 4096; let channel_desc = input_file.channels()?; - let input_format = input_file.format()?; + let channel_count = channel_desc.len(); if channel_desc.len() == 1 { println!("Input file in monoaural, exiting."); return Ok(()); } - let infile_path = Path::new(infile); + let infile_path = Path::new(&settings.input_path); let basename = infile_path .file_stem() .expect("Unable to extract file basename") @@ -68,38 +79,91 @@ fn process_file(infile: &str, delim: &str, numeric_channel_names: bool) -> Resul .parent() .expect("Unable to derive parent directory"); - let ouptut_format = - WaveFmt::new_pcm_mono(input_format.sample_rate, input_format.bits_per_sample); - let mut input_wave_reader = input_file.audio_frame_reader()?; + let output_block_alignment = input_format.bits_per_sample / 8; + let output_format = WaveFmt { + channel_count: 1, + block_alignment: output_block_alignment, + bytes_per_second: output_block_alignment as u32 * input_format.sample_rate, + ..input_format + }; + let mut reader = input_file.audio_frame_reader()?; + let mut writers = channel_desc + .iter() + .enumerate() + .map(|(n, channel)| { + let suffix = name_suffix( + settings.use_numeric_names, + &settings.delimiter, + n + 1, + channel, + ); + let outfile_name = output_dir + .join(format!("{}{}.wav", basename, suffix)) + .into_os_string() + .into_string() + .unwrap(); - for (n, channel) in channel_desc.iter().enumerate() { - let suffix = name_suffix(numeric_channel_names, delim, n + 1, channel); - let outfile_name = output_dir - .join(format!("{}{}.wav", basename, suffix)) - .into_os_string() - .into_string() - .unwrap(); + println!("Will create file {}", outfile_name); - println!("Will create file {}", outfile_name); + WaveWriter::create(&outfile_name, output_format) + .expect("Failed to create new file") + .audio_frame_writer() + }) + .collect::, _>>()?; - let output_file = - WaveWriter::create(&outfile_name, ouptut_format).expect("Failed to create new file"); + let mut input_buffer = vec![S::EQUILIBRIUM; frames_per_read * channel_count]; + let mut output_buffer = vec![S::EQUILIBRIUM; frames_per_read]; - let mut output_wave_writer = output_file.audio_frame_writer()?; - let mut buffer = input_format.create_frame_buffer(1); - - while input_wave_reader.read_integer_frame(&mut buffer)? > 0 { - output_wave_writer.write_integer_frames(&buffer[n..=n])?; + loop { + let frames_read = reader.read_frames(&mut input_buffer)? as usize; + if frames_read == 0 { + break; } - output_wave_writer.end()?; - input_wave_reader.locate(0)?; + output_buffer.resize(frames_read, S::EQUILIBRIUM); + + for (n, writer) in writers.iter_mut().enumerate() { + for (output, input) in output_buffer + .iter_mut() + .zip(input_buffer.iter().skip(n).step_by(channel_count)) + { + *output = *input; + } + writer.write_frames(&output_buffer)?; + } + } + + for writer in writers.drain(..) { + writer.end()?; } Ok(()) } -fn main() -> io::Result<()> { +fn process_file(mut input: WaveReader, settings: Settings) -> Result<(), Error> +where + R: Read + Seek, +{ + let format = input.format()?; + + use CommonFormat::*; + match (format.common_format(), format.bits_per_sample) { + (IntegerPCM, 8) => deinterleave_file::(input, format, settings), + (IntegerPCM, 16) => deinterleave_file::(input, format, settings), + (IntegerPCM, 24) => deinterleave_file::(input, format, settings), + (IntegerPCM, 32) => deinterleave_file::(input, format, settings), + (IeeeFloatPCM, 32) => deinterleave_file::(input, format, settings), + other => panic!("Unsupported format: {:?}", other), + } +} + +struct Settings { + input_path: String, + delimiter: String, + use_numeric_names: bool, +} + +fn main() -> Result<(), Box> { let matches = App::new("wave-deinter") .version(crate_version!()) .author(crate_authors!()) @@ -126,13 +190,12 @@ fn main() -> io::Result<()> { ) .get_matches(); - let delimiter = matches.value_of("channel_delimiter").unwrap(); - let use_numeric_names = matches.is_present("numeric_names"); - let infile = matches.value_of("INPUT").unwrap(); + let settings = Settings { + input_path: matches.value_of("INPUT").unwrap().into(), + delimiter: matches.value_of("channel_delimiter").unwrap().into(), + use_numeric_names: matches.is_present("numeric_names"), + }; - match process_file(infile, delimiter, use_numeric_names) { - Err(Error::IOError(io)) => Err(io), - Err(e) => panic!("Error: {:?}", e), - Ok(()) => Ok(()), - } + process_file(WaveReader::open(&settings.input_path)?, settings)?; + Ok(()) } diff --git a/src/bext.rs b/src/bext.rs index 5c0fdb1..a60da4c 100644 --- a/src/bext.rs +++ b/src/bext.rs @@ -1,4 +1,5 @@ pub type LU = f32; +#[allow(clippy::upper_case_acronyms)] pub type LUFS = f32; pub type Decibels = f32; diff --git a/src/chunks.rs b/src/chunks.rs index 1e78802..d43e286 100644 --- a/src/chunks.rs +++ b/src/chunks.rs @@ -21,11 +21,7 @@ pub trait ReadBWaveChunks: Read { pub trait WriteBWaveChunks: Write { fn write_wave_fmt(&mut self, format: &WaveFmt) -> Result<(), ParserError>; - fn write_bext_string_field( - &mut self, - string: &String, - length: usize, - ) -> Result<(), ParserError>; + fn write_bext_string_field(&mut self, string: &str, length: usize) -> Result<(), ParserError>; fn write_bext(&mut self, bext: &Bext) -> Result<(), ParserError>; } @@ -34,7 +30,7 @@ where T: Write, { fn write_wave_fmt(&mut self, format: &WaveFmt) -> Result<(), ParserError> { - self.write_u16::(format.tag as u16)?; + self.write_u16::(format.tag)?; self.write_u16::(format.channel_count)?; self.write_u32::(format.sample_rate)?; self.write_u32::(format.bytes_per_second)?; @@ -46,18 +42,14 @@ where self.write_u16::(ext.valid_bits_per_sample)?; self.write_u32::(ext.channel_mask)?; let uuid = ext.type_guid.as_bytes(); - self.write(uuid)?; + self.write_all(uuid)?; } Ok(()) } - fn write_bext_string_field( - &mut self, - string: &String, - length: usize, - ) -> Result<(), ParserError> { + fn write_bext_string_field(&mut self, string: &str, length: usize) -> Result<(), ParserError> { let mut buf = ASCII - .encode(&string, EncoderTrap::Ignore) + .encode(string, EncoderTrap::Ignore) .expect("Error encoding text"); buf.truncate(length); let filler_length = length - buf.len(); @@ -142,12 +134,8 @@ where fn read_bext_string_field(&mut self, length: usize) -> Result { let mut buffer: Vec = vec![0; length]; - self.read(&mut buffer)?; - let trimmed: Vec = buffer - .iter() - .take_while(|c| **c != 0 as u8) - .cloned() - .collect(); + self.read_exact(&mut buffer)?; + let trimmed: Vec = buffer.iter().take_while(|c| **c != 0_u8).cloned().collect(); Ok(ASCII .decode(&trimmed, DecoderTrap::Ignore) .expect("Error decoding text")) @@ -168,7 +156,7 @@ where }, umid: { let mut buf = [0u8; 64]; - self.read(&mut buf)?; + self.read_exact(&mut buf)?; if version > 0 { Some(buf) } else { diff --git a/src/common_format.rs b/src/common_format.rs index 1f6e2d0..9f0e3d7 100644 --- a/src/common_format.rs +++ b/src/common_format.rs @@ -1,9 +1,9 @@ use uuid::Uuid; -const BASIC_PCM: u16 = 0x0001; -const BASIC_FLOAT: u16 = 0x0003; -const BASIC_MPEG: u16 = 0x0050; -const BASIC_EXTENDED: u16 = 0xFFFE; +pub const WAVE_TAG_PCM: u16 = 0x0001; +pub const WAVE_TAG_FLOAT: u16 = 0x0003; +pub const WAVE_TAG_MPEG: u16 = 0x0050; +pub const WAVE_TAG_EXTENDED: u16 = 0xFFFE; /* RC 2361 §4: @@ -15,23 +15,23 @@ const BASIC_EXTENDED: u16 = 0xFFFE; */ -pub const UUID_PCM: Uuid = Uuid::from_bytes([ +pub const WAVE_UUID_PCM: Uuid = Uuid::from_bytes([ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71, ]); -pub const UUID_FLOAT: Uuid = Uuid::from_bytes([ +pub const WAVE_UUID_FLOAT: Uuid = Uuid::from_bytes([ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71, ]); -pub const UUID_MPEG: Uuid = Uuid::from_bytes([ +pub const WAVE_UUID_MPEG: Uuid = Uuid::from_bytes([ 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71, ]); -pub const UUID_BFORMAT_PCM: Uuid = Uuid::from_bytes([ +pub const WAVE_UUID_BFORMAT_PCM: Uuid = Uuid::from_bytes([ 0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1, 0xca, 0x00, 0x00, 0x00, ]); -pub const UUID_BFORMAT_FLOAT: Uuid = Uuid::from_bytes([ +pub const WAVE_UUID_BFORMAT_FLOAT: Uuid = Uuid::from_bytes([ 0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1, 0xca, 0x00, 0x00, 0x00, ]); @@ -71,14 +71,14 @@ impl CommonFormat { /// Resolve a tag and Uuid to a `CommonFormat`. pub fn make(basic: u16, uuid: Option) -> Self { match (basic, uuid) { - (BASIC_PCM, _) => Self::IntegerPCM, - (BASIC_FLOAT, _) => Self::IeeeFloatPCM, - (BASIC_MPEG, _) => Self::Mpeg, - (BASIC_EXTENDED, Some(UUID_PCM)) => Self::IntegerPCM, - (BASIC_EXTENDED, Some(UUID_FLOAT)) => Self::IeeeFloatPCM, - (BASIC_EXTENDED, Some(UUID_BFORMAT_PCM)) => Self::AmbisonicBFormatIntegerPCM, - (BASIC_EXTENDED, Some(UUID_BFORMAT_FLOAT)) => Self::AmbisonicBFormatIeeeFloatPCM, - (BASIC_EXTENDED, Some(x)) => CommonFormat::UnknownExtended(x), + (WAVE_TAG_PCM, _) => Self::IntegerPCM, + (WAVE_TAG_FLOAT, _) => Self::IeeeFloatPCM, + (WAVE_TAG_MPEG, _) => Self::Mpeg, + (WAVE_TAG_EXTENDED, Some(WAVE_UUID_PCM)) => Self::IntegerPCM, + (WAVE_TAG_EXTENDED, Some(WAVE_UUID_FLOAT)) => Self::IeeeFloatPCM, + (WAVE_TAG_EXTENDED, Some(WAVE_UUID_BFORMAT_PCM)) => Self::AmbisonicBFormatIntegerPCM, + (WAVE_TAG_EXTENDED, Some(WAVE_UUID_BFORMAT_FLOAT)) => Self::AmbisonicBFormatIeeeFloatPCM, + (WAVE_TAG_EXTENDED, Some(x)) => CommonFormat::UnknownExtended(x), (x, _) => CommonFormat::UnknownBasic(x), } } @@ -89,13 +89,13 @@ impl CommonFormat { /// returned tag will be 0xFFFE and the `Uuid` will describe the format. pub fn take(self) -> (u16, Uuid) { match self { - Self::IntegerPCM => (BASIC_PCM, UUID_PCM), - Self::IeeeFloatPCM => (BASIC_FLOAT, UUID_FLOAT), - Self::Mpeg => (BASIC_MPEG, UUID_MPEG), - Self::AmbisonicBFormatIntegerPCM => (BASIC_EXTENDED, UUID_BFORMAT_PCM), - Self::AmbisonicBFormatIeeeFloatPCM => (BASIC_EXTENDED, UUID_BFORMAT_FLOAT), + Self::IntegerPCM => (WAVE_TAG_PCM, WAVE_UUID_PCM), + Self::IeeeFloatPCM => (WAVE_TAG_FLOAT, WAVE_UUID_FLOAT), + Self::Mpeg => (WAVE_TAG_MPEG, WAVE_UUID_MPEG), + Self::AmbisonicBFormatIntegerPCM => (WAVE_TAG_EXTENDED, WAVE_UUID_BFORMAT_PCM), + Self::AmbisonicBFormatIeeeFloatPCM => (WAVE_TAG_EXTENDED, WAVE_UUID_BFORMAT_FLOAT), Self::UnknownBasic(x) => (x, uuid_from_basic_tag(x)), - Self::UnknownExtended(x) => (BASIC_EXTENDED, x), + Self::UnknownExtended(x) => (WAVE_TAG_EXTENDED, x), } } } diff --git a/src/cue.rs b/src/cue.rs index 5d1d8fd..9c6b727 100644 --- a/src/cue.rs +++ b/src/cue.rs @@ -1,6 +1,7 @@ #![allow(dead_code)] -use super::fourcc::{FourCC,ReadFourCC, WriteFourCC, LABL_SIG, NOTE_SIG, - ADTL_SIG, LTXT_SIG, DATA_SIG}; +use super::fourcc::{ + FourCC, ReadFourCC, WriteFourCC, ADTL_SIG, DATA_SIG, LABL_SIG, LTXT_SIG, NOTE_SIG, +}; use super::list_form::collect_list_form; @@ -68,10 +69,8 @@ struct RawLabel { impl RawLabel { fn write_to(&self) -> Vec { let mut writer = Cursor::new(vec![0u8; 0]); - writer - .write_u32::(self.cue_point_id as u32) - .unwrap(); - writer.write(&self.text).unwrap(); + writer.write_u32::(self.cue_point_id).unwrap(); + writer.write_all(&self.text).unwrap(); writer.into_inner() } @@ -82,7 +81,7 @@ impl RawLabel { Ok(Self { cue_point_id: rdr.read_u32::()?, text: { - let mut buf = vec![0u8; (length - 4) as usize]; + let mut buf = vec![0u8; length - 4]; rdr.read_exact(&mut buf)?; buf }, @@ -100,7 +99,7 @@ impl RawNote { fn write_to(&self) -> Vec { let mut writer = Cursor::new(vec![0u8; 0]); writer.write_u32::(self.cue_point_id).unwrap(); - writer.write(&self.text).unwrap(); + writer.write_all(&self.text).unwrap(); writer.into_inner() } @@ -111,7 +110,7 @@ impl RawNote { Ok(Self { cue_point_id: rdr.read_u32::()?, text: { - let mut buf = vec![0u8; (length - 4) as usize]; + let mut buf = vec![0u8; length - 4]; rdr.read_exact(&mut buf)?; buf }, @@ -142,7 +141,7 @@ impl RawLtxt { writer.write_u16::(self.dialect).unwrap(); writer.write_u16::(self.code_page).unwrap(); if let Some(ext_text) = &self.text { - writer.write(ext_text).unwrap(); + writer.write_all(ext_text).unwrap(); } writer.into_inner() } @@ -161,7 +160,7 @@ impl RawLtxt { code_page: rdr.read_u16::()?, text: { if length - 20 > 0 { - let mut buf = vec![0u8; (length - 20) as usize]; + let mut buf = vec![0u8; length - 20]; rdr.read_exact(&mut buf)?; Some(buf) } else { @@ -186,14 +185,14 @@ impl RawAdtlMember { // It seems like all this casing could be done with traits for member in members.iter() { let (fcc, buf) = match member { - RawAdtlMember::Label(l) => ((LABL_SIG, l.write_to())), - RawAdtlMember::Note(n) => ((NOTE_SIG, n.write_to())), - RawAdtlMember::LabeledText(t) => ((LTXT_SIG, t.write_to())), + RawAdtlMember::Label(l) => (LABL_SIG, l.write_to()), + RawAdtlMember::Note(n) => (NOTE_SIG, n.write_to()), + RawAdtlMember::LabeledText(t) => (LTXT_SIG, t.write_to()), RawAdtlMember::Unrecognized(f) => (*f, vec![0u8; 0]), // <-- this is a dopey case but here for completeness }; w.write_fourcc(fcc).unwrap(); w.write_u32::(buf.len() as u32).unwrap(); - w.write(&buf).unwrap(); + w.write_all(&buf).unwrap(); if buf.len() % 2 == 1 { w.write_u8(0).unwrap(); } @@ -205,7 +204,7 @@ impl RawAdtlMember { writer .write_u32::(chunk_content.len() as u32) .unwrap(); - writer.write(&chunk_content).unwrap(); + writer.write_all(&chunk_content).unwrap(); writer.into_inner() } @@ -291,11 +290,7 @@ pub struct Cue { } fn convert_to_cue_string(buffer: &[u8]) -> String { - let trimmed: Vec = buffer - .iter() - .take_while(|c| **c != 0 as u8) - .cloned() - .collect(); + let trimmed: Vec = buffer.iter().take_while(|c| **c != 0_u8).cloned().collect(); ASCII .decode(&trimmed, DecoderTrap::Ignore) .expect("Error decoding text") @@ -303,7 +298,7 @@ fn convert_to_cue_string(buffer: &[u8]) -> String { fn convert_from_cue_string(val: &str) -> Vec { ASCII - .encode(&val, EncoderTrap::Ignore) + .encode(val, EncoderTrap::Ignore) .expect("Error encoding text") } @@ -324,12 +319,12 @@ impl Cue { let raw_label = cue.label.as_ref().map(|val| RawLabel { cue_point_id: n as u32, - text: convert_from_cue_string(&val), + text: convert_from_cue_string(val), }); let raw_note = cue.note.as_ref().map(|val| RawNote { cue_point_id: n as u32, - text: convert_from_cue_string(&val), + text: convert_from_cue_string(val), }); let raw_ltxt = cue.length.map(|val| RawLtxt { @@ -349,9 +344,15 @@ impl Cue { (Vec::::new(), Vec::::new()), |(mut cues, mut adtls), (cue, label, note, ltxt)| { cues.push(cue); - label.map(|l| adtls.push(RawAdtlMember::Label(l))); - note.map(|n| adtls.push(RawAdtlMember::Note(n))); - ltxt.map(|m| adtls.push(RawAdtlMember::LabeledText(m))); + if let Some(l) = label { + adtls.push(RawAdtlMember::Label(l)) + } + if let Some(n) = note { + adtls.push(RawAdtlMember::Note(n)) + } + if let Some(m) = ltxt { + adtls.push(RawAdtlMember::LabeledText(m)) + } (cues, adtls) }, ) diff --git a/src/errors.rs b/src/errors.rs index 3d073fb..7fd988f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -5,8 +5,6 @@ use std::{ io, }; -use uuid; - /// Errors returned by methods in this crate. #[derive(Debug)] pub enum Error { @@ -43,6 +41,12 @@ pub enum Error { /// The file is not optimized for writing new data DataChunkNotPreparedForAppend, + + /// A buffer with a length that isn't a multiple of channel_count was provided + InvalidBufferSize { + buffer_size: usize, + channel_count: u16, + }, } impl StdError for Error {} diff --git a/src/fmt.rs b/src/fmt.rs index cf3b19d..60654c5 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -1,9 +1,11 @@ -use super::common_format::{CommonFormat, UUID_BFORMAT_PCM, UUID_PCM}; +use crate::common_format::{CommonFormat, WAVE_UUID_BFORMAT_PCM, WAVE_UUID_PCM}; +use crate::Sample; + use std::io::Cursor; use uuid::Uuid; use byteorder::LittleEndian; -use byteorder::{ReadBytesExt, WriteBytesExt}; +use byteorder::ReadBytesExt; // Need more test cases for ADMAudioID #[allow(dead_code)] @@ -105,7 +107,7 @@ impl ChannelMask { (0..18) .map(|i| 1 << i) .filter(|mask| mask & input_mask > 0) - .map(|mask| Into::::into(mask)) + .map(ChannelMask::from) .collect() } } @@ -238,7 +240,7 @@ impl WaveFmt { extended_format: Some(WaveFmtExtended { valid_bits_per_sample: bits_per_sample, channel_mask: ChannelMask::DirectOut as u32, - type_guid: UUID_BFORMAT_PCM, + type_guid: WAVE_UUID_BFORMAT_PCM, }), } } @@ -261,28 +263,24 @@ impl WaveFmt { }); let result: (u16, Option) = match channel_bitmap { - ch if bits_per_sample != container_bits_per_sample => { - ( - 0xFFFE, - Some(WaveFmtExtended { - valid_bits_per_sample: bits_per_sample, - channel_mask: ch, - type_guid: UUID_PCM, - }), - ) - } + ch if bits_per_sample != container_bits_per_sample => ( + 0xFFFE, + Some(WaveFmtExtended { + valid_bits_per_sample: bits_per_sample, + channel_mask: ch, + type_guid: WAVE_UUID_PCM, + }), + ), 0b0100 => (0x0001, None), 0b0011 => (0x0001, None), - ch => { - ( - 0xFFFE, - Some(WaveFmtExtended { - valid_bits_per_sample: bits_per_sample, - channel_mask: ch, - type_guid: UUID_PCM, - }), - ) - } + ch => ( + 0xFFFE, + Some(WaveFmtExtended { + valid_bits_per_sample: bits_per_sample, + channel_mask: ch, + type_guid: WAVE_UUID_PCM, + }), + ), }; let (tag, extformat) = result; @@ -313,8 +311,8 @@ impl WaveFmt { /// /// This is a conveneince method that creates a `Vec` with /// as many elements as there are channels in the underlying stream. - pub fn create_frame_buffer(&self, length: usize) -> Vec { - vec![0i32; self.channel_count as usize * length] + pub fn create_frame_buffer(&self, length: usize) -> Vec { + vec![S::EQUILIBRIUM; self.channel_count as usize * length] } /// Create a raw byte buffer to hold `length` blocks from a reader or @@ -323,33 +321,11 @@ impl WaveFmt { vec![0u8; self.block_alignment as usize * length] } - /// Write frames into a byte vector - pub fn pack_frames(&self, from_frames: &[i32], into_bytes: &mut [u8]) -> () { - let mut write_cursor = Cursor::new(into_bytes); - - assert!( - from_frames.len() % self.channel_count as usize == 0, - "frames buffer does not contain a number of samples % channel_count == 0" - ); - - for n in 0..from_frames.len() { - match (self.valid_bits_per_sample(), self.bits_per_sample) { - (0..=8,8) => write_cursor.write_u8((from_frames[n] + 0x80) as u8 ).unwrap(), // EBU 3285 §A2.2 - (9..=16,16) => write_cursor.write_i16::(from_frames[n] as i16).unwrap(), - (10..=24,24) => write_cursor.write_i24::(from_frames[n]).unwrap(), - (25..=32,32) => write_cursor.write_i32::(from_frames[n]).unwrap(), - (b,_)=> panic!("Unrecognized integer format, bits per sample {}, channels {}, block_alignment {}", - b, self.channel_count, self.block_alignment) - } - } - () - } - /// Read bytes into frames - pub fn unpack_frames(&self, from_bytes: &[u8], into_frames: &mut [i32]) -> () { + pub fn unpack_frames(&self, from_bytes: &[u8], into_frames: &mut [i32]) { let mut rdr = Cursor::new(from_bytes); - for n in 0..(into_frames.len()) { - into_frames[n] = match (self.valid_bits_per_sample(), self.bits_per_sample) { + for frame in into_frames { + *frame = match (self.valid_bits_per_sample(), self.bits_per_sample) { (0..=8,8) => rdr.read_u8().unwrap() as i32 - 0x80_i32, // EBU 3285 §A2.2 (9..=16,16) => rdr.read_i16::().unwrap() as i32, (10..=24,24) => rdr.read_i24::().unwrap(), @@ -425,8 +401,8 @@ where ) -> Result { assert!(into.len() % format.channel_count as usize == 0); - for n in 0..(into.len()) { - into[n] = match (format.valid_bits_per_sample(), format.bits_per_sample) { + for frame in into { + *frame = match (format.valid_bits_per_sample(), format.bits_per_sample) { (0..=8,8) => self.read_u8().unwrap() as i32 - 0x80_i32, // EBU 3285 §A2.2 (9..=16,16) => self.read_i16::().unwrap() as i32, (10..=24,24) => self.read_i24::().unwrap(), @@ -457,10 +433,10 @@ impl WriteWavAudioData for T where T: std::io::Write, { - fn write_i32_frames(&mut self, format: WaveFmt, _: &[i32]) -> Result { + fn write_i32_frames(&mut self, _format: WaveFmt, _: &[i32]) -> Result { todo!() } - fn write_f32_frames(&mut self, format: WaveFmt, _: &[f32]) -> Result { + fn write_f32_frames(&mut self, _format: WaveFmt, _: &[f32]) -> Result { todo!() } } diff --git a/src/lib.rs b/src/lib.rs index 0c87342..0490b66 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,13 +52,19 @@ mod chunks; mod cue; mod fmt; +mod sample; + mod wavereader; mod wavewriter; pub use bext::Bext; -pub use common_format::CommonFormat; +pub use common_format::{ + CommonFormat, WAVE_TAG_EXTENDED, WAVE_TAG_FLOAT, WAVE_TAG_MPEG, WAVE_TAG_PCM, + WAVE_UUID_BFORMAT_FLOAT, WAVE_UUID_BFORMAT_PCM, WAVE_UUID_FLOAT, WAVE_UUID_MPEG, WAVE_UUID_PCM, +}; pub use cue::Cue; pub use errors::Error; pub use fmt::{ADMAudioID, ChannelDescriptor, ChannelMask, WaveFmt, WaveFmtExtended}; +pub use sample::{Sample, I24}; pub use wavereader::{AudioFrameReader, WaveReader}; pub use wavewriter::{AudioFrameWriter, WaveWriter}; diff --git a/src/parser.rs b/src/parser.rs index f7cd87c..19d434f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -71,11 +71,11 @@ impl Parser { let newmap: HashMap = HashMap::new(); let mut the_stream = stream; the_stream.seek(Start(0))?; - return Ok(Parser { + Ok(Parser { stream: the_stream, state: State::New, ds64state: newmap, - }); + }) } // pub fn into_inner(self) -> R { @@ -131,7 +131,7 @@ impl Iterator for Parser { fn next(&mut self) -> Option { let (event, next_state) = self.advance(); self.state = next_state; - return event; + event } } @@ -171,7 +171,7 @@ impl Parser { } } - return Ok((event, next_state)); + Ok((event, next_state)) } fn parse_ds64(&mut self) -> Result<(Event, State), Error> { @@ -182,7 +182,7 @@ impl Parser { let mut read: u64 = 0; if ds64_sig != DS64_SIG { - return Err(Error::MissingRequiredDS64); + Err(Error::MissingRequiredDS64) } else { let long_file_size = self.stream.read_u64::()?; let long_data_size = self.stream.read_u64::()?; @@ -221,7 +221,7 @@ impl Parser { remaining: long_file_size - (4 + 8 + ds64_size), }; - return Ok((event, state)); + Ok((event, state)) } } @@ -262,48 +262,33 @@ impl Parser { } } - return Ok((event, state)); + Ok((event, state)) } fn handle_state(&mut self) -> Result<(Option, State), Error> { match self.state { - State::New => { - return Ok((Some(Event::StartParse), State::ReadyForHeader)); - } + State::New => Ok((Some(Event::StartParse), State::ReadyForHeader)), State::ReadyForHeader => { let (event, state) = self.parse_header()?; - return Ok((Some(event), state)); + Ok((Some(event), state)) } State::ReadyForDS64 => { let (event, state) = self.parse_ds64()?; - return Ok((Some(event), state)); + Ok((Some(event), state)) } State::ReadyForChunk { at, remaining } => { let (event, state) = self.enter_chunk(at, remaining)?; - return Ok((Some(event), state)); - } - State::Error => { - return Ok((Some(Event::FinishParse), State::Complete)); - } - State::Complete => { - return Ok((None, State::Complete)); + Ok((Some(event), state)) } + State::Error => Ok((Some(Event::FinishParse), State::Complete)), + State::Complete => Ok((None, State::Complete)), } } fn advance(&mut self) -> (Option, State) { match self.handle_state() { - Ok((event, state)) => { - return (event, state); - } - Err(error) => { - return ( - Some(Event::Failed { - error: error.into(), - }), - State::Error, - ); - } + Ok((event, state)) => (event, state), + Err(error) => (Some(Event::Failed { error }), State::Error), } } } diff --git a/src/sample.rs b/src/sample.rs new file mode 100644 index 0000000..5fed5b6 --- /dev/null +++ b/src/sample.rs @@ -0,0 +1,14 @@ +pub use dasp_sample::I24; + +use dasp_sample::Duplex; + +pub trait Sample: + dasp_sample::Sample + Duplex + Duplex + Duplex + Duplex + Duplex +{ +} + +impl Sample for u8 {} +impl Sample for i16 {} +impl Sample for I24 {} +impl Sample for i32 {} +impl Sample for f32 {} diff --git a/src/wavereader.rs b/src/wavereader.rs index 7a7f78f..9d71102 100644 --- a/src/wavereader.rs +++ b/src/wavereader.rs @@ -4,7 +4,7 @@ use std::path::Path; use std::io::Cursor; use std::io::SeekFrom; -use std::io::SeekFrom::{Current, Start}; +use std::io::SeekFrom::Start; use std::io::{BufReader, Read, Seek}; use super::bext::Bext; @@ -18,11 +18,13 @@ use super::fourcc::{ IXML_SIG, JUNK_SIG, LIST_SIG, }; use super::parser::Parser; -use super::CommonFormat; +use super::{CommonFormat, Sample, I24}; use byteorder::LittleEndian; use byteorder::ReadBytesExt; +use dasp_sample::Sample as _; // Expose to_sample() + /// Read audio frames /// /// The inner reader is interpreted as a raw audio data @@ -88,78 +90,79 @@ impl AudioFrameReader { Ok((seek_result - self.start) / self.format.block_alignment as u64) } - /// Read a frame + /// Reads frames from the file into the provided buffer /// - /// A single frame is read from the audio stream and the read location - /// is advanced one frame. + /// The function will attempt to fill the buffer, but will stop without error when the end of + /// the file is reached. /// - /// Regardless of the number of bits in the audio sample, this method - /// always writes `i32` samples back to the buffer. These samples are - /// written back "right-aligned" so samples that are shorter than i32 - /// will leave the MSB bits empty. + /// The reader will convert from the file's sample type into the buffer's sample type. + /// Note that no dithering will be applied during sample type conversion, + /// if dithering is required then it will need to be applied manually. /// - /// For example: A full-code sample in 16 bit (0xFFFF) will be written - /// back to the buffer as 0x0000FFFF. - /// - /// - /// ### Panics - /// - /// The `buffer` must have a number of elements equal to the number of - /// channels and this method will panic if this is not the case. - pub fn read_integer_frame(&mut self, buffer: &mut [i32]) -> Result { - assert!( - buffer.len() as u16 == self.format.channel_count, - "read_integer_frame was called with a mis-sized buffer, expected {}, was {}", - self.format.channel_count, - buffer.len() - ); + /// The return value is the number of frames read into the buffer. + pub fn read_frames(&mut self, buffer: &mut [S]) -> Result + where + S: Sample, + { + use CommonFormat::*; - let framed_bits_per_sample = self.format.block_alignment * 8 / self.format.channel_count; + let channel_count = self.format.channel_count as usize; + let common_format = self.format.common_format(); + let bits_per_sample = self.format.bits_per_sample; - let tell = self.inner.seek(Current(0))?; - - if (tell - self.start) < self.length { - for n in 0..(self.format.channel_count as usize) { - buffer[n] = match (self.format.bits_per_sample, framed_bits_per_sample) { - (0..=8,8) => self.inner.read_u8()? as i32 - 0x80_i32, // EBU 3285 §A2.2 - (9..=16,16) => self.inner.read_i16::()? as i32, - (10..=24,24) => self.inner.read_i24::()?, - (25..=32,32) => self.inner.read_i32::()?, - (b,_)=> panic!("Unrecognized integer format, bits per sample {}, channels {}, block_alignment {}", - b, self.format.channel_count, self.format.block_alignment) - } - } - Ok(1) - } else { - Ok(0) + if buffer.len() % channel_count != 0 { + return Err(Error::InvalidBufferSize { + buffer_size: buffer.len(), + channel_count: self.format.channel_count, + }); } + + let position = self.inner.stream_position()? - self.start; + let frames_requested = (buffer.len() / channel_count) as u64; + let bytes_per_frame = self.format.block_alignment as u64; + let frames_remaining = (self.length - position) / bytes_per_frame; + let frames_to_read = frames_requested.min(frames_remaining); + let samples_to_read = frames_to_read as usize * channel_count; + + match (common_format, bits_per_sample) { + (IntegerPCM, 8) => read_into_buffer(samples_to_read, buffer, || { + Ok(self.inner.read_u8()?.to_sample()) + }), + (IntegerPCM, 16) => read_into_buffer(samples_to_read, buffer, || { + Ok(self.inner.read_i16::()?.to_sample()) + }), + (IntegerPCM, 24) => read_into_buffer(samples_to_read, buffer, || { + Ok(I24::from(self.inner.read_i24::()?).to_sample()) + }), + (IntegerPCM, 32) => read_into_buffer(samples_to_read, buffer, || { + Ok(self.inner.read_i32::()?.to_sample()) + }), + (IeeeFloatPCM, 32) => read_into_buffer(samples_to_read, buffer, || { + Ok(self.inner.read_f32::()?.to_sample()) + }), + (_, _) => panic!( + "Unsupported format, bits per sample {}, channels {}, sample format: {:?}", + bits_per_sample, channel_count, common_format + ), + }?; + + Ok(frames_to_read) + } +} + +fn read_into_buffer( + sample_count: usize, + buffer: &mut [S], + mut read_fn: F, +) -> Result<(), Error> +where + F: FnMut() -> Result, +{ + for output in buffer.iter_mut().take(sample_count) { + *output = read_fn()?; } - pub fn read_float_frame(&mut self, buffer: &mut [f32]) -> Result { - assert!( - buffer.len() as u16 == self.format.channel_count, - "read_float_frame was called with a mis-sized buffer, expected {}, was {}", - self.format.channel_count, - buffer.len() - ); - - let framed_bits_per_sample = self.format.block_alignment * 8 / self.format.channel_count; - - let tell = self.inner.seek(Current(0))?; - - if (tell - self.start) < self.length { - for n in 0..(self.format.channel_count as usize) { - buffer[n] = match (self.format.bits_per_sample, framed_bits_per_sample) { - (25..=32,32) => self.inner.read_f32::()?, - (b,_)=> panic!("Unrecognized integer format, bits per sample {}, channels {}, block_alignment {}", - b, self.format.channel_count, self.format.block_alignment) - } - } - Ok(1) - } else { - Ok(0) - } - } + Ok(()) } /// Wave, Broadcast-WAV and RF64/BW64 parser/reader. @@ -173,9 +176,9 @@ impl AudioFrameReader { /// assert_eq!(format.channel_count, 1); /// /// let mut frame_reader = r.audio_frame_reader().unwrap(); -/// let mut buffer = format.create_frame_buffer(1); +/// let mut buffer = format.create_frame_buffer::(1); /// -/// let read = frame_reader.read_integer_frame(&mut buffer).unwrap(); +/// let read = frame_reader.read_frames(&mut buffer).unwrap(); /// /// assert_eq!(buffer, [0i32]); /// assert_eq!(read, 1); @@ -216,7 +219,7 @@ impl WaveReader> { pub fn open>(path: P) -> Result { let f = File::open(path)?; let inner = BufReader::new(f); - Ok(Self::new(inner)?) + Self::new(inner) } } @@ -224,10 +227,9 @@ impl WaveReader { /// Open a file for reading with unbuffered IO. /// /// A convenience that opens `path` and calls `Self::new()` - pub fn open_unbuffered>(path: P) -> Result { let inner = File::open(path)?; - return Ok(Self::new(inner)?); + Self::new(inner) } } @@ -270,7 +272,7 @@ impl WaveReader { /// Unwrap the inner reader. pub fn into_inner(self) -> R { - return self.inner; + self.inner } /// @@ -279,12 +281,12 @@ impl WaveReader { pub fn audio_frame_reader(mut self) -> Result, ParserError> { let format = self.format()?; let audio_chunk_reader = self.get_chunk_extent_at_index(DATA_SIG, 0)?; - Ok(AudioFrameReader::new( + AudioFrameReader::new( self.inner, format, audio_chunk_reader.0, audio_chunk_reader.1, - )?) + ) } /// The count of audio frames in the file. @@ -580,18 +582,16 @@ impl WaveReader { &mut self, ident: FourCC, at: u32, - mut buffer: &mut Vec, + buffer: &mut Vec, ) -> Result { match self.get_chunk_extent_at_index(ident, at) { Ok((start, length)) => { buffer.resize(length as usize, 0x0); self.inner.seek(SeekFrom::Start(start))?; - self.inner - .read(&mut buffer) - .map_err(|e| ParserError::IOError(e)) + self.inner.read(buffer).map_err(ParserError::IOError) } Err(ParserError::ChunkMissing { signature: _ }) => Ok(0), - Err(any) => Err(any.into()), + Err(any) => Err(any), } } @@ -608,7 +608,7 @@ impl WaveReader { /// Index of first LIST for with the given FORM fourcc fn get_list_form(&mut self, fourcc: FourCC) -> Result, ParserError> { for (n, (start, _)) in self.get_chunks_extents(LIST_SIG)?.iter().enumerate() { - self.inner.seek(SeekFrom::Start(*start as u64))?; + self.inner.seek(SeekFrom::Start(*start))?; let this_fourcc = self.inner.read_fourcc()?; if this_fourcc == fourcc { return Ok(Some(n as u32)); @@ -623,7 +623,7 @@ impl WaveReader { fourcc: FourCC, index: u32, ) -> Result<(u64, u64), ParserError> { - if let Some((start, length)) = self.get_chunks_extents(fourcc)?.iter().nth(index as usize) { + if let Some((start, length)) = self.get_chunks_extents(fourcc)?.get(index as usize) { Ok((*start, *length)) } else { Err(ParserError::ChunkMissing { signature: fourcc }) diff --git a/src/wavewriter.rs b/src/wavewriter.rs index a620143..6c84bef 100644 --- a/src/wavewriter.rs +++ b/src/wavewriter.rs @@ -2,12 +2,14 @@ use std::fs::File; use std::io::{BufWriter, Cursor, Seek, SeekFrom, Write}; use std::path::Path; +use crate::CommonFormat; + use super::fmt::WaveFmt; use super::fourcc::{ FourCC, WriteFourCC, AXML_SIG, BEXT_SIG, DATA_SIG, DS64_SIG, ELM1_SIG, FMT__SIG, IXML_SIG, JUNK_SIG, RF64_SIG, RIFF_SIG, WAVE_SIG, }; -use super::Error; +use super::{Error, Sample, I24}; //use super::common_format::CommonFormat; use super::bext::Bext; use super::chunks::WriteBWaveChunks; @@ -23,6 +25,7 @@ where W: Write + Seek, { inner: WaveChunkWriter, + write_buffer: Vec, } impl AudioFrameWriter @@ -30,36 +33,74 @@ where W: Write + Seek, { fn new(inner: WaveChunkWriter) -> Self { - AudioFrameWriter { inner } - } - - fn write_integer_frames_to_buffer(&self, from_frames: &[i32], to_buffer: &mut [u8]) -> () { - assert!( - from_frames.len() % self.inner.inner.format.channel_count as usize == 0, - "frames buffer does not contain a number of samples % channel_count == 0" - ); - self.inner.inner.format.pack_frames(&from_frames, to_buffer); - () + AudioFrameWriter { + inner, + write_buffer: Vec::new(), + } } /// Write interleaved samples in `buffer` /// - /// # Panics - /// - /// This function will panic if `buffer.len()` modulo the Wave file's channel count - /// is not zero. - pub fn write_integer_frames(&mut self, buffer: &[i32]) -> Result { - let mut write_buffer = self - .inner - .inner - .format - .create_raw_buffer(buffer.len() / self.inner.inner.format.channel_count as usize); + /// The writer will convert from the buffer's sample type into the file's sample type. + /// Note that no dithering will be applied during sample type conversion, + /// if dithering is required then it will need to be applied manually. + pub fn write_frames(&mut self, buffer: &[S]) -> Result<(), Error> + where + S: Sample, + { + let format = &self.inner.inner.format; + let channel_count = format.channel_count as usize; - self.write_integer_frames_to_buffer(&buffer, &mut write_buffer); + if buffer.len() % channel_count != 0 { + return Err(Error::InvalidBufferSize { + buffer_size: buffer.len(), + channel_count: format.channel_count, + }); + } - self.inner.write(&write_buffer)?; - self.inner.flush()?; - Ok(write_buffer.len() as u64 / self.inner.inner.format.channel_count as u64) + let frame_count = buffer.len() / channel_count; + let write_buffer_size = format.block_alignment as usize * frame_count; + self.write_buffer.resize(write_buffer_size, 0); + + let mut write_cursor = Cursor::new(&mut self.write_buffer); + + let common_format = format.common_format(); + let bits_per_sample = format.bits_per_sample; + + match (common_format, bits_per_sample) { + (_, 8) => { + for sample in buffer { + write_cursor.write_u8(sample.to_sample())? + } + } + (_, 16) => { + for sample in buffer { + write_cursor.write_i16::(sample.to_sample())? + } + } + (_, 24) => { + for sample in buffer { + write_cursor.write_i24::(sample.to_sample::().inner())? + } + } + (CommonFormat::IntegerPCM, 32) => { + for sample in buffer { + write_cursor.write_i32::(sample.to_sample())? + } + } + (CommonFormat::IeeeFloatPCM, 32) => { + for sample in buffer { + write_cursor.write_f32::(sample.to_sample())? + } + } + (_, _) => panic!( + "Unrecognized format, bits per sample {}, channels {}, sample format {:?}", + bits_per_sample, channel_count, common_format + ), + } + + self.inner.write_all(&self.write_buffer)?; + Ok(()) } /// Finish writing audio frames and unwrap the inner `WaveWriter`. @@ -110,14 +151,14 @@ where fn end(mut self) -> Result, Error> { if self.length % 2 == 1 { self.inner.inner.seek(SeekFrom::End(0))?; - self.inner.inner.write(&[0u8])?; + self.inner.inner.write_u8(0)?; self.inner.increment_form_length(1)?; } Ok(self.inner) } fn increment_chunk_length(&mut self, amount: u64) -> Result<(), std::io::Error> { - self.length = self.length + amount; + self.length += amount; if !self.inner.is_rf64 { self.inner .inner @@ -125,22 +166,20 @@ where self.inner .inner .write_u32::(self.length as u32)?; - } else { - if self.ident == DATA_SIG { - let data_chunk_64bit_field_offset = 8 + 4 + 8 + 8; - self.inner - .inner - .seek(SeekFrom::Start(self.content_start_pos - 4))?; - self.inner.inner.write_u32::(0xFFFF_FFFF)?; - // this only need to happen once, not every time we increment + } else if self.ident == DATA_SIG { + let data_chunk_64bit_field_offset = 8 + 4 + 8 + 8; + self.inner + .inner + .seek(SeekFrom::Start(self.content_start_pos - 4))?; + self.inner.inner.write_u32::(0xFFFF_FFFF)?; + // this only need to happen once, not every time we increment - self.inner - .inner - .seek(SeekFrom::Start(data_chunk_64bit_field_offset))?; - self.inner.inner.write_u64::(self.length)?; - } else { - todo!("FIXME RF64 wave writing is not yet supported for chunks other than `data`") - } + self.inner + .inner + .seek(SeekFrom::Start(data_chunk_64bit_field_offset))?; + self.inner.inner.write_u64::(self.length)?; + } else { + todo!("FIXME RF64 wave writing is not yet supported for chunks other than `data`") } Ok(()) @@ -198,9 +237,9 @@ where /// /// let mut frame_writer = w.audio_frame_writer().unwrap(); /// -/// frame_writer.write_integer_frames(&[0i32]).unwrap(); -/// frame_writer.write_integer_frames(&[0i32]).unwrap(); -/// frame_writer.write_integer_frames(&[0i32]).unwrap(); +/// frame_writer.write_frames(&[0i32]).unwrap(); +/// frame_writer.write_frames(&[0i32]).unwrap(); +/// frame_writer.write_frames(&[0i32]).unwrap(); /// frame_writer.end().unwrap(); /// ``` /// @@ -249,7 +288,7 @@ impl WaveWriter> { pub fn create>(path: P, format: WaveFmt) -> Result { let f = File::create(path)?; let b = BufWriter::new(f); - Ok(Self::new(b, format)?) + Self::new(b, format) } } @@ -257,7 +296,7 @@ impl WaveWriter { /// Creare a new Wave file with unbuffered IO at `path` pub fn create_unbuffered>(path: P, format: WaveFmt) -> Result { let f = File::create(path)?; - Ok(Self::new(f, format)?) + Self::new(f, format) } } @@ -299,11 +338,11 @@ where self.inner.write_fourcc(ident)?; assert!(data.len() < u32::MAX as usize); self.inner.write_u32::(data.len() as u32)?; - self.inner.write(data)?; + self.inner.write_all(data)?; if data.len() % 2 == 0 { self.increment_form_length(8 + data.len() as u64)?; } else { - self.inner.write(&[0u8])?; + self.inner.write_u8(0)?; self.increment_form_length(8 + data.len() as u64 + 1)?; } Ok(()) @@ -317,7 +356,7 @@ where pub fn write_broadcast_metadata(&mut self, bext: &Bext) -> Result<(), Error> { //FIXME Implement re-writing let mut c = Cursor::new(vec![0u8; 0]); - c.write_bext(&bext)?; + c.write_bext(bext)?; let buf = c.into_inner(); self.write_chunk(BEXT_SIG, &buf)?; Ok(()) @@ -326,13 +365,13 @@ where /// Write iXML metadata pub fn write_ixml(&mut self, ixml: &[u8]) -> Result<(), Error> { //FIXME Implement re-writing - self.write_chunk(IXML_SIG, &ixml) + self.write_chunk(IXML_SIG, ixml) } /// Write axml/ADM metadata pub fn write_axml(&mut self, axml: &[u8]) -> Result<(), Error> { //FIXME Implement re-writing - self.write_chunk(AXML_SIG, &axml) + self.write_chunk(AXML_SIG, axml) } /// Write a `JUNK` filler chunk @@ -353,7 +392,7 @@ where let to_add = framing - (lip % framing) - 16; let mut chunk = self.chunk(ELM1_SIG)?; let buf = vec![0u8; to_add as usize]; - chunk.write(&buf)?; + chunk.write_all(&buf)?; let closed = chunk.end()?; let inner = closed.chunk(DATA_SIG)?; Ok(AudioFrameWriter::new(inner)) @@ -383,7 +422,7 @@ where /// Add `amount` to the RIFF/RF64 form length fn increment_form_length(&mut self, amount: u64) -> Result<(), std::io::Error> { - self.form_length = self.form_length + amount; + self.form_length += amount; if self.is_rf64 { self.inner.seek(SeekFrom::Start(8 + 4 + 8))?; self.inner.write_u64::(self.form_length)?; @@ -436,9 +475,9 @@ fn test_write_audio() { let mut frame_writer = w.audio_frame_writer().unwrap(); - frame_writer.write_integer_frames(&[0i32]).unwrap(); - frame_writer.write_integer_frames(&[0i32]).unwrap(); - frame_writer.write_integer_frames(&[0i32]).unwrap(); + frame_writer.write_frames(&[0i32]).unwrap(); + frame_writer.write_frames(&[0i32]).unwrap(); + frame_writer.write_frames(&[0i32]).unwrap(); frame_writer.end().unwrap(); @@ -504,14 +543,14 @@ fn test_write_bext() { let mut frame_writer = w.audio_frame_writer().unwrap(); - frame_writer.write_integer_frames(&[0i32]).unwrap(); - frame_writer.write_integer_frames(&[0i32]).unwrap(); - frame_writer.write_integer_frames(&[0i32]).unwrap(); + frame_writer.write_frames(&[0i32]).unwrap(); + frame_writer.write_frames(&[0i32]).unwrap(); + frame_writer.write_frames(&[0i32]).unwrap(); frame_writer.end().unwrap(); } -// NOTE! This test of RF64 writing takes several minutes to complete. +// NOTE! This test of RF64 writing takes several minutes to complete in debug builds #[test] fn test_create_rf64() { use super::fourcc::ReadFourCC; @@ -530,7 +569,7 @@ fn test_create_rf64() { let mut af = w.audio_frame_writer().unwrap(); for _ in 0..(four_and_a_half_hours_of_frames * format.channel_count as u64 / buflen) { - af.write_integer_frames(&buf).unwrap(); + af.write_frames(&buf).unwrap(); } af.end().unwrap(); diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 85bd525..ae49def 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -3,6 +3,7 @@ extern crate bwavfile; use bwavfile::ChannelMask; use bwavfile::Error; use bwavfile::WaveReader; +use bwavfile::I24; #[test] fn test_open() { @@ -80,16 +81,16 @@ fn test_read() { let path = "tests/media/audacity_16bit.wav"; let mut w = WaveReader::open(path).expect("Failure opening test file"); - let mut buffer = w.format().unwrap().create_frame_buffer(1); + let mut buffer = w.format().unwrap().create_frame_buffer::(1); let mut reader = w.audio_frame_reader().unwrap(); - assert_eq!(reader.read_integer_frame(&mut buffer).unwrap(), 1); - assert_eq!(buffer[0], -2823_i32); - assert_eq!(reader.read_integer_frame(&mut buffer).unwrap(), 1); - assert_eq!(buffer[0], 2012_i32); - assert_eq!(reader.read_integer_frame(&mut buffer).unwrap(), 1); - assert_eq!(buffer[0], 4524_i32); + assert_eq!(reader.read_frames(&mut buffer).unwrap(), 1); + assert_eq!(buffer[0], -2823_i16); + assert_eq!(reader.read_frames(&mut buffer).unwrap(), 1); + assert_eq!(buffer[0], 2012_i16); + assert_eq!(reader.read_frames(&mut buffer).unwrap(), 1); + assert_eq!(buffer[0], 4524_i16); } #[test] @@ -97,21 +98,21 @@ fn test_locate_multichannel_read() { let path = "tests/media/ff_pink.wav"; let mut w = WaveReader::open(path).expect("Failure opening test file"); - let mut buffer = w.format().unwrap().create_frame_buffer(1); + let mut buffer = w.format().unwrap().create_frame_buffer::(1); let mut reader = w.audio_frame_reader().unwrap(); - assert_eq!(reader.read_integer_frame(&mut buffer).unwrap(), 1); - assert_eq!(buffer[0], 332702_i32); - assert_eq!(buffer[1], 3258791_i32); - assert_eq!(reader.read_integer_frame(&mut buffer).unwrap(), 1); - assert_eq!(buffer[0], -258742_i32); // 0x800000 = 8388608 // 8129866 - 8388608 - assert_eq!(buffer[1], 0x0D7EF9_i32); + assert_eq!(reader.read_frames(&mut buffer).unwrap(), 1); + assert_eq!(buffer[0], I24::from(332702)); + assert_eq!(buffer[1], I24::from(3258791)); + assert_eq!(reader.read_frames(&mut buffer).unwrap(), 1); + assert_eq!(buffer[0], I24::from(-258742)); // 0x800000 = 8388608 // 8129866 - 8388608 + assert_eq!(buffer[1], I24::from(0x0D7EF9)); assert_eq!(reader.locate(100).unwrap(), 100); - assert_eq!(reader.read_integer_frame(&mut buffer).unwrap(), 1); - assert_eq!(buffer[0], 0x109422_i32); - assert_eq!(buffer[1], -698901_i32); // 7689707 - 8388608 + assert_eq!(reader.read_frames(&mut buffer).unwrap(), 1); + assert_eq!(buffer[0], I24::from(0x109422)); + assert_eq!(buffer[1], I24::from(-698901)); // 7689707 - 8388608 } #[test]