diff --git a/src/lib.rs b/src/lib.rs index da722f3..08d7fe3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,8 +105,6 @@ mod parser; mod fourcc; mod errors; -mod validation; - mod raw_chunk_reader; mod audio_frame_reader; mod chunks; diff --git a/src/validation.rs b/src/validation.rs deleted file mode 100644 index 0022864..0000000 --- a/src/validation.rs +++ /dev/null @@ -1,153 +0,0 @@ -use super::parser::{Parser}; -use super::fourcc::{FourCC, FMT__SIG,DATA_SIG, BEXT_SIG, JUNK_SIG, FLLR_SIG}; -use super::errors::Error as ParserError; -use super::wavereader::{WaveReader}; - -use std::io::{Read,Seek}; - - -impl WaveReader { - /** - * 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 (data_pos, _) = self.get_chunk_extent_at_index(DATA_SIG, 0)?; - - if fmt_pos < data_pos { - Ok(()) - } else { - Err( ParserError::FmtChunkAfterData) - } - } - - /** - * Validate minimal WAVE file. - * - * `Ok(())` if the source is `validate_readable()` AND - * - * - Contains _only_ a `fmt` chunk and `data` chunk, with no other chunks present - * - is not an RF64/BW64 - * - * Some clients require a WAVE file to only contain format and data without any other - * metadata and this function is provided to validate this condition. - * - * ### Examples - * - * ``` - * # use bwavfile::WaveReader; - * - * let mut w = WaveReader::open("tests/media/ff_minimal.wav").unwrap(); - * w.validate_minimal().expect("Minimal wav did not validate not minimal!"); - * ``` - * - * ``` - * # use bwavfile::WaveReader; - * - * 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> { - self.validate_readable()?; - - let chunk_fourccs : Vec = Parser::make(&mut self.inner)? - .into_chunk_list()?.iter().map(|c| c.signature ).collect(); - - if chunk_fourccs == vec![FMT__SIG, DATA_SIG] { - Ok(()) - } else { - Err( ParserError::NotMinimalWaveFile ) - } - } - - /** - * Validate Broadcast-WAVE file format - * - * 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"); - * ``` - */ - pub fn validate_broadcast_wave(&mut self) -> Result<(), ParserError> { - 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 - * `data` chunk's content begins at 0x4000. - */ - 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 { - Ok(()) - } else { - Err(ParserError::DataChunkNotAligned) - } - } - - /** - * 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 - * 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> { - self.validate_readable()?; - - let chunks = Parser::make(&mut self.inner)?.into_chunk_list()?; - let ds64_space_required = 92; - - 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}); - - if filler < ds64_space_required { - 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) - } - } - } -} - - - - - - - - diff --git a/src/wavereader.rs b/src/wavereader.rs index 904f41b..2e24496 100644 --- a/src/wavereader.rs +++ b/src/wavereader.rs @@ -2,7 +2,7 @@ use std::fs::File; use super::parser::Parser; -use super::fourcc::{FourCC, FMT__SIG, BEXT_SIG, DATA_SIG}; +use super::fourcc::{FourCC, FMT__SIG,DATA_SIG, BEXT_SIG, JUNK_SIG, FLLR_SIG}; use super::errors::Error as ParserError; use super::raw_chunk_reader::RawChunkReader; use super::fmt::WaveFmt; @@ -131,6 +131,142 @@ impl WaveReader { pub fn broadcast_extension(&mut self) -> Result { self.chunk_reader(BEXT_SIG, 0)?.read_bext() } + + /** + * 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 (data_pos, _) = self.get_chunk_extent_at_index(DATA_SIG, 0)?; + + if fmt_pos < data_pos { + Ok(()) + } else { + Err( ParserError::FmtChunkAfterData) + } + } + + /** + * Validate minimal WAVE file. + * + * `Ok(())` if the source is `validate_readable()` AND + * + * - Contains _only_ a `fmt` chunk and `data` chunk, with no other chunks present + * - is not an RF64/BW64 + * + * Some clients require a WAVE file to only contain format and data without any other + * metadata and this function is provided to validate this condition. + * + * ### Examples + * + * ``` + * # use bwavfile::WaveReader; + * + * let mut w = WaveReader::open("tests/media/ff_minimal.wav").unwrap(); + * w.validate_minimal().expect("Minimal wav did not validate not minimal!"); + * ``` + * + * ``` + * # use bwavfile::WaveReader; + * + * 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> { + self.validate_readable()?; + + let chunk_fourccs : Vec = Parser::make(&mut self.inner)? + .into_chunk_list()?.iter().map(|c| c.signature ).collect(); + + if chunk_fourccs == vec![FMT__SIG, DATA_SIG] { + Ok(()) + } else { + Err( ParserError::NotMinimalWaveFile ) + } + } + + /** + * Validate Broadcast-WAVE file format + * + * 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"); + * ``` + */ + pub fn validate_broadcast_wave(&mut self) -> Result<(), ParserError> { + 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 + * `data` chunk's content begins at 0x4000. + */ + 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 { + Ok(()) + } else { + Err(ParserError::DataChunkNotAligned) + } + } + + /** + * 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 + * 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> { + self.validate_readable()?; + + let chunks = Parser::make(&mut self.inner)?.into_chunk_list()?; + let ds64_space_required = 92; + + 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}); + + if filler < ds64_space_required { + 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) + } + } + } } impl WaveReader { /* Private Implementation */ @@ -140,7 +276,7 @@ impl WaveReader { /* Private Implementation */ Ok( RawChunkReader::new(&mut self.inner, start, length) ) } - pub 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> { let p = Parser::make(&mut self.inner)?.into_chunk_list()?; if let Some(chunk) = p.iter().filter(|item| item.signature == fourcc).nth(index as usize) {