diff --git a/README.md b/README.md index 9a2db9c..c3053cc 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,14 @@ Rust Wave File Reader/Writer with Broadcast-WAV, MBWF and RF64 Support This is currently a work-in-progress! However many features presently work: -| Feature | Read | Write | -|---------------------------------------|:-----:|:-----:| +| Feature |Read |Write| +|---------------------------------------|:---:|:-----:| | Standard .wav files | ☑️ | ☑ ️ | | Transparent promotion to RF64/BW64 | ☑️ | | | Unified interface for regular and extended Wave format | ☑️ | | -| Channel/speaker map metadata | ☑️ | | +| Channel/speaker map metadata | ☑️ | ☑️ | | Ambisonic B-format metadata | ☑️ | | -| EBU Broadcast-WAVE metadata | ☑️ | | +| EBU Broadcast-WAVE metadata | ☑️ | ☑️ | | Basic iXML/ADM metadata | ☑️ | | | Enhanced iXML metadata support | | | | Broadcast-WAVE Level overview `levl` metadata | | | diff --git a/src/fmt.rs b/src/fmt.rs index 465c8f0..b5f0d6b 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -181,36 +181,57 @@ pub struct WaveFmt { impl WaveFmt { + + /// Create a new integer PCM format for a monoaural audio stream. + pub fn new_pcm_mono(sample_rate: u32, bits_per_sample: u16) -> Self { + Self::new_pcm_multichannel(sample_rate, bits_per_sample, 0x4) + } - /// Create a new integer PCM format `WaveFmt` - pub fn new_pcm(sample_rate: u32, bits_per_sample: u16, channel_count: u16) -> Self { + /// Create a new integer PCM format for a standard Left-Right stereo audio + /// stream. + pub fn new_pcm_stereo(sample_rate: u32, bits_per_sample: u16) -> Self { + Self::new_pcm_multichannel(sample_rate, bits_per_sample, 0x3) + } + + /// Create a new integer PCM format for ambisonic b-format. + pub fn new_pcm_ambisonic(sample_rate: u32, bits_per_sample: u16, channel_count: u16) -> Self { + todo!() + } + + /// Create a new integer PCM format `WaveFmt` with a custom channel bitmap. + /// + /// The order of `channels` is not important. When reading or writing + /// audio frames you must use the standard multichannel order for Wave + /// files, the numerical order of the cases of `ChannelMask`. + pub fn new_pcm_multichannel(sample_rate: u32, bits_per_sample: u16, channel_bitmap: u32) -> Self { let container_bits_per_sample = bits_per_sample + (bits_per_sample % 8); let container_bytes_per_sample= container_bits_per_sample / 8; + + let channel_count: u16 = (0..=31).fold(0u16, |accum, n| accum + (0x1 & (channel_bitmap >> n) as u16) ); - let tag : u16 = match channel_count { - 1..=2 => 0x01, - x if x > 2 => 0xFFFE, - x => panic!("Invalid channel count {}", x) + let result : (u16, Option) = match channel_bitmap { + ch if bits_per_sample != container_bits_per_sample => ( + (0xFFFE, Some(WaveFmtExtended { valid_bits_per_sample: bits_per_sample, channel_mask: ch, + type_guid: UUID_PCM }) ) + ), + 0b0100 => (0x0001, None), + 0b0011 => (0x0001, None), + ch => ( + (0xFFFE, Some( WaveFmtExtended { valid_bits_per_sample: bits_per_sample, channel_mask: ch, + type_guid: UUID_PCM})) + ) }; + let (tag, extformat) = result; + WaveFmt { - tag, + tag, channel_count, sample_rate, bytes_per_second: container_bytes_per_sample as u32 * sample_rate * channel_count as u32, block_alignment: container_bytes_per_sample * channel_count, bits_per_sample: container_bits_per_sample, - extended_format: { - if channel_count > 2 { - Some( WaveFmtExtended { - channel_mask : !(0xFFFF_FFFF << channel_count), - type_guid: UUID_PCM, - valid_bits_per_sample: bits_per_sample - }) - } else { - None - } - } + extended_format: extformat } } diff --git a/src/wavewriter.rs b/src/wavewriter.rs index ee516a2..566d179 100644 --- a/src/wavewriter.rs +++ b/src/wavewriter.rs @@ -130,7 +130,7 @@ impl Write for WaveChunkWriter where W: Write + Seek { /// /// // Write a three-sample wave file to a cursor /// let mut cursor = Cursor::new(vec![0u8;0]); -/// let format = WaveFmt::new_pcm(48000, 24, 1); +/// let format = WaveFmt::new_pcm_mono(48000, 24); /// let w = WaveWriter::new(&mut cursor, format).unwrap(); /// /// let mut frame_writer = w.audio_frame_writer().unwrap(); @@ -183,16 +183,13 @@ impl WaveWriter where W: Write + Seek { Ok( retval ) } - /// Create a new chunk writer, which takes posession of the `WaveWriter`. - /// - /// Begin writing a chunk segment. To close the chunk (and perhaps write - /// another), call `end()` on the chunk writer. + fn chunk(mut self, ident: FourCC) -> Result,Error> { self.inner.seek(SeekFrom::End(0))?; WaveChunkWriter::begin(self, ident) } - /// Write Broadcast-Wave metadata to the file.Bext + /// Write Broadcast-Wave metadata to the file. /// /// This function will write the metadata chunk immediately; if you have /// already written and closed the audio data the bext chunk will be @@ -244,7 +241,7 @@ fn test_new() { use byteorder::ReadBytesExt; let mut cursor = Cursor::new(vec![0u8;0]); - let format = WaveFmt::new_pcm(4800, 24, 1); + let format = WaveFmt::new_pcm_mono(4800, 24); WaveWriter::new(&mut cursor, format).unwrap(); cursor.seek(SeekFrom::Start(0)).unwrap(); @@ -270,7 +267,7 @@ fn test_write_audio() { use byteorder::ReadBytesExt; let mut cursor = Cursor::new(vec![0u8;0]); - let format = WaveFmt::new_pcm(48000, 24, 1); + let format = WaveFmt::new_pcm_mono(48000, 24); let w = WaveWriter::new(&mut cursor, format).unwrap(); let mut frame_writer = w.audio_frame_writer().unwrap(); @@ -316,7 +313,7 @@ fn test_write_bext() { use std::io::Cursor; let mut cursor = Cursor::new(vec![0u8;0]); - let format = WaveFmt::new_pcm(48000, 24, 1); + let format = WaveFmt::new_pcm_mono(48000, 24); let w = WaveWriter::new(&mut cursor, format).unwrap(); let bext = Bext {