symphonia-format-wav-0.5.4/.cargo_vcs_info.json0000644000000001620000000000100151310ustar { "git": { "sha1": "d3b7742fa73674b70d9ab80cc5f8384cc653df3a" }, "path_in_vcs": "symphonia-format-wav" }symphonia-format-wav-0.5.4/Cargo.toml0000644000000021670000000000100131360ustar # 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.4" 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.4" [dependencies.symphonia-metadata] version = "0.5.4" symphonia-format-wav-0.5.4/Cargo.toml.orig000064400000000000000000000012351046102023000166120ustar 00000000000000[package] name = "symphonia-format-wav" version = "0.5.4" 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.4", path = "../symphonia-core" } symphonia-metadata = { version = "0.5.4", path = "../symphonia-metadata" }symphonia-format-wav-0.5.4/README.md000064400000000000000000000011711046102023000152010ustar 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 a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). symphonia-format-wav-0.5.4/src/chunks.rs000064400000000000000000000725531046102023000163660ustar 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, 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 } } fn fix_channel_mask(mut channel_mask: u32, n_channels: u16) -> u32 { let channel_diff = n_channels as i32 - channel_mask.count_ones() as i32; if channel_diff != 0 { info!("Channel mask not set correctly, channel positions may be incorrect!"); } // Check that the number of ones in the channel mask match the number of channels. if channel_diff > 0 { // Too few ones in mask so add extra ones above the most significant one let shift = 32 - (!channel_mask).leading_ones(); channel_mask |= ((1 << channel_diff) - 1) << shift; } else { // Too many ones in mask so remove the most significant extra ones while channel_mask.count_ones() != n_channels as u32 { let highest_one = 31 - (!channel_mask).leading_ones(); channel_mask &= !(1 << highest_one); } } channel_mask } #[test] fn test_fix_channel_mask() { // Too few assert_eq!(fix_channel_mask(0, 9), 0b111111111); assert_eq!(fix_channel_mask(0b101000, 5), 0b111101000); // Too many assert_eq!(fix_channel_mask(0b1111111, 0), 0); assert_eq!(fix_channel_mask(0b101110111010, 5), 0b10111010); assert_eq!(fix_channel_mask(0xFFFFFFFF, 8), 0b11111111); } fn try_channel_count_to_mask(count: u16) -> Result { (1..=32) .contains(&count) .then(|| Channels::from_bits(((1u64 << count) - 1) as u32)) .flatten() .ok_or(Error::DecodeError("wav: invalid channel count")) } #[test] fn test_try_channel_count_to_mask() { assert!(try_channel_count_to_mask(0).is_err()); for i in 1..27 { assert!(try_channel_count_to_mask(i).is_ok()); } for i in 27..u16::MAX { assert!(try_channel_count_to_mask(i).is_err()); } } /// `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", ) } }; let channels = try_channel_count_to_mask(n_channels)?; 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)?; let channels = try_channel_count_to_mask(n_channels)?; 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. match len { 16 => (), 18 => { let extra_size = reader.read_u16()?; if extra_size != 0 { return decode_error("wav: extra data not expected for fmt_ieee chunk"); } } 40 => { // WAVEFORMATEXTENSIBLE is used for formats having more than two channels // or higher sample resolutions than allowed by WAVEFORMATEX but for now // we just ignore it let _ = reader.ignore_bytes(40 - 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"), }; let channels = try_channel_count_to_mask(n_channels)?; 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 = fix_channel_mask(reader.read_u32()?, n_channels); // 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 = try_channel_count_to_mask(n_channels)?; 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 = try_channel_count_to_mask(n_channels)?; 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.4/src/lib.rs000064400000000000000000000343241046102023000156330ustar 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/. #![deprecated(since = "0.5.4", note = "superseded by `symphonia-format-riff`")] #![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); } }