From 96a616e06bf7a3b0c5f3334edc6682e0d085de9d Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sat, 7 Dec 2024 18:04:33 -0800 Subject: [PATCH 01/19] Adding channel_bitmasks For defining the channel format and layout of a source --- src/channel_bitmask.rs | 49 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 ++ 2 files changed, 51 insertions(+) create mode 100644 src/channel_bitmask.rs diff --git a/src/channel_bitmask.rs b/src/channel_bitmask.rs new file mode 100644 index 00000000..02b1fd25 --- /dev/null +++ b/src/channel_bitmask.rs @@ -0,0 +1,49 @@ + +pub type ChannelBitmask = u64; + +const FRONT_LEFT: ChannelBitmask = 0x1; +const FRONT_RIGHT: ChannelBitmask = 0x2; +const FRONT_CENTER: ChannelBitmask = 0x4; +const LFE: ChannelBitmask = 0x8; +const BACK_LEFT: ChannelBitmask = 0x10; +const BACK_RIGHT: ChannelBitmask = 0x20; +const FRONT_LEFT_CENTER: ChannelBitmask = 0x40; +const FRONT_RIGHT_CENTER: ChannelBitmask = 0x80; +const REAR_CENTER: ChannelBitmask = 0x100; +const SIDE_LEFT: ChannelBitmask = 0x200; +const SIDE_RIGHT: ChannelBitmask = 0x400; +const TOP_CENTER: ChannelBitmask = 0x800; +const TOP_FRONT_LEFT: ChannelBitmask = 0x1000; +const TOP_FRONT_CENTER: ChannelBitmask = 0x2000; +const TOP_FRONT_RIGHT: ChannelBitmask = 0x4000; +const TOP_BACK_LEFT: ChannelBitmask = 0x8000; +const TOP_BACK_CENTER: ChannelBitmask = 0x10000; +const TOP_BACK_RIGHT: ChannelBitmask = 0x20000; + +/// Left total. The left channel of phase-matrix encoded audio, as in Dolby Stereo. +const LEFT_TOTAL: ChannelBitmask = 0x40000; + +/// Right total. The right channel of phase-matrix encoded audio, as in Dolby Stereo. +const RIGHT_TOTAL: ChannelBitmask = 0x80000; + +const AMBISONIC_W: ChannelBitmask = 0x100000; +const AMBISONIC_X: ChannelBitmask = 0x200000; +const AMBISONIC_Y: ChannelBitmask = 0x400000; +const AMBISONIC_Z: ChannelBitmask = 0x800000; +const AMBISONIC_R: ChannelBitmask = 0x1000000; +const AMBISONIC_S: ChannelBitmask = 0x2000000; +const AMBISONIC_T: ChannelBitmask = 0x4000000; +const AMBISONIC_U: ChannelBitmask = 0x8000000; +const AMBISONIC_V: ChannelBitmask = 0x10000000; +const AMBISONIC_K: ChannelBitmask = 0x20000000; +const AMBISONIC_L: ChannelBitmask = 0x40000000; +const AMBISONIC_M: ChannelBitmask = 0x80000000; +const AMBISONIC_N: ChannelBitmask = 0x100000000; +const AMBISONIC_O: ChannelBitmask = 0x200000000; +const AMBUSONIC_P: ChannelBitmask = 0x400000000; +const AMBISONIC_Q: ChannelBitmask = 0x800000000; + +const UNDEFINED: ChannelBitmask = 0x0; +const STEREO: ChannelBitmask = FRONT_LEFT ^ FRONT_RIGHT; +const SURROUND_51: ChannelBitmask = FRONT_LEFT ^ FRONT_RIGHT ^ FRONT_CENTER ^ LFE ^ BACK_LEFT ^ BACK_RIGHT; +const SURROUND_71: ChannelBitmask = SURROUND_51 ^ SIDE_LEFT ^ SIDE_RIGHT; diff --git a/src/lib.rs b/src/lib.rs index ea0a3a35..7f06c98d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -156,6 +156,7 @@ pub use cpal::{ }; mod conversions; +mod channel_bitmask; mod sink; mod spatial_sink; mod stream; @@ -168,6 +169,7 @@ pub mod source; pub mod static_buffer; pub use crate::conversions::Sample; +pub use crate::channel_bitmask::ChannelBitmask; pub use crate::decoder::Decoder; pub use crate::sink::Sink; pub use crate::source::Source; From 28420039b301079d58fa15a32130ae14392a6195 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sat, 7 Dec 2024 18:24:21 -0800 Subject: [PATCH 02/19] Some typos, added some more formats --- src/channel_bitmask.rs | 96 ++++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 41 deletions(-) diff --git a/src/channel_bitmask.rs b/src/channel_bitmask.rs index 02b1fd25..c88d944d 100644 --- a/src/channel_bitmask.rs +++ b/src/channel_bitmask.rs @@ -1,49 +1,63 @@ - pub type ChannelBitmask = u64; -const FRONT_LEFT: ChannelBitmask = 0x1; -const FRONT_RIGHT: ChannelBitmask = 0x2; -const FRONT_CENTER: ChannelBitmask = 0x4; -const LFE: ChannelBitmask = 0x8; -const BACK_LEFT: ChannelBitmask = 0x10; -const BACK_RIGHT: ChannelBitmask = 0x20; -const FRONT_LEFT_CENTER: ChannelBitmask = 0x40; -const FRONT_RIGHT_CENTER: ChannelBitmask = 0x80; -const REAR_CENTER: ChannelBitmask = 0x100; -const SIDE_LEFT: ChannelBitmask = 0x200; -const SIDE_RIGHT: ChannelBitmask = 0x400; -const TOP_CENTER: ChannelBitmask = 0x800; -const TOP_FRONT_LEFT: ChannelBitmask = 0x1000; -const TOP_FRONT_CENTER: ChannelBitmask = 0x2000; -const TOP_FRONT_RIGHT: ChannelBitmask = 0x4000; -const TOP_BACK_LEFT: ChannelBitmask = 0x8000; -const TOP_BACK_CENTER: ChannelBitmask = 0x10000; -const TOP_BACK_RIGHT: ChannelBitmask = 0x20000; +pub const FRONT_LEFT: ChannelBitmask = 0x1; +pub const FRONT_RIGHT: ChannelBitmask = 0x2; +pub const FRONT_CENTER: ChannelBitmask = 0x4; +pub const LFE: ChannelBitmask = 0x8; +pub const BACK_LEFT: ChannelBitmask = 0x10; +pub const BACK_RIGHT: ChannelBitmask = 0x20; +pub const FRONT_LEFT_CENTER: ChannelBitmask = 0x40; +pub const FRONT_RIGHT_CENTER: ChannelBitmask = 0x80; +pub const REAR_CENTER: ChannelBitmask = 0x100; +pub const SIDE_LEFT: ChannelBitmask = 0x200; +pub const SIDE_RIGHT: ChannelBitmask = 0x400; +pub const TOP_CENTER: ChannelBitmask = 0x800; +pub const TOP_FRONT_LEFT: ChannelBitmask = 0x1000; +pub const TOP_FRONT_CENTER: ChannelBitmask = 0x2000; +pub const TOP_FRONT_RIGHT: ChannelBitmask = 0x4000; +pub const TOP_BACK_LEFT: ChannelBitmask = 0x8000; +pub const TOP_BACK_CENTER: ChannelBitmask = 0x10000; +pub const TOP_BACK_RIGHT: ChannelBitmask = 0x20000; /// Left total. The left channel of phase-matrix encoded audio, as in Dolby Stereo. -const LEFT_TOTAL: ChannelBitmask = 0x40000; +pub const LEFT_TOTAL: ChannelBitmask = 0x40000; /// Right total. The right channel of phase-matrix encoded audio, as in Dolby Stereo. -const RIGHT_TOTAL: ChannelBitmask = 0x80000; +pub const RIGHT_TOTAL: ChannelBitmask = 0x80000; + +pub const AMBISONIC_W: ChannelBitmask = 0x100000; +pub const AMBISONIC_X: ChannelBitmask = 0x200000; +pub const AMBISONIC_Y: ChannelBitmask = 0x400000; +pub const AMBISONIC_Z: ChannelBitmask = 0x800000; +pub const AMBISONIC_R: ChannelBitmask = 0x1000000; +pub const AMBISONIC_S: ChannelBitmask = 0x2000000; +pub const AMBISONIC_T: ChannelBitmask = 0x4000000; +pub const AMBISONIC_U: ChannelBitmask = 0x8000000; +pub const AMBISONIC_V: ChannelBitmask = 0x10000000; +pub const AMBISONIC_K: ChannelBitmask = 0x20000000; +pub const AMBISONIC_L: ChannelBitmask = 0x40000000; +pub const AMBISONIC_M: ChannelBitmask = 0x80000000; +pub const AMBISONIC_N: ChannelBitmask = 0x100000000; +pub const AMBISONIC_O: ChannelBitmask = 0x200000000; +pub const AMBISONIC_P: ChannelBitmask = 0x400000000; +pub const AMBISONIC_Q: ChannelBitmask = 0x800000000; -const AMBISONIC_W: ChannelBitmask = 0x100000; -const AMBISONIC_X: ChannelBitmask = 0x200000; -const AMBISONIC_Y: ChannelBitmask = 0x400000; -const AMBISONIC_Z: ChannelBitmask = 0x800000; -const AMBISONIC_R: ChannelBitmask = 0x1000000; -const AMBISONIC_S: ChannelBitmask = 0x2000000; -const AMBISONIC_T: ChannelBitmask = 0x4000000; -const AMBISONIC_U: ChannelBitmask = 0x8000000; -const AMBISONIC_V: ChannelBitmask = 0x10000000; -const AMBISONIC_K: ChannelBitmask = 0x20000000; -const AMBISONIC_L: ChannelBitmask = 0x40000000; -const AMBISONIC_M: ChannelBitmask = 0x80000000; -const AMBISONIC_N: ChannelBitmask = 0x100000000; -const AMBISONIC_O: ChannelBitmask = 0x200000000; -const AMBUSONIC_P: ChannelBitmask = 0x400000000; -const AMBISONIC_Q: ChannelBitmask = 0x800000000; +pub const UNDEFINED: ChannelBitmask = 0x0; +pub const STEREO: ChannelBitmask = FRONT_LEFT ^ FRONT_RIGHT; +pub const LCR: ChannelBitmask = FRONT_LEFT ^ FRONT_CENTER ^ FRONT_RIGHT; +pub const LCRS: ChannelBitmask = LCR ^ REAR_CENTER; +pub const SURROUND_51: ChannelBitmask = + FRONT_LEFT ^ FRONT_RIGHT ^ FRONT_CENTER ^ LFE ^ BACK_LEFT ^ BACK_RIGHT; +pub const SURROUND_71: ChannelBitmask = SURROUND_51 ^ SIDE_LEFT ^ SIDE_RIGHT; -const UNDEFINED: ChannelBitmask = 0x0; -const STEREO: ChannelBitmask = FRONT_LEFT ^ FRONT_RIGHT; -const SURROUND_51: ChannelBitmask = FRONT_LEFT ^ FRONT_RIGHT ^ FRONT_CENTER ^ LFE ^ BACK_LEFT ^ BACK_RIGHT; -const SURROUND_71: ChannelBitmask = SURROUND_51 ^ SIDE_LEFT ^ SIDE_RIGHT; +pub const AMBISONIC_O1: ChannelBitmask = AMBISONIC_W ^ AMBISONIC_X ^ AMBISONIC_Y ^ AMBISONIC_Z; +pub const AMBISONIC_O2: ChannelBitmask = + AMBISONIC_O1 ^ AMBISONIC_R ^ AMBISONIC_S ^ AMBISONIC_T ^ AMBISONIC_U ^ AMBISONIC_V; +pub const AMBISONIC_O3: ChannelBitmask = AMBISONIC_O2 + ^ AMBISONIC_K + ^ AMBISONIC_L + ^ AMBISONIC_M + ^ AMBISONIC_N + ^ AMBISONIC_O + ^ AMBISONIC_P + ^ AMBISONIC_Q; From e7cdbfc16f5068b2369a1534aa9f6fc166de4be7 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sat, 7 Dec 2024 21:01:57 -0800 Subject: [PATCH 03/19] Elaboration, WIP SourceChannelBitmask trait, some typos, a lot more documentation --- src/channel_bitmask.rs | 105 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) diff --git a/src/channel_bitmask.rs b/src/channel_bitmask.rs index c88d944d..a943dc9d 100644 --- a/src/channel_bitmask.rs +++ b/src/channel_bitmask.rs @@ -1,3 +1,6 @@ +#![allow(dead_code)] + +use crate::{Sample, Source}; pub type ChannelBitmask = u64; pub const FRONT_LEFT: ChannelBitmask = 0x1; @@ -8,7 +11,7 @@ pub const BACK_LEFT: ChannelBitmask = 0x10; pub const BACK_RIGHT: ChannelBitmask = 0x20; pub const FRONT_LEFT_CENTER: ChannelBitmask = 0x40; pub const FRONT_RIGHT_CENTER: ChannelBitmask = 0x80; -pub const REAR_CENTER: ChannelBitmask = 0x100; +pub const BACK_CENTER: ChannelBitmask = 0x100; pub const SIDE_LEFT: ChannelBitmask = 0x200; pub const SIDE_RIGHT: ChannelBitmask = 0x400; pub const TOP_CENTER: ChannelBitmask = 0x800; @@ -45,7 +48,7 @@ pub const AMBISONIC_Q: ChannelBitmask = 0x800000000; pub const UNDEFINED: ChannelBitmask = 0x0; pub const STEREO: ChannelBitmask = FRONT_LEFT ^ FRONT_RIGHT; pub const LCR: ChannelBitmask = FRONT_LEFT ^ FRONT_CENTER ^ FRONT_RIGHT; -pub const LCRS: ChannelBitmask = LCR ^ REAR_CENTER; +pub const LCRS: ChannelBitmask = LCR ^ BACK_CENTER; pub const SURROUND_51: ChannelBitmask = FRONT_LEFT ^ FRONT_RIGHT ^ FRONT_CENTER ^ LFE ^ BACK_LEFT ^ BACK_RIGHT; pub const SURROUND_71: ChannelBitmask = SURROUND_51 ^ SIDE_LEFT ^ SIDE_RIGHT; @@ -61,3 +64,101 @@ pub const AMBISONIC_O3: ChannelBitmask = AMBISONIC_O2 ^ AMBISONIC_O ^ AMBISONIC_P ^ AMBISONIC_Q; + +/// A trait for [`Source`]'s that provide a channel bitmask. +/// +/// Sources providing more than one channel of audio may be providing their channels in a +/// particular format for surround sound presentation (e.g. 5.1 surround). The +/// `SourceChannelBitmask` trait defines methods for a `Source` to inform a client the speaker +/// assignment or encoding component for each channel. +/// +/// The trait uses [`ChannelBitmask`] values to describe component assignments for channels in an +/// implementing `Source`. `ChannelBitmask` constants define a single bit in a `u64` for each +/// possible component. +/// +/// The presence of a given component in the source's output is signified by +/// the setting of its corresponding bit, and components are ordered in the Source's iteration +/// according to their corresponding `ChannelBitmask` value. +/// +/// For example: if a source is generating a six-channel stream of samples, it's `channels()` +/// method will return "6" and its its [`SourceChannelBitmask::channel_bitmask()`] will return +/// a bitmask equal to "0b111111" or `FRONT_LEFT ^ FRONT_RIGHT ^ FRONT_CENTER ^ LFE ^ BACK_LEFT ^ +/// BACK_RIGHT` (a constant `SURROUND_51` is provided that is equal to this.) The source will then +/// return its samples in the numerical order of these bitmasks: Left, Right, Center, LFE, Back +/// Left and Back Right. +trait SourceChannelBitmask: Source +where + Self::Item: Sample, +{ + /// The [`ChannelBitmask`] of the `Source`. + fn channel_bitmask(&self) -> ChannelBitmask; + + /// Return the channel indicies in this `Source` for the components in the given `bitmask`. + fn channel_indicies(&self, bitmask: ChannelBitmask) -> Vec { + todo!() + } +} + +/// A `Source` for adding a channel bitmask to a preexisting source. +/// +/// This `Source` only adds the `SourceChannelBitmask` trait methods, allowing the inner source to +/// broadcast the given bitmask metadata. It otherwise does nothing to the source's samples and +/// refers all source methods back to the inner input. This source is provided as a convenience to +/// add a channel bitmask to a source that does not support it. +pub struct ChannelBitmaskAdapter { + input: I, + channel_bitmask: ChannelBitmask, +} + +impl ChannelBitmaskAdapter { + fn new(input: I, channel_bitmask: ChannelBitmask) -> Self { + Self { + input, + channel_bitmask, + } + } +} + +impl Source for ChannelBitmaskAdapter +where + I: Source, + I::Item: Sample, +{ + fn current_frame_len(&self) -> Option { + self.input.current_frame_len() + } + + fn channels(&self) -> u16 { + self.input.channels() + } + + fn sample_rate(&self) -> u32 { + self.input.sample_rate() + } + + fn total_duration(&self) -> Option { + self.input.total_duration() + } +} + +impl Iterator for ChannelBitmaskAdapter +where + I: Source, + I::Item: Sample, +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + self.input.next() + } +} + +impl SourceChannelBitmask for ChannelBitmaskAdapter +where + I: Source, + I::Item: Sample, +{ + fn channel_bitmask(&self) -> ChannelBitmask { + self.channel_bitmask + } +} From 363de7473c55d35be4d2487260c7f7ff5955ffd7 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sat, 7 Dec 2024 21:21:18 -0800 Subject: [PATCH 04/19] Cleaning up some things --- src/channel_bitmask.rs | 60 ++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/src/channel_bitmask.rs b/src/channel_bitmask.rs index a943dc9d..f02ed57f 100644 --- a/src/channel_bitmask.rs +++ b/src/channel_bitmask.rs @@ -19,31 +19,31 @@ pub const TOP_FRONT_LEFT: ChannelBitmask = 0x1000; pub const TOP_FRONT_CENTER: ChannelBitmask = 0x2000; pub const TOP_FRONT_RIGHT: ChannelBitmask = 0x4000; pub const TOP_BACK_LEFT: ChannelBitmask = 0x8000; -pub const TOP_BACK_CENTER: ChannelBitmask = 0x10000; -pub const TOP_BACK_RIGHT: ChannelBitmask = 0x20000; - -/// Left total. The left channel of phase-matrix encoded audio, as in Dolby Stereo. -pub const LEFT_TOTAL: ChannelBitmask = 0x40000; - -/// Right total. The right channel of phase-matrix encoded audio, as in Dolby Stereo. -pub const RIGHT_TOTAL: ChannelBitmask = 0x80000; - -pub const AMBISONIC_W: ChannelBitmask = 0x100000; -pub const AMBISONIC_X: ChannelBitmask = 0x200000; -pub const AMBISONIC_Y: ChannelBitmask = 0x400000; -pub const AMBISONIC_Z: ChannelBitmask = 0x800000; -pub const AMBISONIC_R: ChannelBitmask = 0x1000000; -pub const AMBISONIC_S: ChannelBitmask = 0x2000000; -pub const AMBISONIC_T: ChannelBitmask = 0x4000000; -pub const AMBISONIC_U: ChannelBitmask = 0x8000000; -pub const AMBISONIC_V: ChannelBitmask = 0x10000000; -pub const AMBISONIC_K: ChannelBitmask = 0x20000000; -pub const AMBISONIC_L: ChannelBitmask = 0x40000000; -pub const AMBISONIC_M: ChannelBitmask = 0x80000000; -pub const AMBISONIC_N: ChannelBitmask = 0x100000000; -pub const AMBISONIC_O: ChannelBitmask = 0x200000000; -pub const AMBISONIC_P: ChannelBitmask = 0x400000000; -pub const AMBISONIC_Q: ChannelBitmask = 0x800000000; +pub const TOP_BACK_CENTER: ChannelBitmask = 0x1_0000; +pub const TOP_BACK_RIGHT: ChannelBitmask = 0x2_0000; + +pub const FRONT_LEFT_WIDE: ChannelBitmask = 0x4_0000; +pub const FRONT_RIGHT_WIDE: ChannelBitmask = 0x8_0000; + +pub const AMBISONIC_W: ChannelBitmask = 0x10_0000; +pub const AMBISONIC_X: ChannelBitmask = 0x20_0000; +pub const AMBISONIC_Y: ChannelBitmask = 0x40_0000; +pub const AMBISONIC_Z: ChannelBitmask = 0x80_0000; +pub const AMBISONIC_R: ChannelBitmask = 0x100_0000; +pub const AMBISONIC_S: ChannelBitmask = 0x200_0000; +pub const AMBISONIC_T: ChannelBitmask = 0x400_0000; +pub const AMBISONIC_U: ChannelBitmask = 0x800_0000; +pub const AMBISONIC_V: ChannelBitmask = 0x1000_0000; +pub const AMBISONIC_K: ChannelBitmask = 0x2000_0000; +pub const AMBISONIC_L: ChannelBitmask = 0x4000_0000; +pub const AMBISONIC_M: ChannelBitmask = 0x8000_0000; +pub const AMBISONIC_N: ChannelBitmask = 0x1_0000_0000; +pub const AMBISONIC_O: ChannelBitmask = 0x2_0000_0000; +pub const AMBISONIC_P: ChannelBitmask = 0x4_0000_0000; +pub const AMBISONIC_Q: ChannelBitmask = 0x8_0000_0000; + +pub const MATRIX_LEFT_TOTAL: ChannelBitmask = 0x10_0000_0000; +pub const MATRIX_RIGHT_TOTAL: ChannelBitmask = 0x20_0000_0000; pub const UNDEFINED: ChannelBitmask = 0x0; pub const STEREO: ChannelBitmask = FRONT_LEFT ^ FRONT_RIGHT; @@ -76,9 +76,9 @@ pub const AMBISONIC_O3: ChannelBitmask = AMBISONIC_O2 /// implementing `Source`. `ChannelBitmask` constants define a single bit in a `u64` for each /// possible component. /// -/// The presence of a given component in the source's output is signified by -/// the setting of its corresponding bit, and components are ordered in the Source's iteration -/// according to their corresponding `ChannelBitmask` value. +/// The presence of a given component in the source's output is signified by the setting of its +/// corresponding bit, and components are ordered in the Source's iteration according to their +/// corresponding `ChannelBitmask` value. /// /// For example: if a source is generating a six-channel stream of samples, it's `channels()` /// method will return "6" and its its [`SourceChannelBitmask::channel_bitmask()`] will return @@ -93,10 +93,6 @@ where /// The [`ChannelBitmask`] of the `Source`. fn channel_bitmask(&self) -> ChannelBitmask; - /// Return the channel indicies in this `Source` for the components in the given `bitmask`. - fn channel_indicies(&self, bitmask: ChannelBitmask) -> Vec { - todo!() - } } /// A `Source` for adding a channel bitmask to a preexisting source. From acaa2f50e6ba90fa4cbb6d3836d43bb11ab0d786 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sat, 7 Dec 2024 21:34:50 -0800 Subject: [PATCH 05/19] Integration into Source plumbing, more doc --- src/channel_bitmask.rs | 26 ++++++++++++++++++++++++++ src/source/mod.rs | 14 ++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/channel_bitmask.rs b/src/channel_bitmask.rs index f02ed57f..28c215ed 100644 --- a/src/channel_bitmask.rs +++ b/src/channel_bitmask.rs @@ -92,7 +92,23 @@ where { /// The [`ChannelBitmask`] of the `Source`. fn channel_bitmask(&self) -> ChannelBitmask; +} +/// Return a source wrapping `input` tha implements the `ChannelBitmask` trait. +/// +/// # Panics +/// +/// If `channel_bitmask.count_ones()` is not equal to `input.channels()`. +pub fn add_channel_mask(input: I, channel_bitmask: ChannelBitmask) -> ChannelBitmaskAdapter +where + I: Source, + I::Item: Sample, +{ + assert!( + input.channels() == channel_bitmask.count_ones() as u16, + "Count of 1 bits in channel bitmask do not match count of channels in input!" + ); + ChannelBitmaskAdapter::new(input, channel_bitmask) } /// A `Source` for adding a channel bitmask to a preexisting source. @@ -113,6 +129,16 @@ impl ChannelBitmaskAdapter { channel_bitmask, } } + + /// Get the input source, consuming the adapter + fn into_inner(self) -> I { + self.input + } + + /// Get a mutable reference to the inner source + fn inner_mut(&mut self) -> &I { + &mut self.input + } } impl Source for ChannelBitmaskAdapter diff --git a/src/source/mod.rs b/src/source/mod.rs index 04dc7777..2e9e5d58 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -5,6 +5,7 @@ use core::time::Duration; use cpal::FromSample; +use crate::channel_bitmask::{add_channel_mask, ChannelBitmask, ChannelBitmaskAdapter}; use crate::Sample; pub use self::agc::AutomaticGainControl; @@ -467,6 +468,19 @@ where { SamplesConverter::new(self) } + + /// Returns a source that implements the [`ChannelBitmask`] trait and reports + /// the given `channel_bitmask`. + /// + /// # Panics + /// + /// If the `channel_bitmask.count_ones()` is not equal to `self.channels()`. + fn add_channel_mask(self, channel_bitmask: ChannelBitmask) -> ChannelBitmaskAdapter + where + Self: Sized, + { + add_channel_mask(self, channel_bitmask) + } /// Makes the sound pausable. // TODO: add example From 657848ec74dccf2d50a689851fcda8ee71720fa3 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 8 Dec 2024 10:30:30 -0800 Subject: [PATCH 06/19] Added more channel bitmasks, reordered --- src/channel_bitmask.rs | 88 +++++++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 26 deletions(-) diff --git a/src/channel_bitmask.rs b/src/channel_bitmask.rs index 28c215ed..a6758fef 100644 --- a/src/channel_bitmask.rs +++ b/src/channel_bitmask.rs @@ -3,6 +3,8 @@ use crate::{Sample, Source}; pub type ChannelBitmask = u64; +// Bitmasks 0x1...0x2_000 are identical to speaker assignemnts in the Microsoft WAVE WAVEFORMATEX +// channel bitmask. pub const FRONT_LEFT: ChannelBitmask = 0x1; pub const FRONT_RIGHT: ChannelBitmask = 0x2; pub const FRONT_CENTER: ChannelBitmask = 0x4; @@ -22,36 +24,70 @@ pub const TOP_BACK_LEFT: ChannelBitmask = 0x8000; pub const TOP_BACK_CENTER: ChannelBitmask = 0x1_0000; pub const TOP_BACK_RIGHT: ChannelBitmask = 0x2_0000; +// These four speaker assignments are required to complete a full Dolby Atmos 9.1.6 bed. pub const FRONT_LEFT_WIDE: ChannelBitmask = 0x4_0000; pub const FRONT_RIGHT_WIDE: ChannelBitmask = 0x8_0000; - -pub const AMBISONIC_W: ChannelBitmask = 0x10_0000; -pub const AMBISONIC_X: ChannelBitmask = 0x20_0000; -pub const AMBISONIC_Y: ChannelBitmask = 0x40_0000; -pub const AMBISONIC_Z: ChannelBitmask = 0x80_0000; -pub const AMBISONIC_R: ChannelBitmask = 0x100_0000; -pub const AMBISONIC_S: ChannelBitmask = 0x200_0000; -pub const AMBISONIC_T: ChannelBitmask = 0x400_0000; -pub const AMBISONIC_U: ChannelBitmask = 0x800_0000; -pub const AMBISONIC_V: ChannelBitmask = 0x1000_0000; -pub const AMBISONIC_K: ChannelBitmask = 0x2000_0000; -pub const AMBISONIC_L: ChannelBitmask = 0x4000_0000; -pub const AMBISONIC_M: ChannelBitmask = 0x8000_0000; -pub const AMBISONIC_N: ChannelBitmask = 0x1_0000_0000; -pub const AMBISONIC_O: ChannelBitmask = 0x2_0000_0000; -pub const AMBISONIC_P: ChannelBitmask = 0x4_0000_0000; -pub const AMBISONIC_Q: ChannelBitmask = 0x8_0000_0000; - -pub const MATRIX_LEFT_TOTAL: ChannelBitmask = 0x10_0000_0000; -pub const MATRIX_RIGHT_TOTAL: ChannelBitmask = 0x20_0000_0000; +pub const TOP_SIDE_LEFT: ChannelBitmask = 0x10_0000; +pub const TOP_SIDE_RIGHT: ChannelBitmask = 0x20_0000; + +// These speaker assignments are required for IMAX and TMH 10.2 +pub const BACK_DIRECT_LEFT: ChannelBitmask = 0x40_0000; +pub const BACK_DIRECT_RIGHT: ChannelBitmask = 0x80_0000; + +// Matrix channel assignments are required for Dolby Stereo, Dolby Pro Logic 2 and compatibility with +// ITU Audio Definition Model. +pub const MATRIX_LEFT_TOTAL: ChannelBitmask = 0x100_0000; +pub const MATRIX_RIGHT_TOTAL: ChannelBitmask = 0x200_0000; + +// Ambisonic components +// +// Head-locked mixes: +pub const AMBISONIC_HEADLOCKED_L: ChannelBitmask = 0x400_000; +pub const AMBISONIC_HEADLOCKED_R: ChannelBitmask = 0x800_000; + +// Ambisonic components in FuMa order +pub const AMBISONIC_W: ChannelBitmask = 0x1000_0000; +pub const AMBISONIC_X: ChannelBitmask = 0x2000_0000; +pub const AMBISONIC_Y: ChannelBitmask = 0x4000_0000; +pub const AMBISONIC_Z: ChannelBitmask = 0x8000_0000; +pub const AMBISONIC_R: ChannelBitmask = 0x1_0000_0000; +pub const AMBISONIC_S: ChannelBitmask = 0x2_0000_0000; +pub const AMBISONIC_T: ChannelBitmask = 0x4_0000_0000; +pub const AMBISONIC_U: ChannelBitmask = 0x8_0000_0000; +pub const AMBISONIC_V: ChannelBitmask = 0x10_0000_0000; +pub const AMBISONIC_K: ChannelBitmask = 0x20_0000_0000; +pub const AMBISONIC_L: ChannelBitmask = 0x40_0000_0000; +pub const AMBISONIC_M: ChannelBitmask = 0x80_0000_0000; +pub const AMBISONIC_N: ChannelBitmask = 0x100_0000_0000; +pub const AMBISONIC_O: ChannelBitmask = 0x200_0000_0000; +pub const AMBISONIC_P: ChannelBitmask = 0x400_0000_0000; +pub const AMBISONIC_Q: ChannelBitmask = 0x800_0000_0000; pub const UNDEFINED: ChannelBitmask = 0x0; pub const STEREO: ChannelBitmask = FRONT_LEFT ^ FRONT_RIGHT; pub const LCR: ChannelBitmask = FRONT_LEFT ^ FRONT_CENTER ^ FRONT_RIGHT; pub const LCRS: ChannelBitmask = LCR ^ BACK_CENTER; -pub const SURROUND_51: ChannelBitmask = - FRONT_LEFT ^ FRONT_RIGHT ^ FRONT_CENTER ^ LFE ^ BACK_LEFT ^ BACK_RIGHT; -pub const SURROUND_71: ChannelBitmask = SURROUND_51 ^ SIDE_LEFT ^ SIDE_RIGHT; +pub const SURROUND_5_0: ChannelBitmask = + FRONT_LEFT ^ FRONT_RIGHT ^ FRONT_CENTER ^ BACK_LEFT ^ BACK_RIGHT; +pub const SURROUND_5_1: ChannelBitmask = SURROUND_5_0 ^ LFE; +pub const SURROUND_7_0: ChannelBitmask = SURROUND_5_0 ^ SIDE_LEFT ^ SIDE_RIGHT; +pub const SURROUND_7_1: ChannelBitmask = SURROUND_7_0 ^ LFE; + +pub const ATMOS_5_1_2: ChannelBitmask = SURROUND_5_1 ^ TOP_SIDE_LEFT ^ TOP_SIDE_RIGHT; +pub const ATMOS_5_0_2: ChannelBitmask = SURROUND_5_0 ^ TOP_SIDE_LEFT ^ TOP_SIDE_RIGHT; +pub const ATMOS_7_1_4: ChannelBitmask = + SURROUND_5_1 ^ TOP_FRONT_LEFT ^ TOP_FRONT_RIGHT ^ TOP_BACK_LEFT ^ TOP_BACK_RIGHT; +pub const ATMOS_7_1_2: ChannelBitmask = SURROUND_5_1 ^ TOP_SIDE_LEFT ^ TOP_SIDE_RIGHT; +pub const ATMOS_7_0_2: ChannelBitmask = SURROUND_7_0 ^ TOP_SIDE_LEFT ^ TOP_SIDE_RIGHT; +pub const ATMOS_9_1_6: ChannelBitmask = SURROUND_7_1 + ^ FRONT_LEFT_WIDE + ^ FRONT_RIGHT_WIDE + ^ TOP_FRONT_LEFT + ^ TOP_FRONT_RIGHT + ^ TOP_BACK_LEFT + ^ TOP_BACK_RIGHT + ^ TOP_SIDE_LEFT + ^ TOP_SIDE_RIGHT; pub const AMBISONIC_O1: ChannelBitmask = AMBISONIC_W ^ AMBISONIC_X ^ AMBISONIC_Y ^ AMBISONIC_Z; pub const AMBISONIC_O2: ChannelBitmask = @@ -114,9 +150,9 @@ where /// A `Source` for adding a channel bitmask to a preexisting source. /// /// This `Source` only adds the `SourceChannelBitmask` trait methods, allowing the inner source to -/// broadcast the given bitmask metadata. It otherwise does nothing to the source's samples and -/// refers all source methods back to the inner input. This source is provided as a convenience to -/// add a channel bitmask to a source that does not support it. +/// provide the given bitmask metadata. It otherwise does nothing to the source's samples and +/// refers all `Source` methods back to the inner input. This source is provided as a convenience +/// to add a channel bitmask to a source that does not support it. pub struct ChannelBitmaskAdapter { input: I, channel_bitmask: ChannelBitmask, From a2dac069147e47669ec482e8c2ff666a3ae7b923 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 8 Dec 2024 10:49:11 -0800 Subject: [PATCH 07/19] More documentation --- src/channel_bitmask.rs | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/channel_bitmask.rs b/src/channel_bitmask.rs index a6758fef..280913ac 100644 --- a/src/channel_bitmask.rs +++ b/src/channel_bitmask.rs @@ -3,7 +3,7 @@ use crate::{Sample, Source}; pub type ChannelBitmask = u64; -// Bitmasks 0x1...0x2_000 are identical to speaker assignemnts in the Microsoft WAVE WAVEFORMATEX +// Bitmasks 0x1...0x2_0000 are identical to speaker assignemnts in the Microsoft WAVE WAVEFORMATEX // channel bitmask. pub const FRONT_LEFT: ChannelBitmask = 0x1; pub const FRONT_RIGHT: ChannelBitmask = 0x2; @@ -63,22 +63,54 @@ pub const AMBISONIC_O: ChannelBitmask = 0x200_0000_0000; pub const AMBISONIC_P: ChannelBitmask = 0x400_0000_0000; pub const AMBISONIC_Q: ChannelBitmask = 0x800_0000_0000; +/// Undefined channel format. Use this if the format is unknown or if you are using another method +/// to inform clients about channel components. Monoaural sources may use this or use +/// `FRONT_CENTER`. pub const UNDEFINED: ChannelBitmask = 0x0; + +/// Left-right stereo, for both speakers and headphones. pub const STEREO: ChannelBitmask = FRONT_LEFT ^ FRONT_RIGHT; + +/// Stereo with a hard center. pub const LCR: ChannelBitmask = FRONT_LEFT ^ FRONT_CENTER ^ FRONT_RIGHT; + +/// Four-channel surround sound, as like classic discrete Dolby Stereo. pub const LCRS: ChannelBitmask = LCR ^ BACK_CENTER; + +/// 5.0 surround. pub const SURROUND_5_0: ChannelBitmask = FRONT_LEFT ^ FRONT_RIGHT ^ FRONT_CENTER ^ BACK_LEFT ^ BACK_RIGHT; + +/// 5.1 surround, with LFE. pub const SURROUND_5_1: ChannelBitmask = SURROUND_5_0 ^ LFE; + +/// 7.0 surround sound, with four surround channels. pub const SURROUND_7_0: ChannelBitmask = SURROUND_5_0 ^ SIDE_LEFT ^ SIDE_RIGHT; + +/// 7.1 surround sound, with four surround channels and LFE, as like Dolby Surround 7.1. pub const SURROUND_7_1: ChannelBitmask = SURROUND_7_0 ^ LFE; +/// 7.1 surround sound with five front channels and two surround channels, as like Sony Dynamic +/// Digital Sound (SDDS). +pub const SDDS_7_1: ChannelBitmask = SURROUND_5_1 ^ FRONT_LEFT_CENTER ^ FRONT_RIGHT_CENTER; + +/// Dolby Atmos 5.1.2 Bed. pub const ATMOS_5_1_2: ChannelBitmask = SURROUND_5_1 ^ TOP_SIDE_LEFT ^ TOP_SIDE_RIGHT; + +/// Dolby Atmos 5.0.2 Bed. pub const ATMOS_5_0_2: ChannelBitmask = SURROUND_5_0 ^ TOP_SIDE_LEFT ^ TOP_SIDE_RIGHT; + +/// Dolby Atmos 7.1.4 Bed. pub const ATMOS_7_1_4: ChannelBitmask = SURROUND_5_1 ^ TOP_FRONT_LEFT ^ TOP_FRONT_RIGHT ^ TOP_BACK_LEFT ^ TOP_BACK_RIGHT; + +/// Dolby Atmos 7.1.2 Bed. pub const ATMOS_7_1_2: ChannelBitmask = SURROUND_5_1 ^ TOP_SIDE_LEFT ^ TOP_SIDE_RIGHT; + +/// Dolby Atmos 7.0.2 Bed. pub const ATMOS_7_0_2: ChannelBitmask = SURROUND_7_0 ^ TOP_SIDE_LEFT ^ TOP_SIDE_RIGHT; + +/// Dolby Atmos 9.1.6 Bed. pub const ATMOS_9_1_6: ChannelBitmask = SURROUND_7_1 ^ FRONT_LEFT_WIDE ^ FRONT_RIGHT_WIDE @@ -89,9 +121,14 @@ pub const ATMOS_9_1_6: ChannelBitmask = SURROUND_7_1 ^ TOP_SIDE_LEFT ^ TOP_SIDE_RIGHT; +/// First-order Ambisonic components, WXYZ. pub const AMBISONIC_O1: ChannelBitmask = AMBISONIC_W ^ AMBISONIC_X ^ AMBISONIC_Y ^ AMBISONIC_Z; + +/// Second-order Ambisonic components, WXYZ+RSTUV. pub const AMBISONIC_O2: ChannelBitmask = AMBISONIC_O1 ^ AMBISONIC_R ^ AMBISONIC_S ^ AMBISONIC_T ^ AMBISONIC_U ^ AMBISONIC_V; + +/// Third-order Ambisonic components. WXYZ+RSTUV+KLMNOPQ. pub const AMBISONIC_O3: ChannelBitmask = AMBISONIC_O2 ^ AMBISONIC_K ^ AMBISONIC_L From bb3598510cfd8ff064e676a40fcbd49b2e44dabb Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 8 Dec 2024 11:07:31 -0800 Subject: [PATCH 08/19] rustfmt --- src/channel_bitmask.rs | 9 ++++++++- src/decoder/mod.rs | 2 +- src/lib.rs | 4 ++-- src/source/mod.rs | 4 ++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/channel_bitmask.rs b/src/channel_bitmask.rs index 280913ac..36beb150 100644 --- a/src/channel_bitmask.rs +++ b/src/channel_bitmask.rs @@ -1,6 +1,13 @@ #![allow(dead_code)] use crate::{Sample, Source}; + +/// Channel Bitmask +/// +/// Each bit in a channel bitmask indicates a different speaker or channel component in a +/// multichannel audio stream. Multichannel streams set each bit "1" for the corresponding +/// components they provide samples for, and then provide them in the order of the place-value of +/// the bit, right-to-left/LSB-to-MSB. pub type ChannelBitmask = u64; // Bitmasks 0x1...0x2_0000 are identical to speaker assignemnts in the Microsoft WAVE WAVEFORMATEX @@ -124,7 +131,7 @@ pub const ATMOS_9_1_6: ChannelBitmask = SURROUND_7_1 /// First-order Ambisonic components, WXYZ. pub const AMBISONIC_O1: ChannelBitmask = AMBISONIC_W ^ AMBISONIC_X ^ AMBISONIC_Y ^ AMBISONIC_Z; -/// Second-order Ambisonic components, WXYZ+RSTUV. +/// Second-order Ambisonic components, WXYZ+RSTUV. pub const AMBISONIC_O2: ChannelBitmask = AMBISONIC_O1 ^ AMBISONIC_R ^ AMBISONIC_S ^ AMBISONIC_T ^ AMBISONIC_U ^ AMBISONIC_V; diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 8a3883ee..5f453b1b 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -14,7 +14,7 @@ use crate::Source; #[cfg(feature = "symphonia")] use self::read_seek_source::ReadSeekSource; #[cfg(feature = "symphonia")] -use ::symphonia::core::io::{MediaSource, MediaSourceStream}; +use symphonia::core::io::{MediaSource, MediaSourceStream}; #[cfg(all(feature = "flac", not(feature = "symphonia-flac")))] mod flac; diff --git a/src/lib.rs b/src/lib.rs index 7f06c98d..4272cf9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -155,8 +155,8 @@ pub use cpal::{ SupportedStreamConfig, }; -mod conversions; mod channel_bitmask; +mod conversions; mod sink; mod spatial_sink; mod stream; @@ -168,8 +168,8 @@ pub mod queue; pub mod source; pub mod static_buffer; -pub use crate::conversions::Sample; pub use crate::channel_bitmask::ChannelBitmask; +pub use crate::conversions::Sample; pub use crate::decoder::Decoder; pub use crate::sink::Sink; pub use crate::source::Source; diff --git a/src/source/mod.rs b/src/source/mod.rs index 2e9e5d58..df3a9edd 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -468,8 +468,8 @@ where { SamplesConverter::new(self) } - - /// Returns a source that implements the [`ChannelBitmask`] trait and reports + + /// Returns a source that implements the [`ChannelBitmask`] trait and reports /// the given `channel_bitmask`. /// /// # Panics From 7be03dace573322d3e4242d01b453915ad63b015 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 8 Dec 2024 11:10:54 -0800 Subject: [PATCH 09/19] Reverted a rustfmt "fix" --- src/decoder/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 5f453b1b..8a3883ee 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -14,7 +14,7 @@ use crate::Source; #[cfg(feature = "symphonia")] use self::read_seek_source::ReadSeekSource; #[cfg(feature = "symphonia")] -use symphonia::core::io::{MediaSource, MediaSourceStream}; +use ::symphonia::core::io::{MediaSource, MediaSourceStream}; #[cfg(all(feature = "flac", not(feature = "symphonia-flac")))] mod flac; From c7c2ba15cda282b96c561d1ac3728a76ae9f769d Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 8 Dec 2024 11:20:42 -0800 Subject: [PATCH 10/19] Typo --- src/channel_bitmask.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/channel_bitmask.rs b/src/channel_bitmask.rs index 36beb150..7b1704bd 100644 --- a/src/channel_bitmask.rs +++ b/src/channel_bitmask.rs @@ -174,7 +174,7 @@ where fn channel_bitmask(&self) -> ChannelBitmask; } -/// Return a source wrapping `input` tha implements the `ChannelBitmask` trait. +/// Return a source wrapping `input` that implements the `ChannelBitmask` trait. /// /// # Panics /// From ae09f6358860f17f8833a9a48ce7e250de08a541 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Mon, 9 Dec 2024 21:12:58 -0800 Subject: [PATCH 11/19] Documentation of the ChannelBitmask type --- src/channel_bitmask.rs | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/channel_bitmask.rs b/src/channel_bitmask.rs index 7b1704bd..a41c4825 100644 --- a/src/channel_bitmask.rs +++ b/src/channel_bitmask.rs @@ -8,10 +8,38 @@ use crate::{Sample, Source}; /// multichannel audio stream. Multichannel streams set each bit "1" for the corresponding /// components they provide samples for, and then provide them in the order of the place-value of /// the bit, right-to-left/LSB-to-MSB. +/// +/// # Note on the Immersive Audio +/// +/// Constants from `0x0001...0x800_0000` are for theatrical presentations, for presentations in +/// theaters and equipped home theaters. For immersive presentations like Oculus, Apple Vision Pro +/// etc. the presentation of these components is undefined. +/// +/// 0x0001 ... 0x2_0000 +/// Channel bitmasks 0x1 through 0x2_0000 are identical to speaker assignements in the +/// Microsoft Wave WAVEFORMATEX channel bitmask. +/// +/// 0x4_0000 ... 0x20_0000 +/// These speaker assignents are required to complete a full Dolby Atmos 9.1.6 bed. +/// +/// 0x40_0000 and 0x80_0000 +/// These "DIRECT" speaker assigments are required for IMAX and TMH 10.2. +/// +/// 0x100_0000 and 0x200_0000 +/// We've dedicated these two bits for left-total and right-total components, for use with +/// matrix-encoded formats. Without further qualification, channels mapped to these code +/// points will be interpreted as Dolby Pro Logic 2 Left Total and Right Total components, but +/// other possibilities here could be CBS SQ, Sansui QS or CD-4. +/// +/// 0x400_0000 and 0x800_0000 +/// These are head-locked left and right compoonents of an Ambisonic presentation. These are +/// presented a headset without spatialization simultaneous with... +/// +/// 0x1000_0000 ... 0x800_0000_0000 +/// These are all Ambisonic components in FuMa order. pub type ChannelBitmask = u64; -// Bitmasks 0x1...0x2_0000 are identical to speaker assignemnts in the Microsoft WAVE WAVEFORMATEX -// channel bitmask. +// WAVEFORMATEX pub const FRONT_LEFT: ChannelBitmask = 0x1; pub const FRONT_RIGHT: ChannelBitmask = 0x2; pub const FRONT_CENTER: ChannelBitmask = 0x4; @@ -31,13 +59,13 @@ pub const TOP_BACK_LEFT: ChannelBitmask = 0x8000; pub const TOP_BACK_CENTER: ChannelBitmask = 0x1_0000; pub const TOP_BACK_RIGHT: ChannelBitmask = 0x2_0000; -// These four speaker assignments are required to complete a full Dolby Atmos 9.1.6 bed. +// For Dolby Atmos pub const FRONT_LEFT_WIDE: ChannelBitmask = 0x4_0000; pub const FRONT_RIGHT_WIDE: ChannelBitmask = 0x8_0000; pub const TOP_SIDE_LEFT: ChannelBitmask = 0x10_0000; pub const TOP_SIDE_RIGHT: ChannelBitmask = 0x20_0000; -// These speaker assignments are required for IMAX and TMH 10.2 +// For IMAX and TMH pub const BACK_DIRECT_LEFT: ChannelBitmask = 0x40_0000; pub const BACK_DIRECT_RIGHT: ChannelBitmask = 0x80_0000; From ec4e4854b869a4411faba7c2e3fa2c9b9c5a27d7 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Mon, 9 Dec 2024 21:25:18 -0800 Subject: [PATCH 12/19] More documentation --- src/channel_bitmask.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/channel_bitmask.rs b/src/channel_bitmask.rs index a41c4825..824d0800 100644 --- a/src/channel_bitmask.rs +++ b/src/channel_bitmask.rs @@ -18,6 +18,13 @@ use crate::{Sample, Source}; /// 0x0001 ... 0x2_0000 /// Channel bitmasks 0x1 through 0x2_0000 are identical to speaker assignements in the /// Microsoft Wave WAVEFORMATEX channel bitmask. +/// +/// `BACK_` and `SIDE_` channel assignments correspond to surround assignments in the +/// respective Dolby and DTS channel assignments. These are arrays of speakers arround the +/// listener, see the `DIRECT` assignments for point surrounds. +/// +/// `TOP_FRONT` and `TOP_BACK` channel assignments correspond to overhead surround assignments +/// ahead and behind the listener, accoding to Dolby Home Atmos 7.1.4 standards. /// /// 0x4_0000 ... 0x20_0000 /// These speaker assignents are required to complete a full Dolby Atmos 9.1.6 bed. From 65a98a760ffddfa5dcf1848d640c740dd35eb5be Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Mon, 9 Dec 2024 21:26:37 -0800 Subject: [PATCH 13/19] rustfmt --- src/channel_bitmask.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/channel_bitmask.rs b/src/channel_bitmask.rs index 824d0800..9c7648df 100644 --- a/src/channel_bitmask.rs +++ b/src/channel_bitmask.rs @@ -9,12 +9,12 @@ use crate::{Sample, Source}; /// components they provide samples for, and then provide them in the order of the place-value of /// the bit, right-to-left/LSB-to-MSB. /// -/// # Note on the Immersive Audio +/// # Note on the Immersive Audio /// /// Constants from `0x0001...0x800_0000` are for theatrical presentations, for presentations in /// theaters and equipped home theaters. For immersive presentations like Oculus, Apple Vision Pro /// etc. the presentation of these components is undefined. -/// +/// /// 0x0001 ... 0x2_0000 /// Channel bitmasks 0x1 through 0x2_0000 are identical to speaker assignements in the /// Microsoft Wave WAVEFORMATEX channel bitmask. @@ -24,8 +24,8 @@ use crate::{Sample, Source}; /// listener, see the `DIRECT` assignments for point surrounds. /// /// `TOP_FRONT` and `TOP_BACK` channel assignments correspond to overhead surround assignments -/// ahead and behind the listener, accoding to Dolby Home Atmos 7.1.4 standards. -/// +/// ahead and behind the listener, accoding to Dolby Home Atmos 7.1.4 standards. +/// /// 0x4_0000 ... 0x20_0000 /// These speaker assignents are required to complete a full Dolby Atmos 9.1.6 bed. /// @@ -37,7 +37,7 @@ use crate::{Sample, Source}; /// matrix-encoded formats. Without further qualification, channels mapped to these code /// points will be interpreted as Dolby Pro Logic 2 Left Total and Right Total components, but /// other possibilities here could be CBS SQ, Sansui QS or CD-4. -/// +/// /// 0x400_0000 and 0x800_0000 /// These are head-locked left and right compoonents of an Ambisonic presentation. These are /// presented a headset without spatialization simultaneous with... @@ -72,7 +72,7 @@ pub const FRONT_RIGHT_WIDE: ChannelBitmask = 0x8_0000; pub const TOP_SIDE_LEFT: ChannelBitmask = 0x10_0000; pub const TOP_SIDE_RIGHT: ChannelBitmask = 0x20_0000; -// For IMAX and TMH +// For IMAX and TMH pub const BACK_DIRECT_LEFT: ChannelBitmask = 0x40_0000; pub const BACK_DIRECT_RIGHT: ChannelBitmask = 0x80_0000; From 9ef4bb302e465a82777825d3676ea8f7e7406620 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Mon, 9 Dec 2024 21:29:24 -0800 Subject: [PATCH 14/19] Fixing tokens for docs --- src/channel_bitmask.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/channel_bitmask.rs b/src/channel_bitmask.rs index 9c7648df..c37fdf66 100644 --- a/src/channel_bitmask.rs +++ b/src/channel_bitmask.rs @@ -19,11 +19,11 @@ use crate::{Sample, Source}; /// Channel bitmasks 0x1 through 0x2_0000 are identical to speaker assignements in the /// Microsoft Wave WAVEFORMATEX channel bitmask. /// -/// `BACK_` and `SIDE_` channel assignments correspond to surround assignments in the +/// BACK_ and SIDE_ channel assignments correspond to surround assignments in the /// respective Dolby and DTS channel assignments. These are arrays of speakers arround the /// listener, see the `DIRECT` assignments for point surrounds. /// -/// `TOP_FRONT` and `TOP_BACK` channel assignments correspond to overhead surround assignments +/// TOP_FRONT_ and TOP_BACK_ channel assignments correspond to overhead surround assignments /// ahead and behind the listener, accoding to Dolby Home Atmos 7.1.4 standards. /// /// 0x4_0000 ... 0x20_0000 From 7f6bd21d25de9bcbbe8f49b0dbc4cc1ff1390ac7 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Mon, 9 Dec 2024 21:32:28 -0800 Subject: [PATCH 15/19] doc fixes Figuring out what rustdoc wants to see --- src/channel_bitmask.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/channel_bitmask.rs b/src/channel_bitmask.rs index c37fdf66..f442e050 100644 --- a/src/channel_bitmask.rs +++ b/src/channel_bitmask.rs @@ -21,7 +21,7 @@ use crate::{Sample, Source}; /// /// BACK_ and SIDE_ channel assignments correspond to surround assignments in the /// respective Dolby and DTS channel assignments. These are arrays of speakers arround the -/// listener, see the `DIRECT` assignments for point surrounds. +/// listener, see the DIRECT assignments for point surrounds. /// /// TOP_FRONT_ and TOP_BACK_ channel assignments correspond to overhead surround assignments /// ahead and behind the listener, accoding to Dolby Home Atmos 7.1.4 standards. From a33f5e4c8bb6e396a16d90b93ef3a6ff54f18497 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Mon, 9 Dec 2024 21:35:26 -0800 Subject: [PATCH 16/19] I have bested John Gruber today, but for how long? --- src/channel_bitmask.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/channel_bitmask.rs b/src/channel_bitmask.rs index f442e050..ac4e32d7 100644 --- a/src/channel_bitmask.rs +++ b/src/channel_bitmask.rs @@ -17,14 +17,11 @@ use crate::{Sample, Source}; /// /// 0x0001 ... 0x2_0000 /// Channel bitmasks 0x1 through 0x2_0000 are identical to speaker assignements in the -/// Microsoft Wave WAVEFORMATEX channel bitmask. -/// -/// BACK_ and SIDE_ channel assignments correspond to surround assignments in the -/// respective Dolby and DTS channel assignments. These are arrays of speakers arround the -/// listener, see the DIRECT assignments for point surrounds. -/// -/// TOP_FRONT_ and TOP_BACK_ channel assignments correspond to overhead surround assignments -/// ahead and behind the listener, accoding to Dolby Home Atmos 7.1.4 standards. +/// Microsoft Wave WAVEFORMATEX channel bitmask. `BACK_` and `SIDE_` channel assignments +/// correspond to surround assignments in the respective Dolby and DTS channel assignments. +/// These are arrays of speakers arround the listener, see the DIRECT assignments for point +/// surrounds. `TOP_FRONT_` and `TOP_BACK_` channel assignments correspond to overhead surround +/// assignments ahead and behind the listener, accoding to Dolby Home Atmos 7.1.4 standards. /// /// 0x4_0000 ... 0x20_0000 /// These speaker assignents are required to complete a full Dolby Atmos 9.1.6 bed. From e3fb47e5955c4da59e8e3537aa9f87db4a69f791 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Mon, 9 Dec 2024 21:43:15 -0800 Subject: [PATCH 17/19] Integration of channel bitmasks --- src/channel_bitmask.rs | 341 +++++++++++++++++++++++++++++++++++------ src/lib.rs | 4 +- src/source/mod.rs | 14 ++ 3 files changed, 311 insertions(+), 48 deletions(-) diff --git a/src/channel_bitmask.rs b/src/channel_bitmask.rs index 02b1fd25..ac4e32d7 100644 --- a/src/channel_bitmask.rs +++ b/src/channel_bitmask.rs @@ -1,49 +1,298 @@ +#![allow(dead_code)] +use crate::{Sample, Source}; + +/// Channel Bitmask +/// +/// Each bit in a channel bitmask indicates a different speaker or channel component in a +/// multichannel audio stream. Multichannel streams set each bit "1" for the corresponding +/// components they provide samples for, and then provide them in the order of the place-value of +/// the bit, right-to-left/LSB-to-MSB. +/// +/// # Note on the Immersive Audio +/// +/// Constants from `0x0001...0x800_0000` are for theatrical presentations, for presentations in +/// theaters and equipped home theaters. For immersive presentations like Oculus, Apple Vision Pro +/// etc. the presentation of these components is undefined. +/// +/// 0x0001 ... 0x2_0000 +/// Channel bitmasks 0x1 through 0x2_0000 are identical to speaker assignements in the +/// Microsoft Wave WAVEFORMATEX channel bitmask. `BACK_` and `SIDE_` channel assignments +/// correspond to surround assignments in the respective Dolby and DTS channel assignments. +/// These are arrays of speakers arround the listener, see the DIRECT assignments for point +/// surrounds. `TOP_FRONT_` and `TOP_BACK_` channel assignments correspond to overhead surround +/// assignments ahead and behind the listener, accoding to Dolby Home Atmos 7.1.4 standards. +/// +/// 0x4_0000 ... 0x20_0000 +/// These speaker assignents are required to complete a full Dolby Atmos 9.1.6 bed. +/// +/// 0x40_0000 and 0x80_0000 +/// These "DIRECT" speaker assigments are required for IMAX and TMH 10.2. +/// +/// 0x100_0000 and 0x200_0000 +/// We've dedicated these two bits for left-total and right-total components, for use with +/// matrix-encoded formats. Without further qualification, channels mapped to these code +/// points will be interpreted as Dolby Pro Logic 2 Left Total and Right Total components, but +/// other possibilities here could be CBS SQ, Sansui QS or CD-4. +/// +/// 0x400_0000 and 0x800_0000 +/// These are head-locked left and right compoonents of an Ambisonic presentation. These are +/// presented a headset without spatialization simultaneous with... +/// +/// 0x1000_0000 ... 0x800_0000_0000 +/// These are all Ambisonic components in FuMa order. pub type ChannelBitmask = u64; -const FRONT_LEFT: ChannelBitmask = 0x1; -const FRONT_RIGHT: ChannelBitmask = 0x2; -const FRONT_CENTER: ChannelBitmask = 0x4; -const LFE: ChannelBitmask = 0x8; -const BACK_LEFT: ChannelBitmask = 0x10; -const BACK_RIGHT: ChannelBitmask = 0x20; -const FRONT_LEFT_CENTER: ChannelBitmask = 0x40; -const FRONT_RIGHT_CENTER: ChannelBitmask = 0x80; -const REAR_CENTER: ChannelBitmask = 0x100; -const SIDE_LEFT: ChannelBitmask = 0x200; -const SIDE_RIGHT: ChannelBitmask = 0x400; -const TOP_CENTER: ChannelBitmask = 0x800; -const TOP_FRONT_LEFT: ChannelBitmask = 0x1000; -const TOP_FRONT_CENTER: ChannelBitmask = 0x2000; -const TOP_FRONT_RIGHT: ChannelBitmask = 0x4000; -const TOP_BACK_LEFT: ChannelBitmask = 0x8000; -const TOP_BACK_CENTER: ChannelBitmask = 0x10000; -const TOP_BACK_RIGHT: ChannelBitmask = 0x20000; - -/// Left total. The left channel of phase-matrix encoded audio, as in Dolby Stereo. -const LEFT_TOTAL: ChannelBitmask = 0x40000; - -/// Right total. The right channel of phase-matrix encoded audio, as in Dolby Stereo. -const RIGHT_TOTAL: ChannelBitmask = 0x80000; - -const AMBISONIC_W: ChannelBitmask = 0x100000; -const AMBISONIC_X: ChannelBitmask = 0x200000; -const AMBISONIC_Y: ChannelBitmask = 0x400000; -const AMBISONIC_Z: ChannelBitmask = 0x800000; -const AMBISONIC_R: ChannelBitmask = 0x1000000; -const AMBISONIC_S: ChannelBitmask = 0x2000000; -const AMBISONIC_T: ChannelBitmask = 0x4000000; -const AMBISONIC_U: ChannelBitmask = 0x8000000; -const AMBISONIC_V: ChannelBitmask = 0x10000000; -const AMBISONIC_K: ChannelBitmask = 0x20000000; -const AMBISONIC_L: ChannelBitmask = 0x40000000; -const AMBISONIC_M: ChannelBitmask = 0x80000000; -const AMBISONIC_N: ChannelBitmask = 0x100000000; -const AMBISONIC_O: ChannelBitmask = 0x200000000; -const AMBUSONIC_P: ChannelBitmask = 0x400000000; -const AMBISONIC_Q: ChannelBitmask = 0x800000000; - -const UNDEFINED: ChannelBitmask = 0x0; -const STEREO: ChannelBitmask = FRONT_LEFT ^ FRONT_RIGHT; -const SURROUND_51: ChannelBitmask = FRONT_LEFT ^ FRONT_RIGHT ^ FRONT_CENTER ^ LFE ^ BACK_LEFT ^ BACK_RIGHT; -const SURROUND_71: ChannelBitmask = SURROUND_51 ^ SIDE_LEFT ^ SIDE_RIGHT; +// WAVEFORMATEX +pub const FRONT_LEFT: ChannelBitmask = 0x1; +pub const FRONT_RIGHT: ChannelBitmask = 0x2; +pub const FRONT_CENTER: ChannelBitmask = 0x4; +pub const LFE: ChannelBitmask = 0x8; +pub const BACK_LEFT: ChannelBitmask = 0x10; +pub const BACK_RIGHT: ChannelBitmask = 0x20; +pub const FRONT_LEFT_CENTER: ChannelBitmask = 0x40; +pub const FRONT_RIGHT_CENTER: ChannelBitmask = 0x80; +pub const BACK_CENTER: ChannelBitmask = 0x100; +pub const SIDE_LEFT: ChannelBitmask = 0x200; +pub const SIDE_RIGHT: ChannelBitmask = 0x400; +pub const TOP_CENTER: ChannelBitmask = 0x800; +pub const TOP_FRONT_LEFT: ChannelBitmask = 0x1000; +pub const TOP_FRONT_CENTER: ChannelBitmask = 0x2000; +pub const TOP_FRONT_RIGHT: ChannelBitmask = 0x4000; +pub const TOP_BACK_LEFT: ChannelBitmask = 0x8000; +pub const TOP_BACK_CENTER: ChannelBitmask = 0x1_0000; +pub const TOP_BACK_RIGHT: ChannelBitmask = 0x2_0000; + +// For Dolby Atmos +pub const FRONT_LEFT_WIDE: ChannelBitmask = 0x4_0000; +pub const FRONT_RIGHT_WIDE: ChannelBitmask = 0x8_0000; +pub const TOP_SIDE_LEFT: ChannelBitmask = 0x10_0000; +pub const TOP_SIDE_RIGHT: ChannelBitmask = 0x20_0000; + +// For IMAX and TMH +pub const BACK_DIRECT_LEFT: ChannelBitmask = 0x40_0000; +pub const BACK_DIRECT_RIGHT: ChannelBitmask = 0x80_0000; + +// Matrix channel assignments are required for Dolby Stereo, Dolby Pro Logic 2 and compatibility with +// ITU Audio Definition Model. +pub const MATRIX_LEFT_TOTAL: ChannelBitmask = 0x100_0000; +pub const MATRIX_RIGHT_TOTAL: ChannelBitmask = 0x200_0000; + +// Ambisonic components +// +// Head-locked mixes: +pub const AMBISONIC_HEADLOCKED_L: ChannelBitmask = 0x400_000; +pub const AMBISONIC_HEADLOCKED_R: ChannelBitmask = 0x800_000; + +// Ambisonic components in FuMa order +pub const AMBISONIC_W: ChannelBitmask = 0x1000_0000; +pub const AMBISONIC_X: ChannelBitmask = 0x2000_0000; +pub const AMBISONIC_Y: ChannelBitmask = 0x4000_0000; +pub const AMBISONIC_Z: ChannelBitmask = 0x8000_0000; +pub const AMBISONIC_R: ChannelBitmask = 0x1_0000_0000; +pub const AMBISONIC_S: ChannelBitmask = 0x2_0000_0000; +pub const AMBISONIC_T: ChannelBitmask = 0x4_0000_0000; +pub const AMBISONIC_U: ChannelBitmask = 0x8_0000_0000; +pub const AMBISONIC_V: ChannelBitmask = 0x10_0000_0000; +pub const AMBISONIC_K: ChannelBitmask = 0x20_0000_0000; +pub const AMBISONIC_L: ChannelBitmask = 0x40_0000_0000; +pub const AMBISONIC_M: ChannelBitmask = 0x80_0000_0000; +pub const AMBISONIC_N: ChannelBitmask = 0x100_0000_0000; +pub const AMBISONIC_O: ChannelBitmask = 0x200_0000_0000; +pub const AMBISONIC_P: ChannelBitmask = 0x400_0000_0000; +pub const AMBISONIC_Q: ChannelBitmask = 0x800_0000_0000; + +/// Undefined channel format. Use this if the format is unknown or if you are using another method +/// to inform clients about channel components. Monoaural sources may use this or use +/// `FRONT_CENTER`. +pub const UNDEFINED: ChannelBitmask = 0x0; + +/// Left-right stereo, for both speakers and headphones. +pub const STEREO: ChannelBitmask = FRONT_LEFT ^ FRONT_RIGHT; + +/// Stereo with a hard center. +pub const LCR: ChannelBitmask = FRONT_LEFT ^ FRONT_CENTER ^ FRONT_RIGHT; + +/// Four-channel surround sound, as like classic discrete Dolby Stereo. +pub const LCRS: ChannelBitmask = LCR ^ BACK_CENTER; + +/// 5.0 surround. +pub const SURROUND_5_0: ChannelBitmask = + FRONT_LEFT ^ FRONT_RIGHT ^ FRONT_CENTER ^ BACK_LEFT ^ BACK_RIGHT; + +/// 5.1 surround, with LFE. +pub const SURROUND_5_1: ChannelBitmask = SURROUND_5_0 ^ LFE; + +/// 7.0 surround sound, with four surround channels. +pub const SURROUND_7_0: ChannelBitmask = SURROUND_5_0 ^ SIDE_LEFT ^ SIDE_RIGHT; + +/// 7.1 surround sound, with four surround channels and LFE, as like Dolby Surround 7.1. +pub const SURROUND_7_1: ChannelBitmask = SURROUND_7_0 ^ LFE; + +/// 7.1 surround sound with five front channels and two surround channels, as like Sony Dynamic +/// Digital Sound (SDDS). +pub const SDDS_7_1: ChannelBitmask = SURROUND_5_1 ^ FRONT_LEFT_CENTER ^ FRONT_RIGHT_CENTER; + +/// Dolby Atmos 5.1.2 Bed. +pub const ATMOS_5_1_2: ChannelBitmask = SURROUND_5_1 ^ TOP_SIDE_LEFT ^ TOP_SIDE_RIGHT; + +/// Dolby Atmos 5.0.2 Bed. +pub const ATMOS_5_0_2: ChannelBitmask = SURROUND_5_0 ^ TOP_SIDE_LEFT ^ TOP_SIDE_RIGHT; + +/// Dolby Atmos 7.1.4 Bed. +pub const ATMOS_7_1_4: ChannelBitmask = + SURROUND_5_1 ^ TOP_FRONT_LEFT ^ TOP_FRONT_RIGHT ^ TOP_BACK_LEFT ^ TOP_BACK_RIGHT; + +/// Dolby Atmos 7.1.2 Bed. +pub const ATMOS_7_1_2: ChannelBitmask = SURROUND_5_1 ^ TOP_SIDE_LEFT ^ TOP_SIDE_RIGHT; + +/// Dolby Atmos 7.0.2 Bed. +pub const ATMOS_7_0_2: ChannelBitmask = SURROUND_7_0 ^ TOP_SIDE_LEFT ^ TOP_SIDE_RIGHT; + +/// Dolby Atmos 9.1.6 Bed. +pub const ATMOS_9_1_6: ChannelBitmask = SURROUND_7_1 + ^ FRONT_LEFT_WIDE + ^ FRONT_RIGHT_WIDE + ^ TOP_FRONT_LEFT + ^ TOP_FRONT_RIGHT + ^ TOP_BACK_LEFT + ^ TOP_BACK_RIGHT + ^ TOP_SIDE_LEFT + ^ TOP_SIDE_RIGHT; + +/// First-order Ambisonic components, WXYZ. +pub const AMBISONIC_O1: ChannelBitmask = AMBISONIC_W ^ AMBISONIC_X ^ AMBISONIC_Y ^ AMBISONIC_Z; + +/// Second-order Ambisonic components, WXYZ+RSTUV. +pub const AMBISONIC_O2: ChannelBitmask = + AMBISONIC_O1 ^ AMBISONIC_R ^ AMBISONIC_S ^ AMBISONIC_T ^ AMBISONIC_U ^ AMBISONIC_V; + +/// Third-order Ambisonic components. WXYZ+RSTUV+KLMNOPQ. +pub const AMBISONIC_O3: ChannelBitmask = AMBISONIC_O2 + ^ AMBISONIC_K + ^ AMBISONIC_L + ^ AMBISONIC_M + ^ AMBISONIC_N + ^ AMBISONIC_O + ^ AMBISONIC_P + ^ AMBISONIC_Q; + +/// A trait for [`Source`]'s that provide a channel bitmask. +/// +/// Sources providing more than one channel of audio may be providing their channels in a +/// particular format for surround sound presentation (e.g. 5.1 surround). The +/// `SourceChannelBitmask` trait defines methods for a `Source` to inform a client the speaker +/// assignment or encoding component for each channel. +/// +/// The trait uses [`ChannelBitmask`] values to describe component assignments for channels in an +/// implementing `Source`. `ChannelBitmask` constants define a single bit in a `u64` for each +/// possible component. +/// +/// The presence of a given component in the source's output is signified by the setting of its +/// corresponding bit, and components are ordered in the Source's iteration according to their +/// corresponding `ChannelBitmask` value. +/// +/// For example: if a source is generating a six-channel stream of samples, it's `channels()` +/// method will return "6" and its its [`SourceChannelBitmask::channel_bitmask()`] will return +/// a bitmask equal to "0b111111" or `FRONT_LEFT ^ FRONT_RIGHT ^ FRONT_CENTER ^ LFE ^ BACK_LEFT ^ +/// BACK_RIGHT` (a constant `SURROUND_51` is provided that is equal to this.) The source will then +/// return its samples in the numerical order of these bitmasks: Left, Right, Center, LFE, Back +/// Left and Back Right. +trait SourceChannelBitmask: Source +where + Self::Item: Sample, +{ + /// The [`ChannelBitmask`] of the `Source`. + fn channel_bitmask(&self) -> ChannelBitmask; +} + +/// Return a source wrapping `input` that implements the `ChannelBitmask` trait. +/// +/// # Panics +/// +/// If `channel_bitmask.count_ones()` is not equal to `input.channels()`. +pub fn add_channel_mask(input: I, channel_bitmask: ChannelBitmask) -> ChannelBitmaskAdapter +where + I: Source, + I::Item: Sample, +{ + assert!( + input.channels() == channel_bitmask.count_ones() as u16, + "Count of 1 bits in channel bitmask do not match count of channels in input!" + ); + ChannelBitmaskAdapter::new(input, channel_bitmask) +} + +/// A `Source` for adding a channel bitmask to a preexisting source. +/// +/// This `Source` only adds the `SourceChannelBitmask` trait methods, allowing the inner source to +/// provide the given bitmask metadata. It otherwise does nothing to the source's samples and +/// refers all `Source` methods back to the inner input. This source is provided as a convenience +/// to add a channel bitmask to a source that does not support it. +pub struct ChannelBitmaskAdapter { + input: I, + channel_bitmask: ChannelBitmask, +} + +impl ChannelBitmaskAdapter { + fn new(input: I, channel_bitmask: ChannelBitmask) -> Self { + Self { + input, + channel_bitmask, + } + } + + /// Get the input source, consuming the adapter + fn into_inner(self) -> I { + self.input + } + + /// Get a mutable reference to the inner source + fn inner_mut(&mut self) -> &I { + &mut self.input + } +} + +impl Source for ChannelBitmaskAdapter +where + I: Source, + I::Item: Sample, +{ + fn current_frame_len(&self) -> Option { + self.input.current_frame_len() + } + + fn channels(&self) -> u16 { + self.input.channels() + } + + fn sample_rate(&self) -> u32 { + self.input.sample_rate() + } + + fn total_duration(&self) -> Option { + self.input.total_duration() + } +} + +impl Iterator for ChannelBitmaskAdapter +where + I: Source, + I::Item: Sample, +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + self.input.next() + } +} + +impl SourceChannelBitmask for ChannelBitmaskAdapter +where + I: Source, + I::Item: Sample, +{ + fn channel_bitmask(&self) -> ChannelBitmask { + self.channel_bitmask + } +} diff --git a/src/lib.rs b/src/lib.rs index 7f06c98d..4272cf9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -155,8 +155,8 @@ pub use cpal::{ SupportedStreamConfig, }; -mod conversions; mod channel_bitmask; +mod conversions; mod sink; mod spatial_sink; mod stream; @@ -168,8 +168,8 @@ pub mod queue; pub mod source; pub mod static_buffer; -pub use crate::conversions::Sample; pub use crate::channel_bitmask::ChannelBitmask; +pub use crate::conversions::Sample; pub use crate::decoder::Decoder; pub use crate::sink::Sink; pub use crate::source::Source; diff --git a/src/source/mod.rs b/src/source/mod.rs index 04dc7777..df3a9edd 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -5,6 +5,7 @@ use core::time::Duration; use cpal::FromSample; +use crate::channel_bitmask::{add_channel_mask, ChannelBitmask, ChannelBitmaskAdapter}; use crate::Sample; pub use self::agc::AutomaticGainControl; @@ -468,6 +469,19 @@ where SamplesConverter::new(self) } + /// Returns a source that implements the [`ChannelBitmask`] trait and reports + /// the given `channel_bitmask`. + /// + /// # Panics + /// + /// If the `channel_bitmask.count_ones()` is not equal to `self.channels()`. + fn add_channel_mask(self, channel_bitmask: ChannelBitmask) -> ChannelBitmaskAdapter + where + Self: Sized, + { + add_channel_mask(self, channel_bitmask) + } + /// Makes the sound pausable. // TODO: add example #[inline] From 3ac8570e29b30e5aa00a1a5f50d88ad180f236f1 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Wed, 1 Jan 2025 14:19:25 -0800 Subject: [PATCH 18/19] Fixed broken use (did merge wrong!) --- src/source/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/source/mod.rs b/src/source/mod.rs index a8f0de04..91d96428 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -3,11 +3,10 @@ use core::fmt; use core::time::Duration; -use cpal::FromSample; - use crate::channel_bitmask::{add_channel_mask, ChannelBitmask, ChannelBitmaskAdapter}; use crate::common::{ChannelCount, SampleRate}; use crate::Sample; +use dasp_sample::FromSample; pub use self::agc::AutomaticGainControl; pub use self::amplify::Amplify; From 5d2cbbff1ba731f69590b80843cecf93019ce5f8 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Wed, 1 Jan 2025 14:29:49 -0800 Subject: [PATCH 19/19] Added a defualt implementation of channel_bitmask --- src/source/mod.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/source/mod.rs b/src/source/mod.rs index 91d96428..b05de51b 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -3,7 +3,7 @@ use core::fmt; use core::time::Duration; -use crate::channel_bitmask::{add_channel_mask, ChannelBitmask, ChannelBitmaskAdapter}; +use crate::channel_bitmask::{self, add_channel_mask, ChannelBitmask, ChannelBitmaskAdapter}; use crate::common::{ChannelCount, SampleRate}; use crate::Sample; use dasp_sample::FromSample; @@ -176,6 +176,18 @@ where /// `None` indicates at the same time "infinite" or "unknown". fn total_duration(&self) -> Option; + /// A default implemenation of `channel_bitmask` if not otherwise provided. This uses a + /// very simple heuristic to either return `FRONT_CENTER` or `STEREO` in the case the source + /// is one or two channels, respectiely. Otherwise returns `UNDEFINED`. + #[inline] + fn channel_bitmask(&self) -> ChannelBitmask { + match self.channels() { + 1 => channel_bitmask::FRONT_CENTER, + 2 => channel_bitmask::STEREO, + _ => channel_bitmask::UNDEFINED, + } + } + /// Stores the source in a buffer in addition to returning it. This iterator can be cloned. #[inline] fn buffered(self) -> Buffered