mirror of
https://github.com/iluvcapra/bwavfile.git
synced 2026-01-01 01:10:43 +00:00
prettify code
This commit is contained in:
@@ -1,62 +1,72 @@
|
||||
|
||||
use std::fs::File;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use std::io::SeekFrom;
|
||||
use std::io::Cursor;
|
||||
use std::io::{Read, Seek, BufReader};
|
||||
use std::io::SeekFrom::{Start,Current,};
|
||||
use std::io::SeekFrom;
|
||||
use std::io::SeekFrom::{Current, Start};
|
||||
use std::io::{BufReader, Read, Seek};
|
||||
|
||||
use super::parser::Parser;
|
||||
use super::fourcc::{FourCC, ReadFourCC, FMT__SIG, DATA_SIG, BEXT_SIG, LIST_SIG,
|
||||
JUNK_SIG, FLLR_SIG, CUE__SIG, ADTL_SIG, AXML_SIG, IXML_SIG};
|
||||
use super::errors::Error as ParserError;
|
||||
use super::fmt::{WaveFmt, ChannelDescriptor, ChannelMask};
|
||||
use super::bext::Bext;
|
||||
use super::chunks::ReadBWaveChunks;
|
||||
use super::cue::Cue;
|
||||
use super::errors::Error as ParserError;
|
||||
use super::errors::Error;
|
||||
use super::fmt::{ChannelDescriptor, ChannelMask, WaveFmt};
|
||||
use super::fourcc::{
|
||||
FourCC, ReadFourCC, ADTL_SIG, AXML_SIG, BEXT_SIG, CUE__SIG, DATA_SIG, FLLR_SIG, FMT__SIG,
|
||||
IXML_SIG, JUNK_SIG, LIST_SIG,
|
||||
};
|
||||
use super::parser::Parser;
|
||||
use super::CommonFormat;
|
||||
|
||||
use byteorder::LittleEndian;
|
||||
use byteorder::ReadBytesExt;
|
||||
|
||||
|
||||
|
||||
/// Read audio frames
|
||||
///
|
||||
///
|
||||
/// The inner reader is interpreted as a raw audio data
|
||||
/// bitstream having a format specified by `format`.
|
||||
///
|
||||
///
|
||||
#[derive(Debug)]
|
||||
pub struct AudioFrameReader<R: Read + Seek> {
|
||||
inner : R,
|
||||
inner: R,
|
||||
format: WaveFmt,
|
||||
start: u64,
|
||||
length: u64
|
||||
length: u64,
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> AudioFrameReader<R> {
|
||||
|
||||
/// Create a new `AudioFrameReader`
|
||||
///
|
||||
///
|
||||
/// ### Panics
|
||||
///
|
||||
///
|
||||
/// This method does a few sanity checks on the provided format
|
||||
/// parameter to confirm the `block_alignment` law is fulfilled
|
||||
/// and the format tag is readable by this implementation (only
|
||||
/// format 0x01 is supported at this time.)
|
||||
/// format 0x01 is supported at this time.)
|
||||
pub fn new(mut inner: R, format: WaveFmt, start: u64, length: u64) -> Result<Self, Error> {
|
||||
assert!(format.block_alignment * 8 == format.bits_per_sample * format.channel_count,
|
||||
assert!(
|
||||
format.block_alignment * 8 == format.bits_per_sample * format.channel_count,
|
||||
"Unable to read audio frames from packed formats: block alignment is {}, should be {}",
|
||||
format.block_alignment, (format.bits_per_sample / 8 ) * format.channel_count);
|
||||
|
||||
assert!(format.common_format() == CommonFormat::IntegerPCM || format.common_format() == CommonFormat::IeeeFloatPCM,
|
||||
"Unsupported format tag {:?}", format.tag);
|
||||
|
||||
format.block_alignment,
|
||||
(format.bits_per_sample / 8) * format.channel_count
|
||||
);
|
||||
|
||||
assert!(
|
||||
format.common_format() == CommonFormat::IntegerPCM
|
||||
|| format.common_format() == CommonFormat::IeeeFloatPCM,
|
||||
"Unsupported format tag {:?}",
|
||||
format.tag
|
||||
);
|
||||
|
||||
inner.seek(Start(start))?;
|
||||
Ok( AudioFrameReader { inner , format , start, length} )
|
||||
Ok(AudioFrameReader {
|
||||
inner,
|
||||
format,
|
||||
start,
|
||||
length,
|
||||
})
|
||||
}
|
||||
|
||||
/// Unwrap the inner reader.
|
||||
@@ -65,42 +75,44 @@ impl<R: Read + Seek> AudioFrameReader<R> {
|
||||
}
|
||||
|
||||
/// Locate the read position to a different frame
|
||||
///
|
||||
///
|
||||
/// Seeks within the audio stream.
|
||||
///
|
||||
///
|
||||
/// Returns the new location of the read position.
|
||||
///
|
||||
///
|
||||
/// locate() behaves similarly to Read methods in that
|
||||
/// seeking after the end of the audio data is not an error.
|
||||
pub fn locate(&mut self, to :u64) -> Result<u64,Error> {
|
||||
pub fn locate(&mut self, to: u64) -> Result<u64, Error> {
|
||||
let position = to * self.format.block_alignment as u64;
|
||||
let seek_result = self.inner.seek(Start(self.start + position))?;
|
||||
Ok( (seek_result - self.start) / self.format.block_alignment as u64 )
|
||||
Ok((seek_result - self.start) / self.format.block_alignment as u64)
|
||||
}
|
||||
|
||||
|
||||
/// Read a frame
|
||||
///
|
||||
///
|
||||
/// A single frame is read from the audio stream and the read location
|
||||
/// is advanced one frame.
|
||||
///
|
||||
///
|
||||
/// Regardless of the number of bits in the audio sample, this method
|
||||
/// always writes `i32` samples back to the buffer. These samples are
|
||||
/// 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.
|
||||
///
|
||||
/// For example: A full-code sample in 16 bit (0xFFFF) will be written
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// 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> {
|
||||
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());
|
||||
pub fn read_integer_frame(&mut self, buffer: &mut [i32]) -> Result<u64, Error> {
|
||||
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()
|
||||
);
|
||||
|
||||
let framed_bits_per_sample = self.format.block_alignment * 8 / self.format.channel_count;
|
||||
|
||||
@@ -117,16 +129,19 @@ impl<R: Read + Seek> AudioFrameReader<R> {
|
||||
b, self.format.channel_count, self.format.block_alignment)
|
||||
}
|
||||
}
|
||||
Ok( 1 )
|
||||
Ok(1)
|
||||
} else {
|
||||
Ok( 0 )
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_float_frame(&mut self, buffer: &mut [f32]) -> Result<u64, Error> {
|
||||
assert!(buffer.len() as u16 == self.format.channel_count,
|
||||
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());
|
||||
self.format.channel_count,
|
||||
buffer.len()
|
||||
);
|
||||
|
||||
let framed_bits_per_sample = self.format.block_alignment * 8 / self.format.channel_count;
|
||||
|
||||
@@ -140,9 +155,9 @@ impl<R: Read + Seek> AudioFrameReader<R> {
|
||||
b, self.format.channel_count, self.format.block_alignment)
|
||||
}
|
||||
}
|
||||
Ok( 1 )
|
||||
Ok(1)
|
||||
} else {
|
||||
Ok( 0 )
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,7 +165,7 @@ impl<R: Read + Seek> AudioFrameReader<R> {
|
||||
/// Wave, Broadcast-WAV and RF64/BW64 parser/reader.
|
||||
///
|
||||
/// ```
|
||||
/// use bwavfile::WaveReader;
|
||||
/// use bwavfile::WaveReader;
|
||||
/// let mut r = WaveReader::open("tests/media/ff_silence.wav").unwrap();
|
||||
///
|
||||
/// let format = r.format().unwrap();
|
||||
@@ -161,17 +176,17 @@ impl<R: Read + Seek> AudioFrameReader<R> {
|
||||
/// let mut buffer = format.create_frame_buffer(1);
|
||||
///
|
||||
/// let read = frame_reader.read_integer_frame(&mut buffer).unwrap();
|
||||
///
|
||||
///
|
||||
/// assert_eq!(buffer, [0i32]);
|
||||
/// assert_eq!(read, 1);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// ## Resources
|
||||
///
|
||||
///
|
||||
/// ### Implementation of Wave Files
|
||||
/// - [Peter Kabal, McGill University](http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html)
|
||||
/// - [Multimedia Programming Interface and Data Specifications 1.0](http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/riffmci.pdf)
|
||||
/// - [Multimedia Programming Interface and Data Specifications 1.0](http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/riffmci.pdf)
|
||||
/// (August 1991), IBM Corporation and Microsoft Corporation
|
||||
///
|
||||
/// ### Implementation of Broadcast Wave Files
|
||||
@@ -184,14 +199,13 @@ impl<R: Read + Seek> AudioFrameReader<R> {
|
||||
/// - Presently in force, adopted by the EBU in [EBU Tech 3306v2][ebu3306v2] (June 2018).
|
||||
/// - [EBU Tech 3306v1][ebu3306v1] (July 2009), "MBWF / RF64: An extended File Format for Audio"
|
||||
/// - No longer in force, however long-established.
|
||||
///
|
||||
///
|
||||
///
|
||||
/// [ebu3285]: https://tech.ebu.ch/docs/tech/tech3285.pdf
|
||||
/// [ebu3306v1]: https://tech.ebu.ch/docs/tech/tech3306v1_1.pdf
|
||||
/// [ebu3306v2]: https://tech.ebu.ch/docs/tech/tech3306.pdf
|
||||
/// [itu2088]: https://www.itu.int/dms_pubrec/itu-r/rec/bs/R-REC-BS.2088-1-201910-I!!PDF-E.pdf
|
||||
/// [rfc3261]: https://tools.ietf.org/html/rfc2361
|
||||
|
||||
/// [rfc3261]: https://tools.ietf.org/html/rfc2361
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WaveReader<R: Read + Seek> {
|
||||
@@ -199,40 +213,37 @@ pub struct WaveReader<R: Read + Seek> {
|
||||
}
|
||||
|
||||
impl WaveReader<BufReader<File>> {
|
||||
|
||||
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, ParserError> {
|
||||
let f = File::open(path)?;
|
||||
let inner = BufReader::new(f);
|
||||
Ok( Self::new(inner)? )
|
||||
Ok(Self::new(inner)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl WaveReader<File> {
|
||||
|
||||
/// Open a file for reading with unbuffered IO.
|
||||
///
|
||||
/// A convenience that opens `path` and calls `Self::new()`
|
||||
|
||||
/// Open a file for reading with unbuffered IO.
|
||||
///
|
||||
/// A convenience that opens `path` and calls `Self::new()`
|
||||
|
||||
pub fn open_unbuffered<P: AsRef<Path>>(path: P) -> Result<Self, ParserError> {
|
||||
let inner = File::open(path)?;
|
||||
return Ok( Self::new(inner)? )
|
||||
return Ok(Self::new(inner)?);
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> WaveReader<R> {
|
||||
|
||||
/// Wrap a `Read` struct in a new `WaveReader`.
|
||||
///
|
||||
///
|
||||
/// This is the primary entry point into the `WaveReader` interface. The
|
||||
/// stream passed as `inner` must be at the beginning of the header of the
|
||||
/// WAVE data. For a .wav file, this means it must be at the start of the
|
||||
/// WAVE data. For a .wav file, this means it must be at the start of the
|
||||
/// file.
|
||||
///
|
||||
/// This function does a minimal validation on the provided stream and
|
||||
/// will return an `Err(errors::Error)` immediately if there is a structural
|
||||
/// inconsistency that makes the stream unreadable or if it's missing
|
||||
/// will return an `Err(errors::Error)` immediately if there is a structural
|
||||
/// inconsistency that makes the stream unreadable or if it's missing
|
||||
/// essential components that make interpreting the audio data impossible.
|
||||
|
||||
|
||||
/// ```rust
|
||||
/// use std::fs::File;
|
||||
/// use std::io::{Error,ErrorKind};
|
||||
@@ -249,15 +260,14 @@ impl<R: Read + Seek> WaveReader<R> {
|
||||
/// }
|
||||
/// Err(e) => panic!("Unexpected error was returned {:?}", e)
|
||||
/// }
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
pub fn new(inner: R) -> Result<Self,ParserError> {
|
||||
pub fn new(inner: R) -> Result<Self, ParserError> {
|
||||
let mut retval = Self { inner };
|
||||
retval.validate_readable()?;
|
||||
Ok(retval)
|
||||
}
|
||||
|
||||
|
||||
/// Unwrap the inner reader.
|
||||
pub fn into_inner(self) -> R {
|
||||
return self.inner;
|
||||
@@ -269,18 +279,21 @@ impl<R: Read + Seek> WaveReader<R> {
|
||||
pub fn audio_frame_reader(mut self) -> Result<AudioFrameReader<R>, ParserError> {
|
||||
let format = self.format()?;
|
||||
let audio_chunk_reader = self.get_chunk_extent_at_index(DATA_SIG, 0)?;
|
||||
Ok(AudioFrameReader::new(self.inner, format, audio_chunk_reader.0, audio_chunk_reader.1)?)
|
||||
Ok(AudioFrameReader::new(
|
||||
self.inner,
|
||||
format,
|
||||
audio_chunk_reader.0,
|
||||
audio_chunk_reader.1,
|
||||
)?)
|
||||
}
|
||||
|
||||
|
||||
/// The count of audio frames in the file.
|
||||
pub fn frame_length(&mut self) -> Result<u64, ParserError> {
|
||||
let (_, data_length ) = self.get_chunk_extent_at_index(DATA_SIG, 0)?;
|
||||
let (_, data_length) = self.get_chunk_extent_at_index(DATA_SIG, 0)?;
|
||||
let format = self.format()?;
|
||||
Ok( data_length / (format.block_alignment as u64) )
|
||||
}
|
||||
Ok(data_length / (format.block_alignment as u64))
|
||||
}
|
||||
|
||||
|
||||
/// Sample and frame format of this wave file.
|
||||
///
|
||||
pub fn format(&mut self) -> Result<WaveFmt, ParserError> {
|
||||
@@ -290,29 +303,28 @@ impl<R: Read + Seek> WaveReader<R> {
|
||||
}
|
||||
|
||||
/// The Broadcast-WAV metadata record for this file, if present.
|
||||
///
|
||||
///
|
||||
pub fn broadcast_extension(&mut self) -> Result<Option<Bext>, ParserError> {
|
||||
let mut bext_buff : Vec<u8> = vec![ ];
|
||||
let mut bext_buff: Vec<u8> = vec![];
|
||||
let result = self.read_chunk(BEXT_SIG, 0, &mut bext_buff)?;
|
||||
if result > 0 {
|
||||
let mut bext_cursor = Cursor::new(bext_buff);
|
||||
Ok( Some( bext_cursor.read_bext()? ) )
|
||||
Ok(Some(bext_cursor.read_bext()?))
|
||||
} else {
|
||||
Ok( None)
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Describe the channels in this file
|
||||
///
|
||||
///
|
||||
/// Returns a vector of channel descriptors, one for each channel
|
||||
///
|
||||
///
|
||||
/// ```rust
|
||||
/// use bwavfile::WaveReader;
|
||||
/// use bwavfile::ChannelMask;
|
||||
///
|
||||
/// let mut f = WaveReader::open("tests/media/pt_24bit_51.wav").unwrap();
|
||||
///
|
||||
///
|
||||
/// let chans = f.channels().unwrap();
|
||||
/// assert_eq!(chans[0].index, 0);
|
||||
/// assert_eq!(chans[0].speaker, ChannelMask::FrontLeft);
|
||||
@@ -321,97 +333,100 @@ impl<R: Read + Seek> WaveReader<R> {
|
||||
/// assert_eq!(chans[4].speaker, ChannelMask::BackLeft);
|
||||
/// ```
|
||||
pub fn channels(&mut self) -> Result<Vec<ChannelDescriptor>, ParserError> {
|
||||
|
||||
let format = self.format()?;
|
||||
let channel_masks : Vec<ChannelMask> = match (format.channel_count, format.extended_format) {
|
||||
(1,_) => vec![ChannelMask::FrontCenter],
|
||||
(2,_) => vec![ChannelMask::FrontLeft, ChannelMask::FrontRight],
|
||||
(n,Some(x)) => ChannelMask::channels(x.channel_mask, n),
|
||||
(n,_) => vec![ChannelMask::DirectOut; n as usize]
|
||||
let channel_masks: Vec<ChannelMask> = match (format.channel_count, format.extended_format) {
|
||||
(1, _) => vec![ChannelMask::FrontCenter],
|
||||
(2, _) => vec![ChannelMask::FrontLeft, ChannelMask::FrontRight],
|
||||
(n, Some(x)) => ChannelMask::channels(x.channel_mask, n),
|
||||
(n, _) => vec![ChannelMask::DirectOut; n as usize],
|
||||
};
|
||||
|
||||
Ok( (0..format.channel_count).zip(channel_masks)
|
||||
.map(|(i,m)| ChannelDescriptor { index: i, speaker:m, adm_track_audio_ids: vec![] } )
|
||||
.collect() )
|
||||
Ok((0..format.channel_count)
|
||||
.zip(channel_masks)
|
||||
.map(|(i, m)| ChannelDescriptor {
|
||||
index: i,
|
||||
speaker: m,
|
||||
adm_track_audio_ids: vec![],
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Read cue points.
|
||||
///
|
||||
///
|
||||
/// ```rust
|
||||
/// use bwavfile::WaveReader;
|
||||
/// use bwavfile::Cue;
|
||||
///
|
||||
///
|
||||
/// let mut f = WaveReader::open("tests/media/izotope_test.wav").unwrap();
|
||||
/// let cue_points = f.cue_points().unwrap();
|
||||
///
|
||||
///
|
||||
/// assert_eq!(cue_points.len(), 3);
|
||||
/// assert_eq!(cue_points[0].frame, 12532);
|
||||
/// assert_eq!(cue_points[0].length, None);
|
||||
/// assert_eq!(cue_points[0].label, Some(String::from("Marker 1")));
|
||||
/// assert_eq!(cue_points[0].note, Some(String::from("Marker 1 Comment")));
|
||||
///
|
||||
///
|
||||
/// assert_eq!(cue_points[1].frame, 20997);
|
||||
/// assert_eq!(cue_points[1].length, None);
|
||||
/// assert_eq!(cue_points[1].label, Some(String::from("Marker 2")));
|
||||
/// assert_eq!(cue_points[1].note, Some(String::from("Marker 2 Comment")));
|
||||
///
|
||||
/// assert_eq!(cue_points[1].note, Some(String::from("Marker 2 Comment")));
|
||||
///
|
||||
/// assert_eq!(cue_points[2].frame, 26711);
|
||||
/// assert_eq!(cue_points[2].length, Some(6465));
|
||||
/// assert_eq!(cue_points[2].label, Some(String::from("Timed Region")));
|
||||
/// assert_eq!(cue_points[2].note, Some(String::from("Region Comment")));
|
||||
///
|
||||
/// assert_eq!(cue_points[2].note, Some(String::from("Region Comment")));
|
||||
///
|
||||
/// ```
|
||||
pub fn cue_points(&mut self) -> Result<Vec<Cue>,ParserError> {
|
||||
let mut cue_buffer : Vec<u8> = vec![];
|
||||
let mut adtl_buffer : Vec<u8> = vec![];
|
||||
pub fn cue_points(&mut self) -> Result<Vec<Cue>, ParserError> {
|
||||
let mut cue_buffer: Vec<u8> = vec![];
|
||||
let mut adtl_buffer: Vec<u8> = vec![];
|
||||
|
||||
let cue_read = self.read_chunk(CUE__SIG, 0, &mut cue_buffer)?;
|
||||
let adtl_read = self.read_list(ADTL_SIG, &mut adtl_buffer)?;
|
||||
|
||||
match (cue_read, adtl_read) {
|
||||
(0,_) => Ok( vec![] ),
|
||||
(_,0) => Ok( Cue::collect_from(&cue_buffer, None)? ),
|
||||
(_,_) => Ok( Cue::collect_from(&cue_buffer, Some(&adtl_buffer) )? )
|
||||
(0, _) => Ok(vec![]),
|
||||
(_, 0) => Ok(Cue::collect_from(&cue_buffer, None)?),
|
||||
(_, _) => Ok(Cue::collect_from(&cue_buffer, Some(&adtl_buffer))?),
|
||||
}
|
||||
}
|
||||
|
||||
/// Read iXML data.
|
||||
///
|
||||
///
|
||||
/// The iXML data will be appended to `buffer`.
|
||||
/// If there are no iXML metadata present in the file,
|
||||
/// If there are no iXML metadata present in the file,
|
||||
/// Ok(0) will be returned.
|
||||
pub fn read_ixml(&mut self, buffer: &mut Vec<u8>) -> Result<usize, ParserError> {
|
||||
self.read_chunk(IXML_SIG, 0, buffer)
|
||||
self.read_chunk(IXML_SIG, 0, buffer)
|
||||
}
|
||||
|
||||
/// Read AXML data.
|
||||
///
|
||||
/// The axml data will be appended to `buffer`. By convention this will
|
||||
///
|
||||
/// The axml data will be appended to `buffer`. By convention this will
|
||||
/// generally be ADM metadata.
|
||||
///
|
||||
/// If there are no axml metadata present in the file,
|
||||
///
|
||||
/// If there are no axml metadata present in the file,
|
||||
/// Ok(0) will be returned
|
||||
pub fn read_axml(&mut self, buffer: &mut Vec<u8>) -> Result<usize, ParserError> {
|
||||
self.read_chunk(AXML_SIG, 0, buffer)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate file is readable.
|
||||
*
|
||||
* `Ok(())` if the source meets the minimum standard of
|
||||
* readability by a permissive client:
|
||||
* - `fmt` chunk and `data` chunk are present
|
||||
* - `fmt` chunk appears before `data` chunk
|
||||
*/
|
||||
* Validate file is readable.
|
||||
*
|
||||
* `Ok(())` if the source meets the minimum standard of
|
||||
* readability by a permissive client:
|
||||
* - `fmt` chunk and `data` chunk are present
|
||||
* - `fmt` chunk appears before `data` chunk
|
||||
*/
|
||||
pub fn validate_readable(&mut self) -> Result<(), ParserError> {
|
||||
let (fmt_pos, _) = self.get_chunk_extent_at_index(FMT__SIG, 0)?;
|
||||
let (fmt_pos, _) = self.get_chunk_extent_at_index(FMT__SIG, 0)?;
|
||||
let (data_pos, _) = self.get_chunk_extent_at_index(DATA_SIG, 0)?;
|
||||
|
||||
if fmt_pos < data_pos {
|
||||
Ok(())
|
||||
} else {
|
||||
Err( ParserError::FmtChunkAfterData)
|
||||
Err(ParserError::FmtChunkAfterData)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,35 +457,38 @@ impl<R: Read + Seek> WaveReader<R> {
|
||||
/// let mut x = WaveReader::open("tests/media/pt_24bit_51.wav").unwrap();
|
||||
/// x.validate_minimal().expect_err("Complex WAV validated minimal!");
|
||||
/// ```
|
||||
pub fn validate_minimal(&mut self) -> Result<(), ParserError> {
|
||||
pub fn validate_minimal(&mut self) -> Result<(), ParserError> {
|
||||
self.validate_readable()?;
|
||||
|
||||
let chunk_fourccs : Vec<FourCC> = Parser::make(&mut self.inner)?
|
||||
.into_chunk_list()?.iter().map(|c| c.signature ).collect();
|
||||
let chunk_fourccs: Vec<FourCC> = Parser::make(&mut self.inner)?
|
||||
.into_chunk_list()?
|
||||
.iter()
|
||||
.map(|c| c.signature)
|
||||
.collect();
|
||||
|
||||
if chunk_fourccs == vec![FMT__SIG, DATA_SIG] {
|
||||
Ok(()) /* FIXME: finish implementation */
|
||||
} else {
|
||||
Err( ParserError::NotMinimalWaveFile )
|
||||
Err(ParserError::NotMinimalWaveFile)
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate Broadcast-WAVE file format
|
||||
///
|
||||
/// Returns `Ok(())` if `validate_readable()` and file contains a
|
||||
///
|
||||
/// Returns `Ok(())` if `validate_readable()` and file contains a
|
||||
/// Broadcast-WAV metadata record (a `bext` chunk).
|
||||
///
|
||||
///
|
||||
/// ### Examples
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// # use bwavfile::WaveReader;
|
||||
///
|
||||
///
|
||||
/// let mut w = WaveReader::open("tests/media/ff_bwav_stereo.wav").unwrap();
|
||||
/// w.validate_broadcast_wave().expect("BWAVE file did not validate BWAVE");
|
||||
///
|
||||
///
|
||||
/// let mut x = WaveReader::open("tests/media/pt_24bit.wav").unwrap();
|
||||
/// x.validate_broadcast_wave().expect("BWAVE file did not validate BWAVE");
|
||||
///
|
||||
///
|
||||
/// let mut y = WaveReader::open("tests/media/audacity_16bit.wav").unwrap();
|
||||
/// y.validate_broadcast_wave().expect_err("Plain WAV file DID validate BWAVE");
|
||||
/// ```
|
||||
@@ -479,14 +497,14 @@ impl<R: Read + Seek> WaveReader<R> {
|
||||
self.validate_readable()?;
|
||||
let (_, _) = self.get_chunk_extent_at_index(BEXT_SIG, 0)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Verify data is aligned to a block boundary.
|
||||
///
|
||||
/// Returns `Ok(())` if `validate_readable()` and the start of the
|
||||
/// Returns `Ok(())` if `validate_readable()` and the start of the
|
||||
/// `data` chunk's content begins at 0x4000.
|
||||
pub fn validate_data_chunk_alignment(&mut self) -> Result<() , ParserError> {
|
||||
pub fn validate_data_chunk_alignment(&mut self) -> Result<(), ParserError> {
|
||||
self.validate_readable()?;
|
||||
let (start, _) = self.get_chunk_extent_at_index(DATA_SIG, 0)?;
|
||||
if start == 0x4000 {
|
||||
@@ -497,10 +515,10 @@ impl<R: Read + Seek> WaveReader<R> {
|
||||
}
|
||||
|
||||
/// Verify audio data can be appended immediately to this file.
|
||||
///
|
||||
///
|
||||
/// Returns `Ok(())` if:
|
||||
/// - `validate_readable()`
|
||||
/// - there is a `JUNK` or `FLLR` immediately at the beginning of the chunk
|
||||
/// - there is a `JUNK` or `FLLR` immediately at the beginning of the chunk
|
||||
/// list adequately large enough to be overwritten by a `ds64` (92 bytes)
|
||||
/// - `data` is the final chunk
|
||||
pub fn validate_prepared_for_append(&mut self) -> Result<(), ParserError> {
|
||||
@@ -509,66 +527,82 @@ impl<R: Read + Seek> WaveReader<R> {
|
||||
let chunks = Parser::make(&mut self.inner)?.into_chunk_list()?;
|
||||
let ds64_space_required = 92;
|
||||
|
||||
let eligible_filler_chunks = chunks.iter()
|
||||
let eligible_filler_chunks = chunks
|
||||
.iter()
|
||||
.take_while(|c| c.signature == JUNK_SIG || c.signature == FLLR_SIG);
|
||||
|
||||
let filler = eligible_filler_chunks
|
||||
.enumerate()
|
||||
.fold(0, |accum, (n, item)| if n == 0 { accum + item.length } else {accum + item.length + 8});
|
||||
.fold(0, |accum, (n, item)| {
|
||||
if n == 0 {
|
||||
accum + item.length
|
||||
} else {
|
||||
accum + item.length + 8
|
||||
}
|
||||
});
|
||||
|
||||
if filler < ds64_space_required {
|
||||
Err(ParserError::InsufficientDS64Reservation {expected: ds64_space_required, actual: filler})
|
||||
Err(ParserError::InsufficientDS64Reservation {
|
||||
expected: ds64_space_required,
|
||||
actual: filler,
|
||||
})
|
||||
} else {
|
||||
let data_pos = chunks.iter().position(|c| c.signature == DATA_SIG);
|
||||
|
||||
|
||||
match data_pos {
|
||||
Some(p) if p == chunks.len() - 1 => Ok(()),
|
||||
_ => Err(ParserError::DataChunkNotPreparedForAppend)
|
||||
_ => Err(ParserError::DataChunkNotPreparedForAppend),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R:Read+Seek> WaveReader<R> {
|
||||
|
||||
impl<R: Read + Seek> WaveReader<R> {
|
||||
// Private implementation
|
||||
//
|
||||
// As time passes thi get smore obnoxious because I haven't implemented recursive chunk
|
||||
// As time passes thi get smore obnoxious because I haven't implemented recursive chunk
|
||||
// parsing in the raw parser and I'm working around it
|
||||
|
||||
// fn chunk_reader(&mut self, signature: FourCC, at_index: u32) -> Result<RawChunkReader<R>, ParserError> {
|
||||
// let (start, length) = self.get_chunk_extent_at_index(signature, at_index)?;
|
||||
// Ok( RawChunkReader::new(&mut self.inner, start, length) )
|
||||
// }
|
||||
// }
|
||||
|
||||
fn read_list(&mut self, ident: FourCC, buffer: &mut Vec<u8>) -> Result<usize, ParserError> {
|
||||
if let Some(index) = self.get_list_form(ident)? {
|
||||
self.read_chunk(LIST_SIG, index, buffer)
|
||||
} else {
|
||||
Ok( 0 )
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn read_chunk(&mut self, ident: FourCC, at: u32, mut buffer: &mut Vec<u8>) -> Result<usize, ParserError> {
|
||||
|
||||
fn read_chunk(
|
||||
&mut self,
|
||||
ident: FourCC,
|
||||
at: u32,
|
||||
mut buffer: &mut Vec<u8>,
|
||||
) -> Result<usize, ParserError> {
|
||||
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))
|
||||
},
|
||||
Err(ParserError::ChunkMissing { signature : _} ) => Ok(0),
|
||||
Err( any ) => Err(any.into())
|
||||
self.inner
|
||||
.read(&mut buffer)
|
||||
.map_err(|e| ParserError::IOError(e))
|
||||
}
|
||||
Err(ParserError::ChunkMissing { signature: _ }) => Ok(0),
|
||||
Err(any) => Err(any.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Extent of every chunk with the given fourcc
|
||||
fn get_chunks_extents(&mut self, fourcc: FourCC) -> Result<Vec<(u64,u64)>, ParserError> {
|
||||
fn get_chunks_extents(&mut self, fourcc: FourCC) -> Result<Vec<(u64, u64)>, ParserError> {
|
||||
let p = Parser::make(&mut self.inner)?.into_chunk_list()?;
|
||||
|
||||
Ok( p.iter().filter(|item| item.signature == fourcc)
|
||||
.map(|item| (item.start, item.length)).collect() )
|
||||
Ok(p.iter()
|
||||
.filter(|item| item.signature == fourcc)
|
||||
.map(|item| (item.start, item.length))
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Index of first LIST for with the given FORM fourcc
|
||||
@@ -577,18 +611,22 @@ impl<R:Read+Seek> WaveReader<R> {
|
||||
self.inner.seek(SeekFrom::Start(*start as u64))?;
|
||||
let this_fourcc = self.inner.read_fourcc()?;
|
||||
if this_fourcc == fourcc {
|
||||
return Ok( Some( n as u32 ) );
|
||||
return Ok(Some(n as u32));
|
||||
}
|
||||
}
|
||||
|
||||
Ok( None )
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn get_chunk_extent_at_index(&mut self, fourcc: FourCC, index: u32) -> Result<(u64,u64), ParserError> {
|
||||
fn get_chunk_extent_at_index(
|
||||
&mut self,
|
||||
fourcc: FourCC,
|
||||
index: u32,
|
||||
) -> Result<(u64, u64), ParserError> {
|
||||
if let Some((start, length)) = self.get_chunks_extents(fourcc)?.iter().nth(index as usize) {
|
||||
Ok ((*start, *length))
|
||||
Ok((*start, *length))
|
||||
} else {
|
||||
Err( ParserError::ChunkMissing { signature : fourcc } )
|
||||
Err(ParserError::ChunkMissing { signature: fourcc })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -596,10 +634,9 @@ impl<R:Read+Seek> WaveReader<R> {
|
||||
#[test]
|
||||
fn test_list_form() {
|
||||
let mut f = WaveReader::open("tests/media/izotope_test.wav").unwrap();
|
||||
let mut buf : Vec<u8> = vec![];
|
||||
|
||||
let mut buf: Vec<u8> = vec![];
|
||||
|
||||
f.read_list(ADTL_SIG, &mut buf).unwrap();
|
||||
|
||||
assert_ne!(buf.len(), 0);
|
||||
|
||||
assert_ne!(buf.len(), 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user