From 369d261ba09ca983625075b628888576b35b1d7b Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 01/31] Fix build warnings --- src/cue.rs | 6 +++--- src/fmt.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cue.rs b/src/cue.rs index 5d1d8fd..98651b5 100644 --- a/src/cue.rs +++ b/src/cue.rs @@ -186,9 +186,9 @@ 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(); diff --git a/src/fmt.rs b/src/fmt.rs index cf3b19d..e72a89f 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -457,10 +457,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!() } } From e1b93b24ad057ac294904770772064f17ea6be68 Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 02/31] Run cargo format --- src/cue.rs | 5 +++-- src/fmt.rs | 36 ++++++++++++++++-------------------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/cue.rs b/src/cue.rs index 98651b5..cb06904 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; diff --git a/src/fmt.rs b/src/fmt.rs index e72a89f..2e588c7 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -261,28 +261,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: 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: UUID_PCM, + }), + ), }; let (tag, extformat) = result; From 313f5408cab7fa5facb126ac3961fb1d1fd4dd9f Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 03/31] Update the comment in wave-deinter.rs --- examples/wave-deinter.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/wave-deinter.rs b/examples/wave-deinter.rs index 29fc8f2..464eaa4 100644 --- a/examples/wave-deinter.rs +++ b/examples/wave-deinter.rs @@ -1,8 +1,8 @@ -//! 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::path::Path; From 1234c897a32250dc00f655d2e23d8bae8e901323 Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 04/31] clippy: Use stream_position() to get current position --- src/wavereader.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wavereader.rs b/src/wavereader.rs index 7a7f78f..317e733 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; @@ -116,7 +116,7 @@ impl AudioFrameReader { let framed_bits_per_sample = self.format.block_alignment * 8 / self.format.channel_count; - let tell = self.inner.seek(Current(0))?; + let tell = self.inner.stream_position()?; if (tell - self.start) < self.length { for n in 0..(self.format.channel_count as usize) { @@ -145,7 +145,7 @@ impl AudioFrameReader { let framed_bits_per_sample = self.format.block_alignment * 8 / self.format.channel_count; - let tell = self.inner.seek(Current(0))?; + let tell = self.inner.stream_position()?; if (tell - self.start) < self.length { for n in 0..(self.format.channel_count as usize) { From 04d282ccd6b7ef525d2b01cc3c8f77317c07d51b Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 05/31] clippy: Use Avoid unnecessary indexing in for loops --- src/fmt.rs | 18 +++++++++--------- src/wavereader.rs | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/fmt.rs b/src/fmt.rs index 2e588c7..f29aabc 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -328,12 +328,12 @@ impl WaveFmt { "frames buffer does not contain a number of samples % channel_count == 0" ); - for n in 0..from_frames.len() { + for frame in from_frames { 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(), + (0..=8,8) => write_cursor.write_u8((frame + 0x80) as u8 ).unwrap(), // EBU 3285 §A2.2 + (9..=16,16) => write_cursor.write_i16::(*frame as i16).unwrap(), + (10..=24,24) => write_cursor.write_i24::(*frame).unwrap(), + (25..=32,32) => write_cursor.write_i32::(*frame).unwrap(), (b,_)=> panic!("Unrecognized integer format, bits per sample {}, channels {}, block_alignment {}", b, self.channel_count, self.block_alignment) } @@ -344,8 +344,8 @@ impl WaveFmt { /// Read bytes into frames 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(), @@ -421,8 +421,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(), diff --git a/src/wavereader.rs b/src/wavereader.rs index 317e733..79ba81b 100644 --- a/src/wavereader.rs +++ b/src/wavereader.rs @@ -119,8 +119,8 @@ impl AudioFrameReader { let tell = self.inner.stream_position()?; 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) { + for sample in buffer.iter_mut().take(self.format.channel_count as usize) { + *sample = 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::()?, @@ -148,8 +148,8 @@ impl AudioFrameReader { let tell = self.inner.stream_position()?; 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) { + for sample in buffer.iter_mut().take(self.format.channel_count as usize) { + *sample = 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) From bbd84b7bbbb9dfe9f9e77b4b84d5298e303746ea Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 06/31] clippy: Remove unnecessary uses of `?` --- src/wavereader.rs | 9 ++++----- src/wavewriter.rs | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/wavereader.rs b/src/wavereader.rs index 79ba81b..ad0465f 100644 --- a/src/wavereader.rs +++ b/src/wavereader.rs @@ -216,7 +216,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 +224,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) } } @@ -279,12 +278,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. diff --git a/src/wavewriter.rs b/src/wavewriter.rs index a620143..0e97103 100644 --- a/src/wavewriter.rs +++ b/src/wavewriter.rs @@ -249,7 +249,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 +257,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) } } From 9eb655cd27ddee2891791d3f97769ef7b4b9e25f Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 07/31] clippy: Remove unnecessary use of `return` --- src/parser.rs | 50 +++++++++++++++++++---------------------------- src/wavereader.rs | 2 +- 2 files changed, 21 insertions(+), 31 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index f7cd87c..ef2c34d 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,38 @@ 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: error.into(), + }), + State::Error, + ), } } } diff --git a/src/wavereader.rs b/src/wavereader.rs index ad0465f..9b4e038 100644 --- a/src/wavereader.rs +++ b/src/wavereader.rs @@ -269,7 +269,7 @@ impl WaveReader { /// Unwrap the inner reader. pub fn into_inner(self) -> R { - return self.inner; + self.inner } /// From b6c20ae41051f59aca076d73571e3349f47a34f0 Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 08/31] clippy: Remove unnecessary casts --- src/chunks.rs | 8 ++------ src/cue.rs | 16 +++++----------- src/wavereader.rs | 2 +- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/chunks.rs b/src/chunks.rs index 1e78802..f116b0c 100644 --- a/src/chunks.rs +++ b/src/chunks.rs @@ -34,7 +34,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)?; @@ -143,11 +143,7 @@ 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(); + let trimmed: Vec = buffer.iter().take_while(|c| **c != 0_u8).cloned().collect(); Ok(ASCII .decode(&trimmed, DecoderTrap::Ignore) .expect("Error decoding text")) diff --git a/src/cue.rs b/src/cue.rs index cb06904..884df4a 100644 --- a/src/cue.rs +++ b/src/cue.rs @@ -69,9 +69,7 @@ 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_u32::(self.cue_point_id).unwrap(); writer.write(&self.text).unwrap(); writer.into_inner() } @@ -83,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 }, @@ -112,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 }, @@ -162,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 { @@ -292,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") diff --git a/src/wavereader.rs b/src/wavereader.rs index 9b4e038..18bec67 100644 --- a/src/wavereader.rs +++ b/src/wavereader.rs @@ -607,7 +607,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)); From 69895207431bf33b949257fd5d63b2167f04b678 Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 09/31] clippy: Use Vec::get() instead of iter().nth() --- src/wavereader.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wavereader.rs b/src/wavereader.rs index 18bec67..26b77c9 100644 --- a/src/wavereader.rs +++ b/src/wavereader.rs @@ -622,7 +622,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 }) From 566ad072472a0f9cdd796ab0f1de5333278c11eb Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 10/31] clippy: Remove unnecessary use of .into() --- src/wavereader.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wavereader.rs b/src/wavereader.rs index 26b77c9..d64b65c 100644 --- a/src/wavereader.rs +++ b/src/wavereader.rs @@ -590,7 +590,7 @@ impl WaveReader { .map_err(|e| ParserError::IOError(e)) } Err(ParserError::ChunkMissing { signature: _ }) => Ok(0), - Err(any) => Err(any.into()), + Err(any) => Err(any), } } From 651009c96ae41ad6db43a570ecdbdfa085fc07ef Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 11/31] clippy: Avoid making unnecessary references --- src/cue.rs | 6 +++--- src/wavereader.rs | 6 ++---- src/wavewriter.rs | 10 +++++----- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/cue.rs b/src/cue.rs index 884df4a..da61478 100644 --- a/src/cue.rs +++ b/src/cue.rs @@ -298,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") } @@ -319,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 { diff --git a/src/wavereader.rs b/src/wavereader.rs index d64b65c..92af0f3 100644 --- a/src/wavereader.rs +++ b/src/wavereader.rs @@ -579,15 +579,13 @@ 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(|e| ParserError::IOError(e)) } Err(ParserError::ChunkMissing { signature: _ }) => Ok(0), Err(any) => Err(any), diff --git a/src/wavewriter.rs b/src/wavewriter.rs index 0e97103..949053a 100644 --- a/src/wavewriter.rs +++ b/src/wavewriter.rs @@ -38,7 +38,7 @@ where 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); + self.inner.inner.format.pack_frames(from_frames, to_buffer); () } @@ -55,7 +55,7 @@ where .format .create_raw_buffer(buffer.len() / self.inner.inner.format.channel_count as usize); - self.write_integer_frames_to_buffer(&buffer, &mut write_buffer); + self.write_integer_frames_to_buffer(buffer, &mut write_buffer); self.inner.write(&write_buffer)?; self.inner.flush()?; @@ -317,7 +317,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 +326,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 From 4d81ef36cd5c79920059e89e7f89bbb34b17ae73 Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 12/31] clippy: Use the += operator --- src/wavewriter.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wavewriter.rs b/src/wavewriter.rs index 949053a..e0a6f7b 100644 --- a/src/wavewriter.rs +++ b/src/wavewriter.rs @@ -117,7 +117,7 @@ where } 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 @@ -383,7 +383,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)?; From fc3b4117174dc4e262945439228de01a08645b24 Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 13/31] clippy: Use .read_exact() when reading a fixed number of bytes --- src/chunks.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chunks.rs b/src/chunks.rs index f116b0c..e6d2788 100644 --- a/src/chunks.rs +++ b/src/chunks.rs @@ -142,7 +142,7 @@ where fn read_bext_string_field(&mut self, length: usize) -> Result { let mut buffer: Vec = vec![0; length]; - self.read(&mut buffer)?; + self.read_exact(&mut buffer)?; let trimmed: Vec = buffer.iter().take_while(|c| **c != 0_u8).cloned().collect(); Ok(ASCII .decode(&trimmed, DecoderTrap::Ignore) @@ -164,7 +164,7 @@ where }, umid: { let mut buf = [0u8; 64]; - self.read(&mut buf)?; + self.read_exact(&mut buf)?; if version > 0 { Some(buf) } else { From 4e370c57c1b8ec80af797c083a6536b0ca41c069 Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 14/31] clippy: Use .write_all() when writing a slice --- src/chunks.rs | 2 +- src/cue.rs | 10 +++++----- src/wavewriter.rs | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/chunks.rs b/src/chunks.rs index e6d2788..6d1f624 100644 --- a/src/chunks.rs +++ b/src/chunks.rs @@ -46,7 +46,7 @@ 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(()) } diff --git a/src/cue.rs b/src/cue.rs index da61478..41f8027 100644 --- a/src/cue.rs +++ b/src/cue.rs @@ -70,7 +70,7 @@ impl RawLabel { 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() } @@ -99,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() } @@ -141,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() } @@ -192,7 +192,7 @@ impl RawAdtlMember { }; 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(); } @@ -204,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() } diff --git a/src/wavewriter.rs b/src/wavewriter.rs index e0a6f7b..cf251fd 100644 --- a/src/wavewriter.rs +++ b/src/wavewriter.rs @@ -57,7 +57,7 @@ where self.write_integer_frames_to_buffer(buffer, &mut write_buffer); - self.inner.write(&write_buffer)?; + self.inner.write_all(&write_buffer)?; self.inner.flush()?; Ok(write_buffer.len() as u64 / self.inner.inner.format.channel_count as u64) } @@ -299,7 +299,7 @@ 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 { @@ -353,7 +353,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)) From 7c0177802902509124f829a277b90bce4a252fa8 Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 15/31] clippy: Use .write_u8() when writing a single value --- src/wavewriter.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wavewriter.rs b/src/wavewriter.rs index cf251fd..955688e 100644 --- a/src/wavewriter.rs +++ b/src/wavewriter.rs @@ -110,7 +110,7 @@ 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) @@ -303,7 +303,7 @@ where 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(()) From cf5ec121da8ab9660aa1dbfc7056c2b562627d06 Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 16/31] clippy: Avoid unnecessary closures --- src/fmt.rs | 2 +- src/wavereader.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fmt.rs b/src/fmt.rs index f29aabc..5892638 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -105,7 +105,7 @@ impl ChannelMask { (0..18) .map(|i| 1 << i) .filter(|mask| mask & input_mask > 0) - .map(|mask| Into::::into(mask)) + .map(ChannelMask::from) .collect() } } diff --git a/src/wavereader.rs b/src/wavereader.rs index 92af0f3..73989b2 100644 --- a/src/wavereader.rs +++ b/src/wavereader.rs @@ -585,7 +585,7 @@ impl WaveReader { Ok((start, length)) => { buffer.resize(length as usize, 0x0); self.inner.seek(SeekFrom::Start(start))?; - self.inner.read(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), From f6f4869b5baf98e3f547aff1ca39511a40c62a3f Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 17/31] clippy: Avoid () as return type --- src/fmt.rs | 5 ++--- src/wavewriter.rs | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/fmt.rs b/src/fmt.rs index 5892638..be34ff6 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -320,7 +320,7 @@ impl WaveFmt { } /// Write frames into a byte vector - pub fn pack_frames(&self, from_frames: &[i32], into_bytes: &mut [u8]) -> () { + pub fn pack_frames(&self, from_frames: &[i32], into_bytes: &mut [u8]) { let mut write_cursor = Cursor::new(into_bytes); assert!( @@ -338,11 +338,10 @@ impl WaveFmt { 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 frame in into_frames { *frame = match (self.valid_bits_per_sample(), self.bits_per_sample) { diff --git a/src/wavewriter.rs b/src/wavewriter.rs index 955688e..4190116 100644 --- a/src/wavewriter.rs +++ b/src/wavewriter.rs @@ -33,13 +33,12 @@ where AudioFrameWriter { inner } } - fn write_integer_frames_to_buffer(&self, from_frames: &[i32], to_buffer: &mut [u8]) -> () { + 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); - () } /// Write interleaved samples in `buffer` From 22e8dc79d19d2a753bc55834e902de2ad4635534 Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 18/31] clippy: Use a slice argument instead of the owned equivalent --- src/chunks.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/chunks.rs b/src/chunks.rs index 6d1f624..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>; } @@ -51,13 +47,9 @@ where 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(); From ceb8c4371e72a4260389d0dfc70056450b0d79cb Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 19/31] clippy: Avoid unnecessary conversion --- src/parser.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index ef2c34d..19d434f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -288,12 +288,7 @@ impl Parser { fn advance(&mut self) -> (Option, State) { match self.handle_state() { Ok((event, state)) => (event, state), - Err(error) => ( - Some(Event::Failed { - error: error.into(), - }), - State::Error, - ), + Err(error) => (Some(Event::Failed { error }), State::Error), } } } From 86ffd4310aa584bf19685799bc6c2b30c153fcd4 Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 20/31] clippy: Prefer `if let Some(_)` over Option::map --- src/cue.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/cue.rs b/src/cue.rs index 41f8027..9c6b727 100644 --- a/src/cue.rs +++ b/src/cue.rs @@ -344,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) }, ) From 8d34a517fcbdf0fa1a8ee9daf24c4b086991319c Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 21/31] clippy: Don't warn about the LUFS type name --- src/bext.rs | 1 + 1 file changed, 1 insertion(+) 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; From 0c46a0e21f248852293535fadeefff44538297ba Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 22/31] clippy: Remove unnecessary import --- src/errors.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 3d073fb..378efd3 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 { From 60a3eac0725bb1389908265caed569e8838cb14c Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 23/31] clippy: Collapse nested else/if into else if --- src/wavewriter.rs | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/wavewriter.rs b/src/wavewriter.rs index 4190116..ef3aaff 100644 --- a/src/wavewriter.rs +++ b/src/wavewriter.rs @@ -124,22 +124,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(()) From 0074e13bff48ca403b23523a9ec6560ffce436fb Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 24/31] Avoid flushing output on each write --- src/wavewriter.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wavewriter.rs b/src/wavewriter.rs index ef3aaff..2e04779 100644 --- a/src/wavewriter.rs +++ b/src/wavewriter.rs @@ -57,7 +57,6 @@ where self.write_integer_frames_to_buffer(buffer, &mut write_buffer); self.inner.write_all(&write_buffer)?; - self.inner.flush()?; Ok(write_buffer.len() as u64 / self.inner.inner.format.channel_count as u64) } From 7290d5ec81c7504d74fb76b20f529bda5470291e Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 25/31] Rework the deinter example, create writers before reading through the input file --- examples/wave-deinter.rs | 41 +++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/examples/wave-deinter.rs b/examples/wave-deinter.rs index 464eaa4..ffa57de 100644 --- a/examples/wave-deinter.rs +++ b/examples/wave-deinter.rs @@ -71,29 +71,32 @@ fn process_file(infile: &str, delim: &str, numeric_channel_names: bool) -> Resul 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 mut output_wave_writers = channel_desc + .iter() + .enumerate() + .map(|(n, channel)| { + 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(); - 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(); + WaveWriter::create(&outfile_name, ouptut_format) + .expect("Failed to create new file") + .audio_frame_writer() + }) + .collect::, _>>()?; - println!("Will create file {}", outfile_name); - - let output_file = - WaveWriter::create(&outfile_name, ouptut_format).expect("Failed to create new file"); - - 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])?; + let mut buffer = input_format.create_frame_buffer(1); + while input_wave_reader.read_integer_frame(&mut buffer)? > 0 { + for (n, writer) in output_wave_writers.iter_mut().enumerate() { + writer.write_integer_frames(&buffer[n..=n])?; } + } - output_wave_writer.end()?; - input_wave_reader.locate(0)?; + for writer in output_wave_writers.drain(..) { + writer.end()?; } Ok(()) From 34007789910ece36a2ea662811d547839b1dfcb4 Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 26/31] Introduce generic read_frames and write_frames functions --- Cargo.lock | 9 +++ Cargo.toml | 1 + examples/blits.rs | 2 +- examples/wave-deinter.rs | 112 ++++++++++++++++++++++++-------- src/fmt.rs | 31 ++------- src/lib.rs | 3 + src/sample.rs | 14 ++++ src/wavereader.rs | 131 +++++++++++++++++++------------------- src/wavewriter.rs | 87 ++++++++++++++++++------- tests/integration_test.rs | 35 +++++----- 10 files changed, 269 insertions(+), 156 deletions(-) create mode 100644 src/sample.rs 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..6d96a1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 ffa57de..a75b4ea 100644 --- a/examples/wave-deinter.rs +++ b/examples/wave-deinter.rs @@ -4,11 +4,14 @@ //! 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,41 +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 mut output_wave_writers = channel_desc + 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(numeric_channel_names, delim, n + 1, 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(); - WaveWriter::create(&outfile_name, ouptut_format) + println!("Will create file {}", outfile_name); + + WaveWriter::create(&outfile_name, output_format) .expect("Failed to create new file") .audio_frame_writer() }) .collect::, _>>()?; - let mut buffer = input_format.create_frame_buffer(1); - while input_wave_reader.read_integer_frame(&mut buffer)? > 0 { - for (n, writer) in output_wave_writers.iter_mut().enumerate() { - writer.write_integer_frames(&buffer[n..=n])?; + let mut input_buffer = vec![S::EQUILIBRIUM; frames_per_read * channel_count]; + let mut output_buffer = vec![S::EQUILIBRIUM; frames_per_read]; + + loop { + let frames_read = reader.read_frames(&mut input_buffer)? as usize; + if frames_read == 0 { + break; + } + + 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 output_wave_writers.drain(..) { + 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!()) @@ -129,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/fmt.rs b/src/fmt.rs index be34ff6..bb6cc47 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, UUID_BFORMAT_PCM, 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)] @@ -309,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 @@ -319,27 +321,6 @@ 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 frame in from_frames { - match (self.valid_bits_per_sample(), self.bits_per_sample) { - (0..=8,8) => write_cursor.write_u8((frame + 0x80) as u8 ).unwrap(), // EBU 3285 §A2.2 - (9..=16,16) => write_cursor.write_i16::(*frame as i16).unwrap(), - (10..=24,24) => write_cursor.write_i24::(*frame).unwrap(), - (25..=32,32) => write_cursor.write_i32::(*frame).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]) { let mut rdr = Cursor::new(from_bytes); diff --git a/src/lib.rs b/src/lib.rs index 0c87342..8295079 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,8 @@ mod chunks; mod cue; mod fmt; +mod sample; + mod wavereader; mod wavewriter; @@ -60,5 +62,6 @@ pub use common_format::CommonFormat; 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/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 73989b2..f0e3320 100644 --- a/src/wavereader.rs +++ b/src/wavereader.rs @@ -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 { + /// 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 channel_count = self.format.channel_count as usize; + let common_format = self.format.common_format(); + let bits_per_sample = self.format.bits_per_sample; + 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() % channel_count == 0, + "read_frames was called with a mis-sized buffer, expected a multiple of {}, was {}", + channel_count, buffer.len() ); - let framed_bits_per_sample = self.format.block_alignment * 8 / 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; - let tell = self.inner.stream_position()?; + 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 + ), + }?; - if (tell - self.start) < self.length { - for sample in buffer.iter_mut().take(self.format.channel_count as usize) { - *sample = 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) - } + 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.stream_position()?; - - if (tell - self.start) < self.length { - for sample in buffer.iter_mut().take(self.format.channel_count as usize) { - *sample = 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); diff --git a/src/wavewriter.rs b/src/wavewriter.rs index 2e04779..c3aa0c5 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; @@ -33,28 +35,67 @@ where 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); - } - /// 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 { + pub fn write_frames(&mut self, buffer: &[S]) -> Result + where + S: Sample, + { + let format = &self.inner.inner.format; + let channel_count = format.channel_count as usize; + + assert!( + buffer.len() % channel_count == 0, + "frames buffer does not contain a number of samples % channel_count == 0" + ); + let mut write_buffer = self .inner .inner .format - .create_raw_buffer(buffer.len() / self.inner.inner.format.channel_count as usize); + .create_raw_buffer(buffer.len() / channel_count); - self.write_integer_frames_to_buffer(buffer, &mut write_buffer); + let into_bytes: &mut [u8] = &mut write_buffer; + let mut write_cursor = Cursor::new(into_bytes); + + 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(&write_buffer)?; Ok(write_buffer.len() as u64 / self.inner.inner.format.channel_count as u64) @@ -194,9 +235,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(); /// ``` /// @@ -432,9 +473,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(); @@ -500,14 +541,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; @@ -526,7 +567,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] From 3d1826007e9dcfd6bd102016b173154524ec5080 Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 27/31] Avoid panics when incorrect buffer sizes are provided to read/write_frames --- src/errors.rs | 6 ++++++ src/wavereader.rs | 12 ++++++------ src/wavewriter.rs | 19 ++++++++++--------- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 378efd3..7fd988f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -41,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/wavereader.rs b/src/wavereader.rs index f0e3320..9d71102 100644 --- a/src/wavereader.rs +++ b/src/wavereader.rs @@ -110,12 +110,12 @@ impl AudioFrameReader { let common_format = self.format.common_format(); let bits_per_sample = self.format.bits_per_sample; - assert!( - buffer.len() % channel_count == 0, - "read_frames was called with a mis-sized buffer, expected a multiple of {}, was {}", - channel_count, - buffer.len() - ); + 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; diff --git a/src/wavewriter.rs b/src/wavewriter.rs index c3aa0c5..c5df769 100644 --- a/src/wavewriter.rs +++ b/src/wavewriter.rs @@ -37,21 +37,22 @@ where /// 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_frames(&mut self, buffer: &[S]) -> Result + /// 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; - assert!( - buffer.len() % channel_count == 0, - "frames buffer does not contain a number of samples % channel_count == 0" - ); + if buffer.len() % channel_count != 0 { + return Err(Error::InvalidBufferSize { + buffer_size: buffer.len(), + channel_count: format.channel_count, + }); + } let mut write_buffer = self .inner From bd09ed207ab7eb38d1505b86182fec97de4337d4 Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 28/31] Re-use the write buffer between calls to write_frames This resulted in a ~10% speedup in test runs of the deinter example. --- src/wavewriter.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/wavewriter.rs b/src/wavewriter.rs index c5df769..6c84bef 100644 --- a/src/wavewriter.rs +++ b/src/wavewriter.rs @@ -25,6 +25,7 @@ where W: Write + Seek, { inner: WaveChunkWriter, + write_buffer: Vec, } impl AudioFrameWriter @@ -32,7 +33,10 @@ where W: Write + Seek, { fn new(inner: WaveChunkWriter) -> Self { - AudioFrameWriter { inner } + AudioFrameWriter { + inner, + write_buffer: Vec::new(), + } } /// Write interleaved samples in `buffer` @@ -54,14 +58,11 @@ where }); } - let mut write_buffer = self - .inner - .inner - .format - .create_raw_buffer(buffer.len() / channel_count); + 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 into_bytes: &mut [u8] = &mut write_buffer; - let mut write_cursor = Cursor::new(into_bytes); + let mut write_cursor = Cursor::new(&mut self.write_buffer); let common_format = format.common_format(); let bits_per_sample = format.bits_per_sample; @@ -98,8 +99,8 @@ where ), } - self.inner.write_all(&write_buffer)?; - Ok(write_buffer.len() as u64 / self.inner.inner.format.channel_count as u64) + self.inner.write_all(&self.write_buffer)?; + Ok(()) } /// Finish writing audio frames and unwrap the inner `WaveWriter`. From 72fea1a49357e66422ada18ed06295e968481cf9 Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Fri, 12 May 2023 16:52:06 +0200 Subject: [PATCH 29/31] Expose and rename the supported wave tags and UUIDs --- src/common_format.rs | 46 ++++++++++++++++++++++---------------------- src/fmt.rs | 8 ++++---- src/lib.rs | 5 ++++- 3 files changed, 31 insertions(+), 28 deletions(-) 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/fmt.rs b/src/fmt.rs index bb6cc47..60654c5 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -1,4 +1,4 @@ -use crate::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; @@ -240,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, }), } } @@ -268,7 +268,7 @@ impl WaveFmt { Some(WaveFmtExtended { valid_bits_per_sample: bits_per_sample, channel_mask: ch, - type_guid: UUID_PCM, + type_guid: WAVE_UUID_PCM, }), ), 0b0100 => (0x0001, None), @@ -278,7 +278,7 @@ impl WaveFmt { Some(WaveFmtExtended { valid_bits_per_sample: bits_per_sample, channel_mask: ch, - type_guid: UUID_PCM, + type_guid: WAVE_UUID_PCM, }), ), }; diff --git a/src/lib.rs b/src/lib.rs index 8295079..0490b66 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,7 +58,10 @@ 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}; From 5592e87f0ed45318cd2df835e1b5e8c40a0c376e Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Thu, 18 May 2023 11:45:22 +0200 Subject: [PATCH 30/31] Update Cargo.toml author list --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6d96a1f..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" From eff4fe199f10db5f828977cc2303ea7627eaa692 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Mon, 22 May 2023 17:45:28 -0700 Subject: [PATCH 31/31] Update Cargo.toml Bumping version 2.0.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 177a50d..2fe5329 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bwavfile" -version = "1.1.0" +version = "2.0.0" authors = ["Jamie Hardt ", "Ian Hobson "] edition = "2018" license = "MIT"