summaryrefslogtreecommitdiffstats
path: root/third_party/rust/audio-mixer/src
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/audio-mixer/src')
-rw-r--r--third_party/rust/audio-mixer/src/channel.rs4
-rw-r--r--third_party/rust/audio-mixer/src/coefficient.rs239
2 files changed, 175 insertions, 68 deletions
diff --git a/third_party/rust/audio-mixer/src/channel.rs b/third_party/rust/audio-mixer/src/channel.rs
index b32936895e..d913df39e8 100644
--- a/third_party/rust/audio-mixer/src/channel.rs
+++ b/third_party/rust/audio-mixer/src/channel.rs
@@ -21,6 +21,7 @@ pub enum Channel {
TopBackCenter = 16,
TopBackRight = 17,
Silence = 18,
+ Discrete = 19, // To be used based on its index
}
impl Channel {
@@ -29,7 +30,7 @@ impl Channel {
}
pub const fn count() -> usize {
- Channel::Silence as usize + 1
+ Channel::Discrete as usize + 1
}
pub const fn bitmask(self) -> u32 {
@@ -58,6 +59,7 @@ bitflags! {
const TOP_BACK_CENTER = Channel::TopBackCenter.bitmask();
const TOP_BACK_RIGHT = Channel::TopBackRight.bitmask();
const SILENCE = Channel::Silence.bitmask();
+ const DISCRETE = Channel::Discrete.bitmask();
}
}
diff --git a/third_party/rust/audio-mixer/src/coefficient.rs b/third_party/rust/audio-mixer/src/coefficient.rs
index 799d50289c..c7999ee929 100644
--- a/third_party/rust/audio-mixer/src/coefficient.rs
+++ b/third_party/rust/audio-mixer/src/coefficient.rs
@@ -9,7 +9,7 @@ const CHANNELS: usize = Channel::count();
#[derive(Debug)]
enum Error {
- DuplicateNonSilenceChannel,
+ DuplicateChannel,
AsymmetricChannels,
}
@@ -28,13 +28,15 @@ impl ChannelLayout {
})
}
- // Except Silence channel, the duplicate channels are not allowed.
+ // Except Silence and Discrete channels, duplicate channels aren't allowed.
fn get_channel_map(channels: &[Channel]) -> Result<ChannelMap, Error> {
let mut map = ChannelMap::empty();
for channel in channels {
let bitmask = ChannelMap::from(*channel);
- if channel != &Channel::Silence && map.contains(bitmask) {
- return Err(Error::DuplicateNonSilenceChannel);
+ if (channel != &Channel::Silence && channel != &Channel::Discrete)
+ && map.contains(bitmask)
+ {
+ return Err(Error::DuplicateChannel);
}
map.insert(bitmask);
}
@@ -83,15 +85,41 @@ where
let input_layout = ChannelLayout::new(input_channels).expect("Invalid input layout");
let output_layout = ChannelLayout::new(output_channels).expect("Invalid output layout");
- let mixing_matrix =
- Self::build_mixing_matrix(input_layout.channel_map, output_layout.channel_map)
- .unwrap_or_else(|_| Self::get_basic_matrix());
-
- let coefficient_matrix = Self::pick_coefficients(
- &input_layout.channels,
- &output_layout.channels,
- &mixing_matrix,
- );
+ // Check if this is a professional audio interface rather than a sound card for playback, in
+ // which case it is expected to simply pass all the channel through without change.
+ // Those interfaces only have an explicit mapping for the stereo pair, but have lots of channels.
+ let mut only_stereo_or_discrete = true;
+ for channel in output_channels {
+ if *channel != Channel::Discrete
+ && *channel != Channel::FrontLeft
+ && *channel != Channel::FrontRight
+ {
+ only_stereo_or_discrete = false;
+ break;
+ }
+ }
+ let coefficient_matrix = if only_stereo_or_discrete && output_channels.len() > 2 {
+ let mut matrix = Vec::with_capacity(output_channels.len());
+ // Create a diagonal line of 1.0 for input channels
+ for (output_channel_index, _) in output_channels.iter().enumerate() {
+ let mut coefficients = Vec::with_capacity(input_channels.len());
+ coefficients.resize(input_channels.len(), 0.0);
+ if output_channel_index < coefficients.len() {
+ coefficients[output_channel_index] = 1.0;
+ }
+ matrix.push(coefficients);
+ }
+ matrix
+ } else {
+ let mixing_matrix =
+ Self::build_mixing_matrix(input_layout.channel_map, output_layout.channel_map)
+ .unwrap_or_else(|_| Self::get_basic_matrix());
+ Self::pick_coefficients(
+ &input_layout.channels,
+ &output_layout.channels,
+ &mixing_matrix,
+ )
+ };
let normalized_matrix = Self::normalize(T::max_coefficients_sum(), coefficient_matrix);
@@ -430,7 +458,7 @@ impl MixingCoefficient for f32 {
type Coef = f32;
fn max_coefficients_sum() -> f64 {
- f64::from(std::i32::MAX)
+ f64::from(i32::MAX)
}
fn coefficient_from_f64(value: f64) -> Self::Coef {
@@ -550,12 +578,12 @@ mod test {
#[test]
fn test_create_with_duplicate_silience_channels_f32() {
- test_create_with_duplicate_silience_channels::<f32>()
+ test_create_with_duplicate_channels::<f32>()
}
#[test]
fn test_create_with_duplicate_silience_channels_i16() {
- test_create_with_duplicate_silience_channels::<i16>()
+ test_create_with_duplicate_channels::<i16>()
}
#[test]
@@ -582,7 +610,7 @@ mod test {
test_create_with_duplicate_output_channels::<i16>()
}
- fn test_create_with_duplicate_silience_channels<T>()
+ fn test_create_with_duplicate_channels<T>()
where
T: MixingCoefficient,
T::Coef: Copy,
@@ -649,78 +677,155 @@ mod test {
}
#[test]
- fn test_get_redirect_matrix_f32() {
- test_get_redirect_matrix::<f32>();
+ fn test_get_discrete_mapping() {
+ test_get_discrete_mapping_matrix::<f32>();
+ test_get_discrete_mapping_matrix::<i16>();
}
#[test]
- fn test_get_redirect_matrix_i16() {
- test_get_redirect_matrix::<i16>();
+ fn test_get_discrete_mapping_too_many_channels() {
+ test_get_discrete_mapping_matrix_too_many_channels::<i16>();
+ test_get_discrete_mapping_matrix_too_many_channels::<f32>();
}
- fn test_get_redirect_matrix<T>()
- where
+ #[test]
+ fn test_get_regular_mapping_too_many_channels() {
+ test_get_regular_mapping_matrix_too_many_channels::<i16>();
+ test_get_regular_mapping_matrix_too_many_channels::<f32>();
+ }
+
+ // Check that a matrix is diagonal (1.0 on the diagnoal, 0.0 elsewhere). It's valid to have more input or output channels
+ fn assert_is_diagonal<T>(
+ coefficients: &Coefficient<T>,
+ input_channels: usize,
+ output_channels: usize,
+ ) where
T: MixingCoefficient,
T::Coef: Copy + Debug + PartialEq,
{
- // Create a matrix that only redirect the channels from input side to output side,
- // without mixing input audio data to output audio data.
- fn compute_redirect_matrix<T>(
- input_channels: &[Channel],
- output_channels: &[Channel],
- ) -> Vec<Vec<T::Coef>>
- where
- T: MixingCoefficient,
- {
- let mut matrix = Vec::with_capacity(output_channels.len());
- for output_channel in output_channels {
- let mut row = Vec::with_capacity(input_channels.len());
- for input_channel in input_channels {
- row.push(
- if input_channel != output_channel
- || input_channel == &Channel::Silence
- || output_channel == &Channel::Silence
- {
- 0.0
- } else {
- 1.0
- },
- );
+ for i in 0..input_channels {
+ for j in 0..output_channels {
+ if i == j {
+ assert_eq!(coefficients.get(i, j), T::coefficient_from_f64(1.0));
+ } else {
+ assert_eq!(coefficients.get(i, j), T::coefficient_from_f64(0.0));
}
- matrix.push(row);
}
-
- // Convert the type of the coefficients from f64 to T::Coef.
- matrix
- .into_iter()
- .map(|row| row.into_iter().map(T::coefficient_from_f64).collect())
- .collect()
}
+ println!(
+ "{:?} = {:?} * {:?}",
+ output_channels, coefficients.matrix, input_channels
+ );
+ }
+ fn test_get_discrete_mapping_matrix<T>()
+ where
+ T: MixingCoefficient,
+ T::Coef: Copy + Debug + PartialEq,
+ {
+ // typical 5.1
let input_channels = [
Channel::FrontLeft,
- Channel::Silence,
Channel::FrontRight,
Channel::FrontCenter,
+ Channel::BackLeft,
+ Channel::BackRight,
+ Channel::LowFrequency,
];
+ // going into 8 channels with a tagged stereo pair and discrete channels
let output_channels = [
- Channel::Silence,
Channel::FrontLeft,
- Channel::Silence,
+ Channel::FrontRight,
+ Channel::Discrete,
+ Channel::Discrete,
+ Channel::Discrete,
+ Channel::Discrete,
+ Channel::Discrete,
+ Channel::Discrete,
+ ];
+
+ // Get a pass-through matrix in the first 6 channels
+ let coefficients = Coefficient::<T>::create(&input_channels, &output_channels);
+ assert_is_diagonal::<T>(&coefficients, input_channels.len(), output_channels.len());
+ }
+
+ fn test_get_discrete_mapping_matrix_too_many_channels<T>()
+ where
+ T: MixingCoefficient,
+ T::Coef: Copy + Debug + PartialEq,
+ {
+ // 5.1.4
+ let input_channels = [
+ Channel::FrontLeft,
+ Channel::FrontRight,
Channel::FrontCenter,
- Channel::BackCenter,
+ Channel::LowFrequency,
+ Channel::FrontLeftOfCenter,
+ Channel::FrontRightOfCenter,
+ Channel::TopFrontLeft,
+ Channel::TopFrontRight,
+ Channel::BackLeft,
+ Channel::BackRight,
+ ];
+ // going into 8 channels with a tagged stereo pair and discrete channels
+ let output_channels = [
+ Channel::FrontLeft,
+ Channel::FrontRight,
+ Channel::Discrete,
+ Channel::Discrete,
+ Channel::Discrete,
+ Channel::Discrete,
+ Channel::Discrete,
+ Channel::Discrete,
];
- // Get a redirect matrix since the output layout is asymmetric.
- let coefficient = Coefficient::<T>::create(&input_channels, &output_channels);
+ // First 8 channels are to be played, last two are to be dropped.
+ let coefficients = Coefficient::<T>::create(&input_channels, &output_channels);
+ assert_is_diagonal(&coefficients, input_channels.len(), output_channels.len());
+ }
+
+ fn test_get_regular_mapping_matrix_too_many_channels<T>()
+ where
+ T: MixingCoefficient,
+ T::Coef: Copy + Debug + PartialEq,
+ {
+ // 5.1.4
+ let input_channels = [
+ Channel::FrontLeft,
+ Channel::FrontRight,
+ Channel::FrontCenter,
+ Channel::LowFrequency,
+ Channel::FrontLeftOfCenter,
+ Channel::FrontRightOfCenter,
+ Channel::TopFrontLeft,
+ Channel::TopFrontRight,
+ Channel::BackLeft,
+ Channel::BackRight,
+ ];
+ // going into a regular 5.1 sound card
+ let output_channels = [
+ Channel::FrontLeft,
+ Channel::FrontRight,
+ Channel::FrontCenter,
+ Channel::LowFrequency,
+ Channel::BackLeft,
+ Channel::BackRight,
+ ];
- let expected = compute_redirect_matrix::<T>(&input_channels, &output_channels);
- assert_eq!(coefficient.matrix, expected);
+ let coefficients = Coefficient::<T>::create(&input_channels, &output_channels);
- println!(
- "{:?} = {:?} * {:?}",
- output_channels, coefficient.matrix, input_channels
- );
+ // Non-unity gain non-silence coefficients must be present when down mixing.
+ let mut found_non_unity_non_silence = false;
+ for row in coefficients.matrix.iter() {
+ for coeff in row.iter() {
+ if T::coefficient_from_f64(1.0) != *coeff || T::coefficient_from_f64(0.0) != *coeff
+ {
+ found_non_unity_non_silence = true;
+ break;
+ }
+ }
+ }
+ assert!(found_non_unity_non_silence);
}
#[test]
@@ -732,7 +837,7 @@ mod test {
vec![4.0_f64, 6.0_f64, 10.0_f64],
];
- let mut max_row_sum: f64 = std::f64::MIN;
+ let mut max_row_sum: f64 = f64::MIN;
for row in &m {
max_row_sum = max_row_sum.max(row.iter().sum());
}
@@ -746,7 +851,7 @@ mod test {
let smaller_max = max_row_sum - 0.5_f64;
assert!(smaller_max > 0.0_f64);
let n = Coefficient::<f32>::normalize(smaller_max, m);
- let mut max_row_sum: f64 = std::f64::MIN;
+ let mut max_row_sum: f64 = f64::MIN;
for row in &n {
max_row_sum = max_row_sum.max(row.iter().sum());
assert!(row.iter().sum::<f64>() <= smaller_max);