diff options
Diffstat (limited to 'third_party/rust/cubeb-coreaudio/src/backend/mixer.rs')
-rw-r--r-- | third_party/rust/cubeb-coreaudio/src/backend/mixer.rs | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/third_party/rust/cubeb-coreaudio/src/backend/mixer.rs b/third_party/rust/cubeb-coreaudio/src/backend/mixer.rs new file mode 100644 index 0000000000..a4f63926b1 --- /dev/null +++ b/third_party/rust/cubeb-coreaudio/src/backend/mixer.rs @@ -0,0 +1,492 @@ +use cubeb_backend::{ChannelLayout, SampleFormat}; +use std::mem; +use std::os::raw::{c_int, c_void}; + +extern crate audio_mixer; +pub use self::audio_mixer::Channel; + +const CHANNEL_OERDER: [audio_mixer::Channel; audio_mixer::Channel::count()] = [ + audio_mixer::Channel::FrontLeft, + audio_mixer::Channel::FrontRight, + audio_mixer::Channel::FrontCenter, + audio_mixer::Channel::LowFrequency, + audio_mixer::Channel::BackLeft, + audio_mixer::Channel::BackRight, + audio_mixer::Channel::FrontLeftOfCenter, + audio_mixer::Channel::FrontRightOfCenter, + audio_mixer::Channel::BackCenter, + audio_mixer::Channel::SideLeft, + audio_mixer::Channel::SideRight, + audio_mixer::Channel::TopCenter, + audio_mixer::Channel::TopFrontLeft, + audio_mixer::Channel::TopFrontCenter, + audio_mixer::Channel::TopFrontRight, + audio_mixer::Channel::TopBackLeft, + audio_mixer::Channel::TopBackCenter, + audio_mixer::Channel::TopBackRight, + audio_mixer::Channel::Silence, +]; + +pub fn get_channel_order(channel_layout: ChannelLayout) -> Vec<audio_mixer::Channel> { + let mut map = channel_layout.bits(); + let mut order = Vec::new(); + let mut channel_index: usize = 0; + while map != 0 { + if map & 1 == 1 { + order.push(CHANNEL_OERDER[channel_index]); + } + map >>= 1; + channel_index += 1; + } + order +} + +fn get_default_channel_order(channel_count: usize) -> Vec<audio_mixer::Channel> { + assert_ne!(channel_count, 0); + let mut channels = Vec::with_capacity(channel_count); + for channel in CHANNEL_OERDER.iter().take(channel_count) { + channels.push(*channel); + } + + if channel_count > CHANNEL_OERDER.len() { + channels.extend(vec![ + audio_mixer::Channel::Silence; + channel_count - CHANNEL_OERDER.len() + ]); + } + + channels +} + +#[derive(Debug)] +enum MixerType { + IntegerMixer(audio_mixer::Mixer<i16>), + FloatMixer(audio_mixer::Mixer<f32>), +} + +impl MixerType { + fn new( + format: SampleFormat, + input_channels: &[audio_mixer::Channel], + output_channels: &[audio_mixer::Channel], + ) -> Self { + match format { + SampleFormat::S16LE | SampleFormat::S16BE | SampleFormat::S16NE => { + cubeb_log!("Create an integer type(i16) mixer"); + Self::IntegerMixer(audio_mixer::Mixer::<i16>::new( + input_channels, + output_channels, + )) + } + SampleFormat::Float32LE | SampleFormat::Float32BE | SampleFormat::Float32NE => { + cubeb_log!("Create an floating type(f32) mixer"); + Self::FloatMixer(audio_mixer::Mixer::<f32>::new( + input_channels, + output_channels, + )) + } + } + } + + fn sample_size(&self) -> usize { + match self { + MixerType::IntegerMixer(_) => mem::size_of::<i16>(), + MixerType::FloatMixer(_) => mem::size_of::<f32>(), + } + } + + fn input_channels(&self) -> &[Channel] { + match self { + MixerType::IntegerMixer(m) => m.input_channels(), + MixerType::FloatMixer(m) => m.input_channels(), + } + } + + fn output_channels(&self) -> &[Channel] { + match self { + MixerType::IntegerMixer(m) => m.output_channels(), + MixerType::FloatMixer(m) => m.output_channels(), + } + } + + fn mix( + &self, + input_buffer_ptr: *const (), + input_buffer_size: usize, + output_buffer_ptr: *mut (), + output_buffer_size: usize, + frames: usize, + ) { + use std::slice; + + // Check input buffer size. + let size_needed = frames * self.input_channels().len() * self.sample_size(); + assert!(input_buffer_size >= size_needed); + // Check output buffer size. + let size_needed = frames * self.output_channels().len() * self.sample_size(); + assert!(output_buffer_size >= size_needed); + + match self { + MixerType::IntegerMixer(m) => { + let in_buf_ptr = input_buffer_ptr as *const i16; + let out_buf_ptr = output_buffer_ptr as *mut i16; + let input_buffer = unsafe { + slice::from_raw_parts(in_buf_ptr, frames * self.input_channels().len()) + }; + let output_buffer = unsafe { + slice::from_raw_parts_mut(out_buf_ptr, frames * self.output_channels().len()) + }; + let mut in_buf = input_buffer.chunks(self.input_channels().len()); + let mut out_buf = output_buffer.chunks_mut(self.output_channels().len()); + for _ in 0..frames { + m.mix(in_buf.next().unwrap(), out_buf.next().unwrap()); + } + } + MixerType::FloatMixer(m) => { + let in_buf_ptr = input_buffer_ptr as *const f32; + let out_buf_ptr = output_buffer_ptr as *mut f32; + let input_buffer = unsafe { + slice::from_raw_parts(in_buf_ptr, frames * self.input_channels().len()) + }; + let output_buffer = unsafe { + slice::from_raw_parts_mut(out_buf_ptr, frames * self.output_channels().len()) + }; + let mut in_buf = input_buffer.chunks(self.input_channels().len()); + let mut out_buf = output_buffer.chunks_mut(self.output_channels().len()); + for _ in 0..frames { + m.mix(in_buf.next().unwrap(), out_buf.next().unwrap()); + } + } + }; + } +} + +#[derive(Debug)] +pub struct Mixer { + mixer: MixerType, + // Only accessed from callback thread. + buffer: Vec<u8>, +} + +impl Mixer { + pub fn new( + format: SampleFormat, + in_channel_count: usize, + input_layout: ChannelLayout, + out_channel_count: usize, + mut output_channels: Vec<audio_mixer::Channel>, + ) -> Self { + assert!(in_channel_count > 0); + assert!(out_channel_count > 0); + + cubeb_log!( + "Creating a mixer with input channel count: {}, input layout: {:?},\ + out channel count: {}, output channels: {:?}", + in_channel_count, + input_layout, + out_channel_count, + output_channels + ); + + let input_channels = if in_channel_count as u32 != input_layout.bits().count_ones() { + cubeb_log!( + "Mismatch between input channels and layout. Applying default layout instead" + ); + get_default_channel_order(in_channel_count) + } else { + get_channel_order(input_layout) + }; + + // When having one or two channel, force mono or stereo. Some devices (namely, + // Bose QC35, mark 1 and 2), expose a single channel mapped to the right for + // some reason. Some devices (e.g., builtin speaker on MacBook Pro 2018) map + // the channel layout to the undefined channels. + if out_channel_count == 1 { + output_channels = vec![audio_mixer::Channel::FrontCenter]; + } else if out_channel_count == 2 { + output_channels = vec![ + audio_mixer::Channel::FrontLeft, + audio_mixer::Channel::FrontRight, + ]; + } + + let all_silence = vec![audio_mixer::Channel::Silence; out_channel_count]; + if output_channels.is_empty() + || out_channel_count != output_channels.len() + || all_silence == output_channels + || Self::non_silent_duplicate_channel_present(&output_channels) + { + cubeb_log!("Use invalid layout. Apply default layout instead"); + output_channels = get_default_channel_order(out_channel_count); + } + + Self { + mixer: MixerType::new(format, &input_channels, &output_channels), + buffer: Vec::new(), + } + } + + pub fn update_buffer_size(&mut self, frames: usize) -> bool { + let size_needed = frames * self.mixer.input_channels().len() * self.mixer.sample_size(); + let elements_needed = size_needed / mem::size_of::<u8>(); + if self.buffer.len() < elements_needed { + self.buffer.resize(elements_needed, 0); + true + } else { + false + } + } + + pub fn get_buffer_mut_ptr(&mut self) -> *mut u8 { + self.buffer.as_mut_ptr() + } + + // `update_buffer_size` must be called before this. + pub fn mix(&self, frames: usize, dest_buffer: *mut c_void, dest_buffer_size: usize) -> c_int { + let (src_buffer_ptr, src_buffer_size) = self.get_buffer_info(); + self.mixer.mix( + src_buffer_ptr as *const (), + src_buffer_size, + dest_buffer as *mut (), + dest_buffer_size, + frames, + ); + 0 + } + + fn get_buffer_info(&self) -> (*const u8, usize) { + ( + self.buffer.as_ptr(), + self.buffer.len() * mem::size_of::<u8>(), + ) + } + + fn non_silent_duplicate_channel_present(channels: &[audio_mixer::Channel]) -> bool { + let mut bitmap: u32 = 0; + for channel in channels { + if channel != &Channel::Silence { + if (bitmap & channel.bitmask()) != 0 { + return true; + } + bitmap |= channel.bitmask(); + } + } + false + } +} + +// This test gives a clear channel order of the ChannelLayout passed from cubeb interface. +#[test] +fn test_get_channel_order() { + assert_eq!( + get_channel_order(ChannelLayout::MONO), + [Channel::FrontCenter] + ); + assert_eq!( + get_channel_order(ChannelLayout::MONO_LFE), + [Channel::FrontCenter, Channel::LowFrequency] + ); + assert_eq!( + get_channel_order(ChannelLayout::STEREO), + [Channel::FrontLeft, Channel::FrontRight] + ); + assert_eq!( + get_channel_order(ChannelLayout::STEREO_LFE), + [ + Channel::FrontLeft, + Channel::FrontRight, + Channel::LowFrequency + ] + ); + assert_eq!( + get_channel_order(ChannelLayout::_3F), + [ + Channel::FrontLeft, + Channel::FrontRight, + Channel::FrontCenter + ] + ); + assert_eq!( + get_channel_order(ChannelLayout::_3F_LFE), + [ + Channel::FrontLeft, + Channel::FrontRight, + Channel::FrontCenter, + Channel::LowFrequency + ] + ); + assert_eq!( + get_channel_order(ChannelLayout::_2F1), + [Channel::FrontLeft, Channel::FrontRight, Channel::BackCenter] + ); + assert_eq!( + get_channel_order(ChannelLayout::_2F1_LFE), + [ + Channel::FrontLeft, + Channel::FrontRight, + Channel::LowFrequency, + Channel::BackCenter + ] + ); + assert_eq!( + get_channel_order(ChannelLayout::_3F1), + [ + Channel::FrontLeft, + Channel::FrontRight, + Channel::FrontCenter, + Channel::BackCenter + ] + ); + assert_eq!( + get_channel_order(ChannelLayout::_3F1_LFE), + [ + Channel::FrontLeft, + Channel::FrontRight, + Channel::FrontCenter, + Channel::LowFrequency, + Channel::BackCenter + ] + ); + assert_eq!( + get_channel_order(ChannelLayout::_2F2), + [ + Channel::FrontLeft, + Channel::FrontRight, + Channel::SideLeft, + Channel::SideRight + ] + ); + assert_eq!( + get_channel_order(ChannelLayout::_2F2_LFE), + [ + Channel::FrontLeft, + Channel::FrontRight, + Channel::LowFrequency, + Channel::SideLeft, + Channel::SideRight + ] + ); + assert_eq!( + get_channel_order(ChannelLayout::QUAD), + [ + Channel::FrontLeft, + Channel::FrontRight, + Channel::BackLeft, + Channel::BackRight + ] + ); + assert_eq!( + get_channel_order(ChannelLayout::QUAD_LFE), + [ + Channel::FrontLeft, + Channel::FrontRight, + Channel::LowFrequency, + Channel::BackLeft, + Channel::BackRight + ] + ); + assert_eq!( + get_channel_order(ChannelLayout::_3F2), + [ + Channel::FrontLeft, + Channel::FrontRight, + Channel::FrontCenter, + Channel::SideLeft, + Channel::SideRight + ] + ); + assert_eq!( + get_channel_order(ChannelLayout::_3F2_LFE), + [ + Channel::FrontLeft, + Channel::FrontRight, + Channel::FrontCenter, + Channel::LowFrequency, + Channel::SideLeft, + Channel::SideRight + ] + ); + assert_eq!( + get_channel_order(ChannelLayout::_3F2_BACK), + [ + Channel::FrontLeft, + Channel::FrontRight, + Channel::FrontCenter, + Channel::BackLeft, + Channel::BackRight + ] + ); + assert_eq!( + get_channel_order(ChannelLayout::_3F2_LFE_BACK), + [ + Channel::FrontLeft, + Channel::FrontRight, + Channel::FrontCenter, + Channel::LowFrequency, + Channel::BackLeft, + Channel::BackRight + ] + ); + assert_eq!( + get_channel_order(ChannelLayout::_3F3R_LFE), + [ + Channel::FrontLeft, + Channel::FrontRight, + Channel::FrontCenter, + Channel::LowFrequency, + Channel::BackCenter, + Channel::SideLeft, + Channel::SideRight + ] + ); + assert_eq!( + get_channel_order(ChannelLayout::_3F4_LFE), + [ + Channel::FrontLeft, + Channel::FrontRight, + Channel::FrontCenter, + Channel::LowFrequency, + Channel::BackLeft, + Channel::BackRight, + Channel::SideLeft, + Channel::SideRight + ] + ); +} + +#[test] +fn test_get_default_channel_order() { + for len in 1..CHANNEL_OERDER.len() + 10 { + let channels = get_default_channel_order(len); + if len <= CHANNEL_OERDER.len() { + assert_eq!(channels, &CHANNEL_OERDER[..len]); + } else { + let silences = vec![audio_mixer::Channel::Silence; len - CHANNEL_OERDER.len()]; + assert_eq!(channels[..CHANNEL_OERDER.len()], CHANNEL_OERDER); + assert_eq!(&channels[CHANNEL_OERDER.len()..], silences.as_slice()); + } + } +} + +#[test] +fn test_non_silent_duplicate_channels() { + let duplicate = [ + Channel::FrontLeft, + Channel::Silence, + Channel::FrontRight, + Channel::FrontCenter, + Channel::Silence, + Channel::FrontRight, + ]; + assert!(Mixer::non_silent_duplicate_channel_present(&duplicate)); + + let non_duplicate = [ + Channel::FrontLeft, + Channel::Silence, + Channel::FrontRight, + Channel::FrontCenter, + Channel::Silence, + Channel::Silence, + ]; + assert!(!Mixer::non_silent_duplicate_channel_present(&non_duplicate)); +} |