symphonia-format-wav-0.5.2/.cargo_vcs_info.json0000644000000001620000000000100151270ustar { "git": { "sha1": "412f44daab39920beeb81d78b0e4271b263d33e9" }, "path_in_vcs": "symphonia-format-wav" }symphonia-format-wav-0.5.2/Cargo.toml0000644000000021670000000000100131340ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.53" name = "symphonia-format-wav" version = "0.5.2" authors = ["Philip Deljanov "] description = "Pure Rust WAV demuxer (a part of project Symphonia)." homepage = "https://github.com/pdeljanov/Symphonia" readme = "README.md" keywords = [ "audio", "media", "demuxer", "wav", "riff", ] categories = [ "multimedia", "multimedia::audio", "multimedia::encoding", ] license = "MPL-2.0" repository = "https://github.com/pdeljanov/Symphonia" [dependencies.log] version = "0.4" [dependencies.symphonia-core] version = "0.5.2" [dependencies.symphonia-metadata] version = "0.5.2" symphonia-format-wav-0.5.2/Cargo.toml.orig000064400000000000000000000012351046102023000166100ustar 00000000000000[package] name = "symphonia-format-wav" version = "0.5.2" description = "Pure Rust WAV demuxer (a part of project Symphonia)." homepage = "https://github.com/pdeljanov/Symphonia" repository = "https://github.com/pdeljanov/Symphonia" authors = ["Philip Deljanov "] license = "MPL-2.0" readme = "README.md" categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] keywords = ["audio", "media", "demuxer", "wav", "riff"] edition = "2018" rust-version = "1.53" [dependencies] log = "0.4" symphonia-core = { version = "0.5.2", path = "../symphonia-core" } symphonia-metadata = { version = "0.5.2", path = "../symphonia-metadata" }symphonia-format-wav-0.5.2/README.md000064400000000000000000000013571046102023000152050ustar 00000000000000# Symphonia Wave Codec [![Docs](https://docs.rs/symphonia-format-wav/badge.svg)](https://docs.rs/symphonia-format-wav) WAV decoder for Project Symphonia. **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. ## License Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. ## Contributing Symphonia is an open-source project and contributions are very welcome! If you would like to make a large contribution, please raise an issue ahead of time to make sure your efforts fit into the project goals, and that no duplication of efforts occurs. All contributors will be credited within the CONTRIBUTORS file. symphonia-format-wav-0.5.2/src/chunks.rs000064400000000000000000000714341046102023000163610ustar 00000000000000// Symphonia // Copyright (c) 2019-2022 The Project Symphonia Developers. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. use std::fmt; use std::marker::PhantomData; use symphonia_core::audio::Channels; use symphonia_core::codecs::CodecType; use symphonia_core::codecs::{ CODEC_TYPE_ADPCM_IMA_WAV, CODEC_TYPE_ADPCM_MS, CODEC_TYPE_PCM_ALAW, CODEC_TYPE_PCM_F32LE, CODEC_TYPE_PCM_F64LE, CODEC_TYPE_PCM_MULAW, CODEC_TYPE_PCM_S16LE, CODEC_TYPE_PCM_S24LE, CODEC_TYPE_PCM_S32LE, CODEC_TYPE_PCM_U8, }; use symphonia_core::errors::{decode_error, unsupported_error, Result}; use symphonia_core::io::ReadBytes; use symphonia_core::meta::Tag; use symphonia_metadata::riff; use log::info; use crate::PacketInfo; /// `ParseChunkTag` implements `parse_tag` to map between the 4-byte chunk identifier and the /// enumeration pub trait ParseChunkTag: Sized { fn parse_tag(tag: [u8; 4], len: u32) -> Option; } enum NullChunks {} impl ParseChunkTag for NullChunks { fn parse_tag(_tag: [u8; 4], _len: u32) -> Option { None } } /// `ChunksReader` reads chunks from a `ByteStream`. It is generic across a type, usually an enum, /// implementing the `ParseChunkTag` trait. When a new chunk is encountered in the stream, /// `parse_tag` on T is called to return an object capable of parsing/reading that chunk or `None`. /// This makes reading the actual chunk data lazy in that the chunk is not read until the object is /// consumed. pub struct ChunksReader { len: u32, consumed: u32, phantom: PhantomData, } impl ChunksReader { pub fn new(len: u32) -> Self { ChunksReader { len, consumed: 0, phantom: PhantomData } } pub fn next(&mut self, reader: &mut B) -> Result> { // Loop until a chunk is recognized and returned, or the end of stream is reached. loop { // Align to the next 2-byte boundary if not currently aligned. if self.consumed & 0x1 == 1 { reader.read_u8()?; self.consumed += 1; } // Check if there are enough bytes for another chunk, if not, there are no more chunks. if self.consumed + 8 > self.len { return Ok(None); } // Read tag and len, the chunk header. let tag = reader.read_quad_bytes()?; let len = reader.read_u32()?; self.consumed += 8; // Check if the ChunkReader has enough unread bytes to fully read the chunk. // // Warning: the formulation of this conditional is critical because len is untrusted // input, it may overflow when if added to anything. if self.len - self.consumed < len { // When ffmpeg encodes wave to stdout the riff (parent) and data chunk lengths are // (2^32)-1 since the size can't be known ahead of time. if !(self.len == len && len == u32::MAX) { return decode_error("wav: chunk length exceeds parent (list) chunk length"); } } // The length of the chunk has been validated, so "consume" the chunk. self.consumed = self.consumed.saturating_add(len); match T::parse_tag(tag, len) { Some(chunk) => return Ok(Some(chunk)), None => { // As per the RIFF spec, unknown chunks are to be ignored. info!( "ignoring unknown chunk: tag={}, len={}.", String::from_utf8_lossy(&tag), len ); reader.ignore_bytes(u64::from(len))? } } } } pub fn finish(&mut self, reader: &mut B) -> Result<()> { // If data is remaining in this chunk, skip it. if self.consumed < self.len { let remaining = self.len - self.consumed; reader.ignore_bytes(u64::from(remaining))?; self.consumed += remaining; } // Pad the chunk to the next 2-byte boundary. if self.len & 0x1 == 1 { reader.read_u8()?; } Ok(()) } } /// Common trait implemented for all chunks that are parsed by a `ChunkParser`. pub trait ParseChunk: Sized { fn parse(reader: &mut B, tag: [u8; 4], len: u32) -> Result; } /// `ChunkParser` is a utility struct for unifying the parsing of chunks. pub struct ChunkParser { tag: [u8; 4], len: u32, phantom: PhantomData

