diff --git a/src/backend/mod.rs b/src/backend/mod.rs index b990845d..838e32b5 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -829,14 +829,14 @@ fn get_default_device_id(devtype: DeviceType) -> std::result::Result Vec { +fn audiounit_convert_channel_layout(layout: &AudioChannelLayout) -> Result> { if layout.mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions { // kAudioChannelLayoutTag_UseChannelBitmap // kAudioChannelLayoutTag_Mono // kAudioChannelLayoutTag_Stereo // .... cubeb_log!("Only handling UseChannelDescriptions for now.\n"); - return Vec::new(); + return Err(Error::error()); } let channel_descriptions = unsafe { @@ -852,10 +852,10 @@ fn audiounit_convert_channel_layout(layout: &AudioChannelLayout) -> Vec Vec { +fn audiounit_get_preferred_channel_layout(output_unit: AudioUnit) -> Result> { let mut rv = NO_ERR; let mut size: usize = 0; rv = audio_unit_get_property_info( @@ -871,7 +871,7 @@ fn audiounit_get_preferred_channel_layout(output_unit: AudioUnit) -> Vec 0); @@ -889,7 +889,7 @@ fn audiounit_get_preferred_channel_layout(output_unit: AudioUnit) -> Vec Vec Vec { +fn audiounit_get_current_channel_layout(output_unit: AudioUnit) -> Result> { let mut rv = NO_ERR; let mut size: usize = 0; rv = audio_unit_get_property_info( @@ -913,8 +913,7 @@ fn audiounit_get_current_channel_layout(output_unit: AudioUnit) -> Vec 0); @@ -932,12 +931,25 @@ fn audiounit_get_current_channel_layout(output_unit: AudioUnit) -> Vec Result> { + audiounit_get_current_channel_layout(output_unit) + .or_else(|_| { + // The kAudioUnitProperty_AudioChannelLayout property isn't known before + // macOS 10.12, attempt another method. + cubeb_log!( + "Cannot get current channel layout for audiounit @ {:p}. Trying preferred channel layout.", + output_unit + ); + audiounit_get_preferred_channel_layout(output_unit) + }) +} + fn start_audiounit(unit: AudioUnit) -> Result<()> { let status = audio_output_unit_start(unit); if status == NO_ERR { @@ -2956,20 +2968,22 @@ impl<'ctx> CoreStreamData<'ctx> { e })?; - // XXX - let device_layout = if using_voice_processing_unit { - let mut channels = Vec::with_capacity(output_hw_desc.mChannelsPerFrame as usize); - match output_hw_desc.mChannelsPerFrame { - 1 => channels.push(mixer::Channel::FrontCenter), - _ => { - channels.push(mixer::Channel::FrontLeft); - channels.push(mixer::Channel::FrontRight); - } - } - channels - } else { - audiounit_get_current_channel_layout(self.output_unit) - }; + let device_layout = self + .get_output_channel_layout() + .map_err(|e| { + cubeb_log!( + "({:p}) Could not get any channel layout. Defaulting to no channels.", + self.stm_ptr + ); + e + }) + .unwrap_or_default(); + + cubeb_log!( + "({:p} Using output device channel layout {:?}", + self.stm_ptr, + device_layout + ); // The mixer will be set up when // 1. using aggregate device whose input device has output channels @@ -3445,6 +3459,26 @@ impl<'ctx> CoreStreamData<'ctx> { Ok(()) } + + fn get_output_channel_layout(&self) -> Result> { + if self.input_unit != self.output_unit { + return get_channel_layout(self.output_unit); + } + + // The VoiceProcessingIO unit (as tried on MacOS 14) is known to not support + // kAudioUnitProperty_AudioChannelLayout queries, and to lie about + // kAudioDevicePropertyPreferredChannelLayout. If we're using + // VoiceProcessingIO, try standing up a regular AudioUnit and query that. + cubeb_log!( + "({:p}) get_output_channel_layout with a VoiceProcessingIO output unit. Trying a dedicated unit.", + self.stm_ptr + ); + let mut dedicated_unit = create_audiounit(&self.output_device)?; + let res = get_channel_layout(dedicated_unit); + dispose_audio_unit(dedicated_unit); + dedicated_unit = ptr::null_mut(); + res + } } impl<'ctx> Drop for CoreStreamData<'ctx> { diff --git a/src/backend/tests/api.rs b/src/backend/tests/api.rs index 8322162d..319b27f5 100644 --- a/src/backend/tests/api.rs +++ b/src/backend/tests/api.rs @@ -700,7 +700,7 @@ fn test_convert_channel_layout() { } let layout_ref = unsafe { &(*(&layout as *const TestLayout as *const AudioChannelLayout)) }; assert_eq!( - &audiounit_convert_channel_layout(layout_ref), + &audiounit_convert_channel_layout(layout_ref).unwrap(), expected_layout ); } @@ -711,7 +711,9 @@ fn test_convert_channel_layout() { #[test] fn test_get_preferred_channel_layout_output() { match test_get_default_audiounit(Scope::Output) { - Some(unit) => assert!(!audiounit_get_preferred_channel_layout(unit.get_inner()).is_empty()), + Some(unit) => assert!(!audiounit_get_preferred_channel_layout(unit.get_inner()) + .unwrap() + .is_empty()), None => println!("No output audiounit for test."), } } @@ -721,7 +723,9 @@ fn test_get_preferred_channel_layout_output() { #[test] fn test_get_current_channel_layout_output() { match test_get_default_audiounit(Scope::Output) { - Some(unit) => assert!(!audiounit_get_current_channel_layout(unit.get_inner()).is_empty()), + Some(unit) => assert!(!audiounit_get_current_channel_layout(unit.get_inner()) + .unwrap() + .is_empty()), None => println!("No output audiounit for test."), } }