From cbfcce235c0f3f572475ddfeebaec7e2540ee45e Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 27 Dec 2020 11:34:12 -0800 Subject: [PATCH] Frame writing methods to make go faster --- src/fmt.rs | 8 ++++ src/wavewriter.rs | 105 +++++++++++++++++++++++++++++----------------- 2 files changed, 74 insertions(+), 39 deletions(-) diff --git a/src/fmt.rs b/src/fmt.rs index 1c1b53b..c789c1a 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -182,6 +182,14 @@ pub struct WaveFmt { impl WaveFmt { + pub fn valid_bits_per_sample(&self) -> u16 { + if let Some(ext) = self.extended_format { + ext.valid_bits_per_sample + } else { + self.bits_per_sample + } + } + /// Create a new integer PCM format for a monoaural audio stream. pub fn new_pcm_mono(sample_rate: u32, bits_per_sample: u16) -> Self { Self::new_pcm_multichannel(sample_rate, bits_per_sample, 0x4) diff --git a/src/wavewriter.rs b/src/wavewriter.rs index afe598f..cdec19a 100644 --- a/src/wavewriter.rs +++ b/src/wavewriter.rs @@ -1,5 +1,5 @@ use std::fs::File; -use std::io::{Write,Seek,SeekFrom}; +use std::io::{Write,Seek,SeekFrom,Cursor}; use super::Error; use super::fourcc::{FourCC, WriteFourCC, RIFF_SIG, RF64_SIG, DS64_SIG, @@ -16,35 +16,60 @@ use byteorder::WriteBytesExt; /// /// pub struct AudioFrameWriter where W: Write + Seek { - inner : WaveChunkWriter + inner : WaveChunkWriter, + framed_bits_per_sample : u16, + bits_per_sample: u16, + channel_count: u16, + block_alignment: u16, } impl AudioFrameWriter where W: Write + Seek { - /// Write one audio frame. - /// - pub fn write_integer_frame(&mut self, buffer: &[i32]) -> Result { - let format = self.inner.inner.format; - assert!(buffer.len() as u16 == format.channel_count, - "read_integer_frame was called with a mis-sized buffer, expected {}, was {}", - format.channel_count, buffer.len()); - - let framed_bits_per_sample = format.block_alignment * 8 / format.channel_count; - - for n in 0..(format.channel_count as usize) { - match (format.bits_per_sample, framed_bits_per_sample) { - (0..=8,8) => self.inner.write_u8((buffer[n] + 0x80) as u8 )?, // EBU 3285 §A2.2 - (9..=16,16) => self.inner.write_i16::(buffer[n] as i16)?, - (10..=24,24) => self.inner.write_i24::(buffer[n])?, - (25..=32,32) => self.inner.write_i32::(buffer[n])?, - (b,_)=> panic!("Unrecognized integer format, bits per sample {}, channels {}, block_alignment {}", - b, format.channel_count, format.block_alignment) - } + fn new(inner: WaveChunkWriter) -> Self { + let fbps = inner.inner.format.bits_per_sample; + let ba = inner.inner.format.block_alignment; + let cc = inner.inner.format.channel_count; + let bps = inner.inner.format.valid_bits_per_sample(); + AudioFrameWriter { + inner, + framed_bits_per_sample: fbps, + bits_per_sample: bps, + channel_count: cc, + block_alignment: ba, } - self.inner.flush()?; - Ok(1) } + fn write_integer_frames_to_buffer(&self, from_frames :&[i32], to_buffer : &mut Vec) -> () { + let mut write_cursor = Cursor::new(to_buffer); + + 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.bits_per_sample, self.framed_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) + } + } + () + } + + pub fn write_integer_frames(&mut self, buffer: &[i32]) -> Result { + let mut write_buffer = vec![0u8; 0]; + + self.write_integer_frames_to_buffer(&buffer, &mut write_buffer); + + self.inner.write(&write_buffer)?; + self.inner.flush()?; + Ok(write_buffer.len() as u64 / self.channel_count as u64) + } + + + /// Finish writing audio frames and unwrap the inner `WaveWriter`. /// /// This method must be called when the client has finished writing audio @@ -162,9 +187,9 @@ impl Write for WaveChunkWriter where W: Write + Seek { /// /// let mut frame_writer = w.audio_frame_writer().unwrap(); /// -/// frame_writer.write_integer_frame(&[0i32]).unwrap(); -/// frame_writer.write_integer_frame(&[0i32]).unwrap(); -/// frame_writer.write_integer_frame(&[0i32]).unwrap(); +/// frame_writer.write_integer_frames(&[0i32]).unwrap(); +/// frame_writer.write_integer_frames(&[0i32]).unwrap(); +/// frame_writer.write_integer_frames(&[0i32]).unwrap(); /// frame_writer.end().unwrap(); /// ``` pub struct WaveWriter where W: Write + Seek { @@ -262,7 +287,7 @@ impl WaveWriter where W: Write + Seek { chunk.write(&buf)?; let closed = chunk.end()?; let inner = closed.chunk(DATA_SIG)?; - Ok( AudioFrameWriter { inner } ) + Ok( AudioFrameWriter::new(inner) ) } fn increment_form_length(&mut self, amount: u64) -> Result<(), std::io::Error> { @@ -320,9 +345,9 @@ fn test_write_audio() { let mut frame_writer = w.audio_frame_writer().unwrap(); - frame_writer.write_integer_frame(&[0i32]).unwrap(); - frame_writer.write_integer_frame(&[0i32]).unwrap(); - frame_writer.write_integer_frame(&[0i32]).unwrap(); + frame_writer.write_integer_frames(&[0i32]).unwrap(); + frame_writer.write_integer_frames(&[0i32]).unwrap(); + frame_writer.write_integer_frames(&[0i32]).unwrap(); frame_writer.end().unwrap(); @@ -385,9 +410,9 @@ fn test_write_bext() { let mut frame_writer = w.audio_frame_writer().unwrap(); - frame_writer.write_integer_frame(&[0i32]).unwrap(); - frame_writer.write_integer_frame(&[0i32]).unwrap(); - frame_writer.write_integer_frame(&[0i32]).unwrap(); + frame_writer.write_integer_frames(&[0i32]).unwrap(); + frame_writer.write_integer_frames(&[0i32]).unwrap(); + frame_writer.write_integer_frames(&[0i32]).unwrap(); frame_writer.end().unwrap(); } @@ -396,7 +421,7 @@ fn test_write_bext() { // NOTE! This test of RF64 writing passes on my machine but because it takes // nearly 5 mins to run I have omitted it from the source for now... -// #[test] +#[test] fn test_create_rf64() { use std::io::Cursor; use super::fourcc::ReadFourCC; @@ -406,19 +431,21 @@ fn test_create_rf64() { let format = WaveFmt::new_pcm_stereo(48000, 24); let w = WaveWriter::new(&mut cursor, format).unwrap(); + let buflen = 16000 as u64; - let buf = format.create_frame_buffer(); + let buf = vec![0i32; buflen as usize]; - let four_and_a_half_hours = 48000 * 16_200; // 4,665,600,000 bytes / 777,600,000 frames + let four_and_a_half_hours_of_frames = 48000 * 16_200; let mut af = w.audio_frame_writer().unwrap(); - for _ in 0..four_and_a_half_hours { - af.write_integer_frame(&buf).unwrap(); + for _ in 0..(four_and_a_half_hours_of_frames * format.channel_count as u64 / buflen) { + af.write_integer_frames(&buf).unwrap(); } af.end().unwrap(); - let expected_data_length = four_and_a_half_hours * format.block_alignment as u64; + assert!(cursor.seek(SeekFrom::End(0)).unwrap() > 0xFFFF_FFFFu64, "internal test error, Created file is not long enough to be RF64" ); + let expected_data_length = four_and_a_half_hours_of_frames * format.block_alignment as u64; cursor.seek(SeekFrom::Start(0)).unwrap(); assert_eq!(cursor.read_fourcc().unwrap(), RF64_SIG);