mirror of
https://github.com/iluvcapra/bwavfile.git
synced 2025-12-31 08:50:44 +00:00
Introduce generic read_frames and write_frames functions
This commit is contained in:
31
src/fmt.rs
31
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<i32>` with
|
||||
/// as many elements as there are channels in the underlying stream.
|
||||
pub fn create_frame_buffer(&self, length: usize) -> Vec<i32> {
|
||||
vec![0i32; self.channel_count as usize * length]
|
||||
pub fn create_frame_buffer<S: Sample>(&self, length: usize) -> Vec<S> {
|
||||
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::<LittleEndian>(*frame as i16).unwrap(),
|
||||
(10..=24,24) => write_cursor.write_i24::<LittleEndian>(*frame).unwrap(),
|
||||
(25..=32,32) => write_cursor.write_i32::<LittleEndian>(*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);
|
||||
|
||||
@@ -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};
|
||||
|
||||
14
src/sample.rs
Normal file
14
src/sample.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
pub use dasp_sample::I24;
|
||||
|
||||
use dasp_sample::Duplex;
|
||||
|
||||
pub trait Sample:
|
||||
dasp_sample::Sample + Duplex<u8> + Duplex<i16> + Duplex<I24> + Duplex<i32> + Duplex<f32>
|
||||
{
|
||||
}
|
||||
|
||||
impl Sample for u8 {}
|
||||
impl Sample for i16 {}
|
||||
impl Sample for I24 {}
|
||||
impl Sample for i32 {}
|
||||
impl Sample for f32 {}
|
||||
@@ -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<R: Read + Seek> AudioFrameReader<R> {
|
||||
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<u64, Error> {
|
||||
/// The return value is the number of frames read into the buffer.
|
||||
pub fn read_frames<S>(&mut self, buffer: &mut [S]) -> Result<u64, Error>
|
||||
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::<LittleEndian>()?.to_sample())
|
||||
}),
|
||||
(IntegerPCM, 24) => read_into_buffer(samples_to_read, buffer, || {
|
||||
Ok(I24::from(self.inner.read_i24::<LittleEndian>()?).to_sample())
|
||||
}),
|
||||
(IntegerPCM, 32) => read_into_buffer(samples_to_read, buffer, || {
|
||||
Ok(self.inner.read_i32::<LittleEndian>()?.to_sample())
|
||||
}),
|
||||
(IeeeFloatPCM, 32) => read_into_buffer(samples_to_read, buffer, || {
|
||||
Ok(self.inner.read_f32::<LittleEndian>()?.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::<LittleEndian>()? as i32,
|
||||
(10..=24,24) => self.inner.read_i24::<LittleEndian>()?,
|
||||
(25..=32,32) => self.inner.read_i32::<LittleEndian>()?,
|
||||
(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<S, F>(
|
||||
sample_count: usize,
|
||||
buffer: &mut [S],
|
||||
mut read_fn: F,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
F: FnMut() -> Result<S, Error>,
|
||||
{
|
||||
for output in buffer.iter_mut().take(sample_count) {
|
||||
*output = read_fn()?;
|
||||
}
|
||||
|
||||
pub fn read_float_frame(&mut self, buffer: &mut [f32]) -> Result<u64, Error> {
|
||||
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::<LittleEndian>()?,
|
||||
(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<R: Read + Seek> AudioFrameReader<R> {
|
||||
/// 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::<i32>(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);
|
||||
|
||||
@@ -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<u64, Error> {
|
||||
pub fn write_frames<S>(&mut self, buffer: &[S]) -> Result<u64, 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"
|
||||
);
|
||||
|
||||
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::<LittleEndian>(sample.to_sample())?
|
||||
}
|
||||
}
|
||||
(_, 24) => {
|
||||
for sample in buffer {
|
||||
write_cursor.write_i24::<LittleEndian>(sample.to_sample::<I24>().inner())?
|
||||
}
|
||||
}
|
||||
(CommonFormat::IntegerPCM, 32) => {
|
||||
for sample in buffer {
|
||||
write_cursor.write_i32::<LittleEndian>(sample.to_sample())?
|
||||
}
|
||||
}
|
||||
(CommonFormat::IeeeFloatPCM, 32) => {
|
||||
for sample in buffer {
|
||||
write_cursor.write_f32::<LittleEndian>(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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user