Created
October 23, 2019 14:36
-
-
Save ChunMinChang/14308c25e08e2e15e86a0e5acf8531ed to your computer and use it in GitHub Desktop.
A mixer prototype
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[derive(Clone, Debug, PartialEq)] | |
enum Channel { | |
FrontLeft = 0, | |
FrontRight = 1, | |
FrontCenter = 2, | |
LowFrequency = 3, | |
BackLeft = 4, | |
BackRight = 5, | |
FrontLeftOfCenter = 6, | |
FrontRightOfCenter = 7, | |
BackCenter = 8, | |
SideLeft = 9, | |
SideRight = 10, | |
TopCenter = 11, | |
TopFrontLeft = 12, | |
TopFrontCenter = 13, | |
TopFrontRight = 14, | |
TopBackLeft = 15, | |
TopBackCenter = 16, | |
TopBackRight = 17, | |
Silence = 18, | |
} | |
const CHANNELS: usize = Channel::Silence as usize + 1; | |
// A mixer mixing M-channel input data to N-channel output data within defined layouts. | |
struct Mixer { | |
input_layout: Vec<Channel>, | |
output_layout: Vec<Channel>, | |
matrix: Vec<Vec<f32>>, | |
} | |
impl Mixer { | |
// Create a mixer that can mix M-channel input data to N-channel output data, based on the | |
// given input and output layout (channel order). Except Silence channel, the channel in | |
// the layout cannot be duplicate. | |
// A Silence channel in input_layout means its corresponding input-channel data won't be | |
// mixed into the output-channel data. A Silence channel in output_layout means its | |
// corresponding output-channel data will be zero. | |
fn new(input_layout: Vec<Channel>, output_layout: Vec<Channel>) -> Self { | |
// Preprocess the mixing matrix. | |
let matrix = Mixer::generate_matrix(&input_layout, &output_layout); | |
Self { | |
input_layout, | |
output_layout, | |
matrix, | |
} | |
} | |
// Mix M-channel input data to N-channel output data. The order of the input-channel data must | |
// be the same of the given input_layout and the order of the calculated output-channel data | |
// will be the same of the given output_layout. | |
fn mix(&self, input_buffer: &[f32], output_buffer: &mut [f32]) { | |
// self.matrix is a NxM-matrix containing mixing coefficients such that | |
// output[i] = self.matrix[i][j] * input[j] for all j in [0, M), where i is in [0, N). | |
assert_eq!(self.matrix.len(), output_buffer.len()); | |
for (i, output) in output_buffer.iter_mut().enumerate() { | |
*output = 0.0; | |
assert_eq!(self.matrix[i].len(), input_buffer.len()); | |
for (j, input) in input_buffer.iter().enumerate() { | |
*output += self.matrix[i][j] * input; | |
} | |
} | |
} | |
// Given a Mx1-matrix input_layout, which stating the order of M input channels, | |
// and a Nx1 matrix output_layout, which stating the order of N input channels, | |
// generate a NxM-matrix m with mixing coefficients such that | |
// output_channel_data[i] = m[i][j] * inpu_channel_data[j] for all j in [0, M), | |
// where i is in [0, N). | |
// Instead of representing a vertical Nx1-matrix formally by a 2D-vector with N | |
// rows vector containing only 1 element, the Nx1-matrix, input_layout and output_layout, | |
// will be represented by a 1D-vector with N elements for convenience. | |
fn generate_matrix(input_layout: &[Channel], output_layout: &[Channel]) -> Vec<Vec<f32>> { | |
assert!(Mixer::validate_layout(&input_layout)); | |
assert!(Mixer::validate_layout(&output_layout)); | |
// Now, generate a CHANNELSxCHANNELS matrix m such that | |
// output_channel_data[i] = m[i][j] * input_channel_data[j] for all j in [0, CHANNELS), | |
// where i is in [0, CHANNELS). The channels of input and output is sorted by the order | |
// defined in enum Channel. That is, the output-channel and input-channel are enum | |
// Channel(i) and Channel(j) respectively. | |
let mut matrix = [[0.0; CHANNELS]; CHANNELS]; | |
let silence_index = Channel::Silence as usize; | |
for (i, row) in matrix.iter_mut().enumerate() { | |
if i != silence_index { | |
row[i] = 1.0; | |
} | |
} | |
// TODO: Generate more mixing coefficients to matrix here ... | |
// Generate a NxM-matrix m such that | |
// output_channel_data[i] = m[i][j] * inpu_channel_data[j] for all j in [0, M), where i is in [0, N). | |
// The channels of input and output is defined by input_layout and output_layout. | |
// That is, output-channel i is output_layout[i] and input-channel j is input_layout[j]. | |
let mut mixing_matrix = vec![vec![0.0; input_layout.len()]; output_layout.len()]; | |
for (i, output_channel) in output_layout.iter().enumerate() { | |
let output_channel_index = output_channel.clone() as usize; | |
for (j, input_channel) in input_layout.iter().enumerate() { | |
let input_channel_index = input_channel.clone() as usize; | |
mixing_matrix[i][j] = matrix[output_channel_index][input_channel_index]; | |
} | |
} | |
// TODO: Normalizing the mixing_matrix ... | |
mixing_matrix | |
} | |
// Except Silence channel, the duplicated channels are not allowed. | |
fn validate_layout(layout: &[Channel]) -> bool { | |
let mut map = 0; // Use bitmap as a hash table. | |
for channel in layout { | |
if channel == &Channel::Silence { | |
continue; | |
} | |
let bit = 1 << channel.clone() as usize; | |
if map & bit != 0 { | |
return false; | |
} | |
map |= bit; | |
} | |
true | |
} | |
} | |
fn main() { | |
let input = vec![ | |
Channel::FrontLeft, | |
Channel::Silence, | |
Channel::FrontRight, | |
Channel::FrontCenter, | |
]; | |
let output = vec![ | |
Channel::Silence, | |
Channel::FrontRight, | |
Channel::FrontLeft, | |
Channel::Silence, | |
Channel::FrontCenter, | |
Channel::BackCenter, | |
]; | |
let matrix = Mixer::generate_matrix(&input, &output); | |
println!("{:?} = {:?} * {:?}", output, matrix, input); | |
let input_buffer = [1.0, 2.0, 3.0, 4.0]; | |
let mut output_buffer = vec![0.0; output.len()]; | |
let mixer = Mixer::new(input, output); | |
mixer.mix(&input_buffer, &mut output_buffer.as_mut_slice()); | |
println!("{:?} = {:?} * {:?}", output_buffer, matrix, input_buffer); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment