mirror of
https://github.com/iluvcapra/bwavfile.git
synced 2025-12-31 08:50:44 +00:00
144 lines
5.2 KiB
Rust
144 lines
5.2 KiB
Rust
|
|
use std::fs::File;
|
|
|
|
use super::parser::Parser;
|
|
use super::fourcc::{FourCC, FMT__SIG, BEXT_SIG, DATA_SIG};
|
|
use super::errors::Error as ParserError;
|
|
use super::raw_chunk_reader::RawChunkReader;
|
|
use super::chunks::{WaveFmt, Bext};
|
|
use super::audio_frame_reader::AudioFrameReader;
|
|
use super::chunks::ReadBWaveChunks;
|
|
//use super::validation;
|
|
use std::io::SeekFrom::{Start};
|
|
use std::io::{Read, Seek};
|
|
|
|
|
|
/**
|
|
* Wave, Broadcast-WAV and RF64/BW64 parser/reader.
|
|
*
|
|
* ## Resources
|
|
*
|
|
* ### Implementation of Broadcast Wave Files
|
|
* - [EBU Tech 3285][ebu3285] (May 2011), "Specification of the Broadcast Wave Format (BWF)"
|
|
*
|
|
*
|
|
* ### Implementation of 64-bit Wave Files
|
|
* - [ITU-R 2088][itu2088] (October 2019), "Long-form file format for the international exchange of audio programme materials with metadata"
|
|
* - 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.
|
|
*
|
|
*
|
|
* ### Implementation of Wave format `fmt` chunk
|
|
* - [MSDN WAVEFORMATEX](https://docs.microsoft.com/en-us/windows/win32/api/mmeapi/ns-mmeapi-waveformatex)
|
|
* - [MSDN WAVEFORMATEXTENSIBLE](https://docs.microsoft.com/en-us/windows/win32/api/mmreg/ns-mmreg-waveformatextensible)
|
|
*
|
|
*
|
|
* ### Other resources
|
|
* - [RFC 3261][rfc3261] (June 1998) "WAVE and AVI Codec Registries"
|
|
* - [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)
|
|
* IBM Corporation and Microsoft Corporation, (August 1991)
|
|
*
|
|
* [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
|
|
*/
|
|
#[derive(Debug)]
|
|
pub struct WaveReader<R: Read + Seek> {
|
|
pub inner: R,
|
|
}
|
|
|
|
impl WaveReader<File> {
|
|
/**
|
|
* Open a file for reading.
|
|
*
|
|
* A convenience that opens `path` and calls `Self::new()`
|
|
*/
|
|
pub fn open(path: &str) -> Result<Self, ParserError> {
|
|
let inner = File::open(path)?;
|
|
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
|
|
* 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
|
|
* essential components that make interpreting the audio data impoossible.
|
|
*/
|
|
pub fn new(inner: R) -> Result<Self,ParserError> {
|
|
let mut retval = Self { inner };
|
|
retval.validate_readable()?;
|
|
Ok(retval)
|
|
}
|
|
|
|
/**
|
|
* Unwrap and reliqnish ownership of the inner reader.
|
|
*/
|
|
pub fn into_inner(self) -> R {
|
|
return self.inner;
|
|
}
|
|
|
|
/**
|
|
* Create an `AudioFrameReader` for reading each audio frame.
|
|
*/
|
|
pub fn audio_frame_reader(&mut self) -> Result<AudioFrameReader<RawChunkReader<R>>, ParserError> {
|
|
let format = self.format()?;
|
|
let audio_chunk_reader = self.chunk_reader(DATA_SIG, 0)?;
|
|
Ok(AudioFrameReader::new(audio_chunk_reader, format))
|
|
}
|
|
|
|
/**
|
|
* 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 format = self.format()?;
|
|
Ok( data_length / (format.block_alignment as u64) )
|
|
}
|
|
|
|
/**
|
|
* Sample and frame format of this wave file.
|
|
*/
|
|
pub fn format(&mut self) -> Result<WaveFmt, ParserError> {
|
|
self.chunk_reader(FMT__SIG, 0)?.read_wave_fmt()
|
|
}
|
|
|
|
/**
|
|
* The Broadcast-WAV metadata record for this file.
|
|
*/
|
|
pub fn broadcast_extension(&mut self) -> Result<Bext, ParserError> {
|
|
self.chunk_reader(BEXT_SIG, 0)?.read_bext()
|
|
}
|
|
}
|
|
|
|
impl<R:Read+Seek> WaveReader<R> { /* Private Implementation */
|
|
|
|
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) )
|
|
}
|
|
|
|
pub 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) {
|
|
Ok ((chunk.start, chunk.length))
|
|
} else {
|
|
Err( ParserError::ChunkMissing { signature : fourcc })
|
|
}
|
|
}
|
|
}
|