, } impl ChunkParser

{ fn new(tag: [u8; 4], len: u32) -> Self { ChunkParser { tag, len, phantom: PhantomData } } pub fn parse(&self, reader: &mut B) -> Result

{ P::parse(reader, self.tag, self.len) } } pub enum WaveFormatData { Pcm(WaveFormatPcm), Adpcm(WaveFormatAdpcm), IeeeFloat(WaveFormatIeeeFloat), Extensible(WaveFormatExtensible), ALaw(WaveFormatALaw), MuLaw(WaveFormatMuLaw), } pub struct WaveFormatPcm { /// The number of bits per sample. In the PCM format, this is always a multiple of 8-bits. pub bits_per_sample: u16, /// Channel bitmask. pub channels: Channels, /// Codec type. pub codec: CodecType, } pub struct WaveFormatAdpcm { /// The number of bits per sample. At the moment only 4bit is supported. pub bits_per_sample: u16, /// Channel bitmask. pub channels: Channels, /// Codec type. pub codec: CodecType, } pub struct WaveFormatIeeeFloat { /// Channel bitmask. pub channels: Channels, /// Codec type. pub codec: CodecType, } pub struct WaveFormatExtensible { /// The number of bits per sample as stored in the stream. This value is always a multiple of /// 8-bits. pub bits_per_sample: u16, /// The number of bits per sample that are valid. This number is always less than the number /// of bits per sample. pub bits_per_coded_sample: u16, /// Channel bitmask. pub channels: Channels, /// Globally unique identifier of the format. pub sub_format_guid: [u8; 16], /// Codec type. pub codec: CodecType, } pub struct WaveFormatALaw { /// Channel bitmask. pub channels: Channels, /// Codec type. pub codec: CodecType, } pub struct WaveFormatMuLaw { /// Channel bitmask. pub channels: Channels, /// Codec type. pub codec: CodecType, } pub struct WaveFormatChunk { /// The number of channels. pub n_channels: u16, /// The sample rate in Hz. For non-PCM formats, this value must be interpreted as per the /// format's specifications. pub sample_rate: u32, /// The required average data rate required in bytes/second. For non-PCM formats, this value /// must be interpreted as per the format's specifications. pub avg_bytes_per_sec: u32, /// The byte alignment of one audio frame. For PCM formats, this is equal to /// `(n_channels * format_data.bits_per_sample) / 8`. For non-PCM formats, this value must be /// interpreted as per the format's specifications. pub block_align: u16, /// Extra data associated with the format block conditional upon the format tag. pub format_data: WaveFormatData, } impl WaveFormatChunk { fn read_pcm_fmt( reader: &mut B, bits_per_sample: u16, n_channels: u16, len: u32, ) -> Result { // WaveFormat for a PCM format may be extended with an extra data length field followed by // the extension data itself. Use the chunk length to determine if the format chunk is // extended. match len { // Basic WavFormat struct, no extension. 16 => (), // WaveFormatEx with extension data length field present, but no extension data. 18 => { // Extension data length should be 0. let _extension_len = reader.read_be_u16()?; } // WaveFormatEx with extension data length field present, and extension data. 40 => { // Extension data length should be either 0 or 22 (if valid data is present). let _extension_len = reader.read_u16()?; reader.ignore_bytes(22)?; } _ => return decode_error("wav: malformed fmt_pcm chunk"), } // Bits per sample for PCM is both the encoded sample width, and the actual sample width. // Strictly, this must either be 8 or 16 bits, but there is no reason why 24 and 32 bits // can't be supported. Since these files do exist, allow for 8/16/24/32-bit samples, but // error if not a multiple of 8 or greater than 32-bits. // // Select the appropriate codec using bits per sample. Samples are always interleaved and // little-endian encoded for the PCM format. let codec = match bits_per_sample { 8 => CODEC_TYPE_PCM_U8, 16 => CODEC_TYPE_PCM_S16LE, 24 => CODEC_TYPE_PCM_S24LE, 32 => CODEC_TYPE_PCM_S32LE, _ => { return decode_error( "wav: bits per sample for fmt_pcm must be 8, 16, 24 or 32 bits", ) } }; // The PCM format only supports 1 or 2 channels, for mono and stereo channel layouts, // respectively. let channels = match n_channels { 1 => Channels::FRONT_LEFT, 2 => Channels::FRONT_LEFT | Channels::FRONT_RIGHT, _ => return decode_error("wav: channel layout is not stereo or mono for fmt_pcm"), }; Ok(WaveFormatData::Pcm(WaveFormatPcm { bits_per_sample, channels, codec })) } fn read_adpcm_fmt( reader: &mut B, bits_per_sample: u16, n_channels: u16, len: u32, codec: CodecType, ) -> Result { if bits_per_sample != 4 { return decode_error("wav: bits per sample for fmt_adpcm must be 4 bits"); } // WaveFormatEx with extension data length field present and with atleast frames per block data. if len < 20 { return decode_error("wav: malformed fmt_adpcm chunk"); } let extra_size = reader.read_u16()? as u64; match codec { CODEC_TYPE_ADPCM_MS if extra_size < 32 => { return decode_error("wav: malformed fmt_adpcm chunk"); } CODEC_TYPE_ADPCM_IMA_WAV if extra_size != 2 => { return decode_error("wav: malformed fmt_adpcm chunk"); } _ => (), } reader.ignore_bytes(extra_size)?; // The ADPCM format only supports 1 or 2 channels, for mono and stereo channel layouts, // respectively. let channels = match n_channels { 1 => Channels::FRONT_LEFT, 2 => Channels::FRONT_LEFT | Channels::FRONT_RIGHT, _ => return decode_error("wav: channel layout is not stereo or mono for fmt_adpcm"), }; Ok(WaveFormatData::Adpcm(WaveFormatAdpcm { bits_per_sample, channels, codec })) } fn read_ieee_fmt( reader: &mut B, bits_per_sample: u16, n_channels: u16, len: u32, ) -> Result { // WaveFormat for a IEEE format should not be extended, but it may still have an extra data // length parameter. if len == 18 { let extra_size = reader.read_u16()?; if extra_size != 0 { return decode_error("wav: extra data not expected for fmt_ieee chunk"); } } else if len > 16 { return decode_error("wav: malformed fmt_ieee chunk"); } // Officially, only 32-bit floats are supported, but Symphonia can handle 64-bit floats. // // Select the appropriate codec using bits per sample. Samples are always interleaved and // little-endian encoded for the IEEE Float format. let codec = match bits_per_sample { 32 => CODEC_TYPE_PCM_F32LE, 64 => CODEC_TYPE_PCM_F64LE, _ => return decode_error("wav: bits per sample for fmt_ieee must be 32 or 64 bits"), }; // The IEEE format only supports 1 or 2 channels, for mono and stereo channel layouts, // respectively. let channels = match n_channels { 1 => Channels::FRONT_LEFT, 2 => Channels::FRONT_LEFT | Channels::FRONT_RIGHT, _ => return decode_error("wav: channel layout is not stereo or mono for fmt_ieee"), }; Ok(WaveFormatData::IeeeFloat(WaveFormatIeeeFloat { channels, codec })) } fn read_ext_fmt( reader: &mut B, bits_per_coded_sample: u16, n_channels: u16, len: u32, ) -> Result { // WaveFormat for the extensible format must be extended to 40 bytes in length. if len < 40 { return decode_error("wav: malformed fmt_ext chunk"); } let extra_size = reader.read_u16()?; // The size of the extra data for the Extensible format is exactly 22 bytes. if extra_size != 22 { return decode_error("wav: extra data size not 22 bytes for fmt_ext chunk"); } let bits_per_sample = reader.read_u16()?; // Bits per coded sample for extensible formats is the width per sample as stored in the // stream. This must be a multiple of 8. if (bits_per_coded_sample & 0x7) != 0 { return decode_error("wav: bits per coded sample for fmt_ext must be a multiple of 8"); } // Bits per sample indicates the number of valid bits in the encoded sample. The sample is // encoded in a bits per coded sample width value, therefore the valid number of bits must // be at most bits per coded sample long. if bits_per_sample > bits_per_coded_sample { return decode_error( "wav: bits per sample must be <= bits per coded sample for fmt_ext", ); } let channel_mask = reader.read_u32()?; // The number of ones in the channel mask should match the number of channels. if channel_mask.count_ones() != u32::from(n_channels) { return decode_error("wav: channel mask mismatch with number of channels for fmt_ext"); } // Try to map channels. let channels = match Channels::from_bits(channel_mask) { Some(channels) => channels, _ => return unsupported_error("wav: too many channels in mask for fmt_ext"), }; let mut sub_format_guid = [0u8; 16]; reader.read_buf_exact(&mut sub_format_guid)?; // These GUIDs identifiy the format of the data chunks. These definitions can be found in // ksmedia.h of the Microsoft Windows Platform SDK. #[rustfmt::skip] const KSDATAFORMAT_SUBTYPE_PCM: [u8; 16] = [ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71, ]; // #[rustfmt::skip] // const KSDATAFORMAT_SUBTYPE_ADPCM: [u8; 16] = [ // 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, // 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71, // ]; #[rustfmt::skip] const KSDATAFORMAT_SUBTYPE_IEEE_FLOAT: [u8; 16] = [ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71, ]; #[rustfmt::skip] const KSDATAFORMAT_SUBTYPE_ALAW: [u8; 16] = [ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71, ]; #[rustfmt::skip] const KSDATAFORMAT_SUBTYPE_MULAW: [u8; 16] = [ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71, ]; // Verify support based on the format GUID. let codec = match sub_format_guid { KSDATAFORMAT_SUBTYPE_PCM => { // Only support up-to 32-bit integer samples. if bits_per_coded_sample > 32 { return decode_error( "bits per sample for fmt_ext PCM sub-type must be <= 32 bits", ); } // Use bits per coded sample to select the codec to use. If bits per sample is less // than the bits per coded sample, the codec will expand the sample during decode. match bits_per_coded_sample { 8 => CODEC_TYPE_PCM_U8, 16 => CODEC_TYPE_PCM_S16LE, 24 => CODEC_TYPE_PCM_S24LE, 32 => CODEC_TYPE_PCM_S32LE, _ => unreachable!(), } } KSDATAFORMAT_SUBTYPE_IEEE_FLOAT => { // IEEE floating formats do not support truncated sample widths. if bits_per_sample != bits_per_coded_sample { return decode_error( "wav: bits per sample for fmt_ext IEEE sub-type must equal bits per coded sample" ); } // Select the appropriate codec based on the bits per coded sample. match bits_per_coded_sample { 32 => CODEC_TYPE_PCM_F32LE, 64 => CODEC_TYPE_PCM_F64LE, _ => { return decode_error( "wav: bits per sample for fmt_ext IEEE sub-type must be 32 or 64 bits", ) } } } KSDATAFORMAT_SUBTYPE_ALAW => CODEC_TYPE_PCM_ALAW, KSDATAFORMAT_SUBTYPE_MULAW => CODEC_TYPE_PCM_MULAW, _ => return unsupported_error("wav: unsupported fmt_ext sub-type"), }; Ok(WaveFormatData::Extensible(WaveFormatExtensible { bits_per_sample, bits_per_coded_sample, channels, sub_format_guid, codec, })) } fn read_alaw_pcm_fmt( reader: &mut B, n_channels: u16, len: u32, ) -> Result { if len != 18 { return decode_error("wav: malformed fmt_alaw chunk"); } let extra_size = reader.read_u16()?; if extra_size > 0 { reader.ignore_bytes(u64::from(extra_size))?; } let channels = match n_channels { 1 => Channels::FRONT_LEFT, 2 => Channels::FRONT_LEFT | Channels::FRONT_RIGHT, _ => return decode_error("wav: channel layout is not stereo or mono for fmt_alaw"), }; Ok(WaveFormatData::ALaw(WaveFormatALaw { codec: CODEC_TYPE_PCM_ALAW, channels })) } fn read_mulaw_pcm_fmt( reader: &mut B, n_channels: u16, len: u32, ) -> Result { if len != 18 { return decode_error("wav: malformed fmt_mulaw chunk"); } let extra_size = reader.read_u16()?; if extra_size > 0 { reader.ignore_bytes(u64::from(extra_size))?; } let channels = match n_channels { 1 => Channels::FRONT_LEFT, 2 => Channels::FRONT_LEFT | Channels::FRONT_RIGHT, _ => return decode_error("wav: channel layout is not stereo or mono for fmt_mulaw"), }; Ok(WaveFormatData::MuLaw(WaveFormatMuLaw { codec: CODEC_TYPE_PCM_MULAW, channels })) } pub(crate) fn packet_info(&self) -> Result { match self.format_data { WaveFormatData::Adpcm(WaveFormatAdpcm { codec, bits_per_sample, .. }) //| WaveFormatData::Extensible(WaveFormatExtensible { codec, bits_per_sample, .. }) if codec == CODEC_TYPE_ADPCM_MS => { let frames_per_block = ((((self.block_align - (7 * self.n_channels)) * 8) / (bits_per_sample * self.n_channels)) + 2) as u64; PacketInfo::with_blocks(self.block_align, frames_per_block) } WaveFormatData::Adpcm(WaveFormatAdpcm { codec, bits_per_sample, .. }) if codec == CODEC_TYPE_ADPCM_IMA_WAV => { let frames_per_block = (((self.block_align - (4 * self.n_channels)) * 8) / (bits_per_sample * self.n_channels) + 1) as u64; PacketInfo::with_blocks(self.block_align, frames_per_block) } _ => Ok(PacketInfo::without_blocks(self.block_align)), } } } impl ParseChunk for WaveFormatChunk { fn parse(reader: &mut B, _tag: [u8; 4], len: u32) -> Result { // WaveFormat has a minimal length of 16 bytes. This may be extended with format specific // data later. if len < 16 { return decode_error("wav: malformed fmt chunk"); } let format = reader.read_u16()?; let n_channels = reader.read_u16()?; let sample_rate = reader.read_u32()?; let avg_bytes_per_sec = reader.read_u32()?; let block_align = reader.read_u16()?; let bits_per_sample = reader.read_u16()?; // The definition of these format identifiers can be found in mmreg.h of the Microsoft // Windows Platform SDK. const WAVE_FORMAT_PCM: u16 = 0x0001; const WAVE_FORMAT_ADPCM: u16 = 0x0002; const WAVE_FORMAT_IEEE_FLOAT: u16 = 0x0003; const WAVE_FORMAT_ALAW: u16 = 0x0006; const WAVE_FORMAT_MULAW: u16 = 0x0007; const WAVE_FORMAT_ADPCM_IMA: u16 = 0x0011; const WAVE_FORMAT_EXTENSIBLE: u16 = 0xfffe; let format_data = match format { // The PCM Wave Format WAVE_FORMAT_PCM => Self::read_pcm_fmt(reader, bits_per_sample, n_channels, len), // The Microsoft ADPCM Format WAVE_FORMAT_ADPCM => { Self::read_adpcm_fmt(reader, bits_per_sample, n_channels, len, CODEC_TYPE_ADPCM_MS) } // The IEEE Float Wave Format WAVE_FORMAT_IEEE_FLOAT => Self::read_ieee_fmt(reader, bits_per_sample, n_channels, len), // The Extensible Wave Format WAVE_FORMAT_EXTENSIBLE => Self::read_ext_fmt(reader, bits_per_sample, n_channels, len), // The Alaw Wave Format. WAVE_FORMAT_ALAW => Self::read_alaw_pcm_fmt(reader, n_channels, len), // The MuLaw Wave Format. WAVE_FORMAT_MULAW => Self::read_mulaw_pcm_fmt(reader, n_channels, len), // The IMA ADPCM Format WAVE_FORMAT_ADPCM_IMA => Self::read_adpcm_fmt( reader, bits_per_sample, n_channels, len, CODEC_TYPE_ADPCM_IMA_WAV, ), // Unsupported format. _ => return unsupported_error("wav: unsupported wave format"), }?; Ok(WaveFormatChunk { n_channels, sample_rate, avg_bytes_per_sec, block_align, format_data }) } } impl fmt::Display for WaveFormatChunk { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "WaveFormatChunk {{")?; writeln!(f, "\tn_channels: {},", self.n_channels)?; writeln!(f, "\tsample_rate: {} Hz,", self.sample_rate)?; writeln!(f, "\tavg_bytes_per_sec: {},", self.avg_bytes_per_sec)?; writeln!(f, "\tblock_align: {},", self.block_align)?; match self.format_data { WaveFormatData::Pcm(ref pcm) => { writeln!(f, "\tformat_data: Pcm {{")?; writeln!(f, "\t\tbits_per_sample: {},", pcm.bits_per_sample)?; writeln!(f, "\t\tchannels: {},", pcm.channels)?; writeln!(f, "\t\tcodec: {},", pcm.codec)?; } WaveFormatData::Adpcm(ref adpcm) => { writeln!(f, "\tformat_data: Adpcm {{")?; writeln!(f, "\t\tbits_per_sample: {},", adpcm.bits_per_sample)?; writeln!(f, "\t\tchannels: {},", adpcm.channels)?; writeln!(f, "\t\tcodec: {},", adpcm.codec)?; } WaveFormatData::IeeeFloat(ref ieee) => { writeln!(f, "\tformat_data: IeeeFloat {{")?; writeln!(f, "\t\tchannels: {},", ieee.channels)?; writeln!(f, "\t\tcodec: {},", ieee.codec)?; } WaveFormatData::Extensible(ref ext) => { writeln!(f, "\tformat_data: Extensible {{")?; writeln!(f, "\t\tbits_per_sample: {},", ext.bits_per_sample)?; writeln!(f, "\t\tbits_per_coded_sample: {},", ext.bits_per_coded_sample)?; writeln!(f, "\t\tchannels: {},", ext.channels)?; writeln!(f, "\t\tsub_format_guid: {:?},", &ext.sub_format_guid)?; writeln!(f, "\t\tcodec: {},", ext.codec)?; } WaveFormatData::ALaw(ref alaw) => { writeln!(f, "\tformat_data: ALaw {{")?; writeln!(f, "\t\tchannels: {},", alaw.channels)?; writeln!(f, "\t\tcodec: {},", alaw.codec)?; } WaveFormatData::MuLaw(ref mulaw) => { writeln!(f, "\tformat_data: MuLaw {{")?; writeln!(f, "\t\tchannels: {},", mulaw.channels)?; writeln!(f, "\t\tcodec: {},", mulaw.codec)?; } }; writeln!(f, "\t}}")?; writeln!(f, "}}") } } pub struct FactChunk { pub n_frames: u32, } impl ParseChunk for FactChunk { fn parse(reader: &mut B, _tag: [u8; 4], len: u32) -> Result { // A Fact chunk is exactly 4 bytes long, though there is some mystery as to whether there // can be more fields in the chunk. if len != 4 { return decode_error("wav: malformed fact chunk"); } Ok(FactChunk { n_frames: reader.read_u32()? }) } } impl fmt::Display for FactChunk { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "FactChunk {{")?; writeln!(f, "\tn_frames: {},", self.n_frames)?; writeln!(f, "}}") } } pub struct ListChunk { pub form: [u8; 4], pub len: u32, } impl ListChunk { pub fn skip(&self, reader: &mut B) -> Result<()> { ChunksReader::::new(self.len).finish(reader) } } impl ParseChunk for ListChunk { fn parse(reader: &mut B, _tag: [u8; 4], len: u32) -> Result { // A List chunk must contain atleast the list/form identifier. However, an empty list // (len == 4) is permissible. if len < 4 { return decode_error("wav: malformed list chunk"); } Ok(ListChunk { form: reader.read_quad_bytes()?, len: len - 4 }) } } impl fmt::Display for ListChunk { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "ListChunk {{")?; writeln!(f, "\tform: {},", String::from_utf8_lossy(&self.form))?; writeln!(f, "\tlen: {},", self.len)?; writeln!(f, "}}") } } pub struct InfoChunk { pub tag: Tag, } impl ParseChunk for InfoChunk { fn parse(reader: &mut B, tag: [u8; 4], len: u32) -> Result { // TODO: Apply limit. let mut value_buf = vec![0u8; len as usize]; reader.read_buf_exact(&mut value_buf)?; Ok(InfoChunk { tag: riff::parse(tag, &value_buf) }) } } pub struct DataChunk { pub len: u32, } impl ParseChunk for DataChunk { fn parse(_: &mut B, _: [u8; 4], len: u32) -> Result { Ok(DataChunk { len }) } } pub enum RiffWaveChunks { Format(ChunkParser), List(ChunkParser), Fact(ChunkParser), Data(ChunkParser), } macro_rules! parser { ($class:expr, $result:ty, $tag:expr, $len:expr) => { Some($class(ChunkParser::<$result>::new($tag, $len))) }; } impl ParseChunkTag for RiffWaveChunks { fn parse_tag(tag: [u8; 4], len: u32) -> Option { match &tag { b"fmt " => parser!(RiffWaveChunks::Format, WaveFormatChunk, tag, len), b"LIST" => parser!(RiffWaveChunks::List, ListChunk, tag, len), b"fact" => parser!(RiffWaveChunks::Fact, FactChunk, tag, len), b"data" => parser!(RiffWaveChunks::Data, DataChunk, tag, len), _ => None, } } } pub enum RiffInfoListChunks { Info(ChunkParser), } impl ParseChunkTag for RiffInfoListChunks { fn parse_tag(tag: [u8; 4], len: u32) -> Option { // Right now it is assumed all list chunks are INFO chunks, but that's not really // guaranteed. // // TODO: Actually validate that the chunk is an info chunk. parser!(RiffInfoListChunks::Info, InfoChunk, tag, len) } } symphonia-format-wav-0.5.2/src/lib.rs000064400000000000000000000342041046102023000156260ustar 00000000000000// Symphonia // Copyright (c) 2019-2022 The Project Symphonia Developers. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. #![warn(rust_2018_idioms)] #![forbid(unsafe_code)] // The following lints are allowed in all Symphonia crates. Please see clippy.toml for their // justification. #![allow(clippy::comparison_chain)] #![allow(clippy::excessive_precision)] #![allow(clippy::identity_op)] #![allow(clippy::manual_range_contains)] use std::io::{Seek, SeekFrom}; use symphonia_core::codecs::CodecParameters; use symphonia_core::errors::{decode_error, end_of_stream_error, seek_error, unsupported_error}; use symphonia_core::errors::{Result, SeekErrorKind}; use symphonia_core::formats::prelude::*; use symphonia_core::io::*; use symphonia_core::meta::{Metadata, MetadataBuilder, MetadataLog, MetadataRevision}; use symphonia_core::probe::{Descriptor, Instantiate, QueryDescriptor}; use symphonia_core::support_format; use log::{debug, error}; mod chunks; use chunks::*; /// WAVE is actually a RIFF stream, with a "RIFF" ASCII stream marker. const WAVE_STREAM_MARKER: [u8; 4] = *b"RIFF"; /// The RIFF form is "wave". const WAVE_RIFF_FORM: [u8; 4] = *b"WAVE"; /// The maximum number of frames that will be in a packet. const WAVE_MAX_FRAMES_PER_PACKET: u64 = 1152; /// `PacketInfo` helps to simulate packetization over a number of blocks of data. /// In case the codec is blockless the block size equals one full audio frame in bytes. pub(crate) struct PacketInfo { block_size: u64, frames_per_block: u64, max_blocks_per_packet: u64, } impl PacketInfo { fn with_blocks(block_size: u16, frames_per_block: u64) -> Result { if frames_per_block == 0 { return decode_error("wav: frames per block is 0"); } Ok(Self { block_size: u64::from(block_size), frames_per_block, max_blocks_per_packet: frames_per_block.max(WAVE_MAX_FRAMES_PER_PACKET) / frames_per_block, }) } fn without_blocks(frame_len: u16) -> Self { Self { block_size: u64::from(frame_len), frames_per_block: 1, max_blocks_per_packet: WAVE_MAX_FRAMES_PER_PACKET, } } fn is_empty(&self) -> bool { self.block_size == 0 } fn get_max_frames_per_packet(&self) -> u64 { self.max_blocks_per_packet * self.frames_per_block } fn get_frames(&self, data_len: u64) -> u64 { data_len / self.block_size * self.frames_per_block } fn get_actual_ts(&self, ts: u64) -> u64 { let max_frames_per_packet = self.get_max_frames_per_packet(); ts / max_frames_per_packet * max_frames_per_packet } } /// WAVE (WAV) format reader. /// /// `WavReader` implements a demuxer for the WAVE container format. pub struct WavReader { reader: MediaSourceStream, tracks: Vec, cues: Vec, metadata: MetadataLog, packet_info: PacketInfo, data_start_pos: u64, data_end_pos: u64, } impl QueryDescriptor for WavReader { fn query() -> &'static [Descriptor] { &[ // WAVE RIFF form support_format!( "wave", "Waveform Audio File Format", &["wav", "wave"], &["audio/vnd.wave", "audio/x-wav", "audio/wav", "audio/wave"], &[b"RIFF"] ), ] } fn score(_context: &[u8]) -> u8 { 255 } } impl FormatReader for WavReader { fn try_new(mut source: MediaSourceStream, _options: &FormatOptions) -> Result { // The RIFF marker should be present. let marker = source.read_quad_bytes()?; if marker != WAVE_STREAM_MARKER { return unsupported_error("wav: missing riff stream marker"); } // A Wave file is one large RIFF chunk, with the actual meta and audio data as sub-chunks. // Therefore, the header was the chunk ID, and the next 4 bytes is the length of the RIFF // chunk. let riff_len = source.read_u32()?; let riff_form = source.read_quad_bytes()?; // The RIFF chunk contains WAVE data. if riff_form != WAVE_RIFF_FORM { error!("riff form is not wave ({})", String::from_utf8_lossy(&riff_form)); return unsupported_error("wav: riff form is not wave"); } let mut riff_chunks = ChunksReader::::new(riff_len); let mut codec_params = CodecParameters::new(); let mut metadata: MetadataLog = Default::default(); let mut packet_info = PacketInfo::without_blocks(0); loop { let chunk = riff_chunks.next(&mut source)?; // The last chunk should always be a data chunk, if it is not, then the stream is // unsupported. if chunk.is_none() { return unsupported_error("wav: missing data chunk"); } match chunk.unwrap() { RiffWaveChunks::Format(fmt) => { let format = fmt.parse(&mut source)?; // The Format chunk contains the block_align field and possible additional information // to handle packetization and seeking. packet_info = format.packet_info()?; codec_params .with_max_frames_per_packet(packet_info.get_max_frames_per_packet()) .with_frames_per_block(packet_info.frames_per_block); // Append Format chunk fields to codec parameters. append_format_params(&mut codec_params, format); } RiffWaveChunks::Fact(fct) => { let fact = fct.parse(&mut source)?; // Append Fact chunk fields to codec parameters. append_fact_params(&mut codec_params, &fact); } RiffWaveChunks::List(lst) => { let list = lst.parse(&mut source)?; // Riff Lists can have many different forms, but WavReader only supports Info // lists. match &list.form { b"INFO" => metadata.push(read_info_chunk(&mut source, list.len)?), _ => list.skip(&mut source)?, } } RiffWaveChunks::Data(dat) => { let data = dat.parse(&mut source)?; // Record the bounds of the data chunk. let data_start_pos = source.pos(); let data_end_pos = data_start_pos + u64::from(data.len); // Append Data chunk fields to codec parameters. append_data_params(&mut codec_params, &data, &packet_info); // Add a new track using the collected codec parameters. return Ok(WavReader { reader: source, tracks: vec![Track::new(0, codec_params)], cues: Vec::new(), metadata, packet_info, data_start_pos, data_end_pos, }); } } } // Chunks are processed until the Data chunk is found, or an error occurs. } fn next_packet(&mut self) -> Result { let pos = self.reader.pos(); if self.tracks.is_empty() { return decode_error("wav: no tracks"); } if self.packet_info.is_empty() { return decode_error("wav: block size is 0"); } // Determine the number of complete blocks remaining in the data chunk. let num_blocks_left = if pos < self.data_end_pos { (self.data_end_pos - pos) / self.packet_info.block_size } else { 0 }; if num_blocks_left == 0 { return end_of_stream_error(); } let blocks_per_packet = num_blocks_left.min(self.packet_info.max_blocks_per_packet); let dur = blocks_per_packet * self.packet_info.frames_per_block; let packet_len = blocks_per_packet * self.packet_info.block_size; // Copy the frames. let packet_buf = self.reader.read_boxed_slice(packet_len as usize)?; // The packet timestamp is the position of the first byte of the first frame in the // packet relative to the start of the data chunk divided by the length per frame. let pts = self.packet_info.get_frames(pos - self.data_start_pos); Ok(Packet::new_from_boxed_slice(0, pts, dur, packet_buf)) } fn metadata(&mut self) -> Metadata<'_> { self.metadata.metadata() } fn cues(&self) -> &[Cue] { &self.cues } fn tracks(&self) -> &[Track] { &self.tracks } fn seek(&mut self, _mode: SeekMode, to: SeekTo) -> Result { if self.tracks.is_empty() || self.packet_info.is_empty() { return seek_error(SeekErrorKind::Unseekable); } let params = &self.tracks[0].codec_params; let ts = match to { // Frame timestamp given. SeekTo::TimeStamp { ts, .. } => ts, // Time value given, calculate frame timestamp from sample rate. SeekTo::Time { time, .. } => { // Use the sample rate to calculate the frame timestamp. If sample rate is not // known, the seek cannot be completed. if let Some(sample_rate) = params.sample_rate { TimeBase::new(1, sample_rate).calc_timestamp(time) } else { return seek_error(SeekErrorKind::Unseekable); } } }; // If the total number of frames in the track is known, verify the desired frame timestamp // does not exceed it. if let Some(n_frames) = params.n_frames { if ts > n_frames { return seek_error(SeekErrorKind::OutOfRange); } } debug!("seeking to frame_ts={}", ts); // WAVE is not internally packetized for PCM codecs. Packetization is simulated by trying to // read a constant number of samples or blocks every call to next_packet. Therefore, a packet begins // wherever the data stream is currently positioned. Since timestamps on packets should be // determinstic, instead of seeking to the exact timestamp requested and starting the next // packet there, seek to a packet boundary. In this way, packets will have have the same // timestamps regardless if the stream was seeked or not. let actual_ts = self.packet_info.get_actual_ts(ts); // Calculate the absolute byte offset of the desired audio frame. let seek_pos = self.data_start_pos + (actual_ts * self.packet_info.block_size); // If the reader supports seeking we can seek directly to the frame's offset wherever it may // be. if self.reader.is_seekable() { self.reader.seek(SeekFrom::Start(seek_pos))?; } // If the reader does not support seeking, we can only emulate forward seeks by consuming // bytes. If the reader has to seek backwards, return an error. else { let current_pos = self.reader.pos(); if seek_pos >= current_pos { self.reader.ignore_bytes(seek_pos - current_pos)?; } else { return seek_error(SeekErrorKind::ForwardOnly); } } debug!("seeked to packet_ts={} (delta={})", actual_ts, actual_ts as i64 - ts as i64); Ok(SeekedTo { track_id: 0, actual_ts, required_ts: ts }) } fn into_inner(self: Box) -> MediaSourceStream { self.reader } } fn read_info_chunk(source: &mut MediaSourceStream, len: u32) -> Result { let mut info_list = ChunksReader::::new(len); let mut metadata_builder = MetadataBuilder::new(); loop { let chunk = info_list.next(source)?; if let Some(RiffInfoListChunks::Info(info)) = chunk { let parsed_info = info.parse(source)?; metadata_builder.add_tag(parsed_info.tag); } else { break; } } info_list.finish(source)?; Ok(metadata_builder.metadata()) } fn append_format_params(codec_params: &mut CodecParameters, format: WaveFormatChunk) { codec_params .with_sample_rate(format.sample_rate) .with_time_base(TimeBase::new(1, format.sample_rate)); match format.format_data { WaveFormatData::Pcm(pcm) => { codec_params .for_codec(pcm.codec) .with_bits_per_coded_sample(u32::from(pcm.bits_per_sample)) .with_bits_per_sample(u32::from(pcm.bits_per_sample)) .with_channels(pcm.channels); } WaveFormatData::Adpcm(adpcm) => { codec_params.for_codec(adpcm.codec).with_channels(adpcm.channels); } WaveFormatData::IeeeFloat(ieee) => { codec_params.for_codec(ieee.codec).with_channels(ieee.channels); } WaveFormatData::Extensible(ext) => { codec_params .for_codec(ext.codec) .with_bits_per_coded_sample(u32::from(ext.bits_per_coded_sample)) .with_bits_per_sample(u32::from(ext.bits_per_sample)) .with_channels(ext.channels); } WaveFormatData::ALaw(alaw) => { codec_params.for_codec(alaw.codec).with_channels(alaw.channels); } WaveFormatData::MuLaw(mulaw) => { codec_params.for_codec(mulaw.codec).with_channels(mulaw.channels); } } } fn append_fact_params(codec_params: &mut CodecParameters, fact: &FactChunk) { codec_params.with_n_frames(u64::from(fact.n_frames)); } fn append_data_params( codec_params: &mut CodecParameters, data: &DataChunk, packet_info: &PacketInfo, ) { if !packet_info.is_empty() { let n_frames = packet_info.get_frames(u64::from(data.len)); codec_params.with_n_frames(n_frames); } }