in src/backend/mod.rs [3363:3534]
fn create_audiounits(
&mut self,
shared_voice_processing_unit: &mut SharedVoiceProcessingUnitManager,
) -> Result<(device_info, device_info)> {
self.debug_assert_is_on_stream_queue();
let should_use_voice_processing_unit = self.has_input()
&& (self
.input_stream_params
.prefs()
.contains(StreamPrefs::VOICE)
|| CoreStreamData::should_force_vpio_for_input_device(self.input_device.id))
&& !self.should_block_vpio_for_device_pair(&self.input_device, &self.output_device)
&& macos_kernel_major_version() != Ok(MACOS_KERNEL_MAJOR_VERSION_MONTEREY);
let should_use_aggregate_device = {
// It's impossible to create an aggregate device from an aggregate device, and it's
// unnecessary to create an aggregate device when opening the same device input/output. In
// all other cases, use an aggregate device.
let mut either_already_aggregate = false;
if self.has_input() {
let input_is_aggregate =
get_device_transport_type(self.input_device.id, DeviceType::INPUT).unwrap_or(0)
== kAudioDeviceTransportTypeAggregate;
if input_is_aggregate {
either_already_aggregate = true;
}
cubeb_log!(
"Input device ID: {} (aggregate: {:?})",
self.input_device.id,
input_is_aggregate
);
}
if self.has_output() {
let output_is_aggregate =
get_device_transport_type(self.output_device.id, DeviceType::OUTPUT)
.unwrap_or(0)
== kAudioDeviceTransportTypeAggregate;
if output_is_aggregate {
either_already_aggregate = true;
}
cubeb_log!(
"Output device ID: {} (aggregate: {:?})",
self.output_device.id,
output_is_aggregate
);
}
// Only use an aggregate device when the device are different.
self.has_input()
&& self.has_output()
&& self.input_device.id != self.output_device.id
&& !either_already_aggregate
};
// Create an AudioUnit:
// - If we're eligible to use voice processing, try creating a VoiceProcessingIO AudioUnit.
// - If we should use an aggregate device, try creating one and input and output AudioUnits next.
// - As last resort, create regular AudioUnits. This is also the normal non-duplex path.
if should_use_voice_processing_unit {
if let Ok(mut au_handle) = get_voiceprocessing_audiounit(
shared_voice_processing_unit,
&self.input_device,
&self.output_device,
) {
self.input_unit = au_handle.as_mut().unit;
if self.has_output() {
self.output_unit = au_handle.as_mut().unit;
}
self.voiceprocessing_unit_handle = Some(au_handle);
return Ok((self.input_device.clone(), self.output_device.clone()));
}
cubeb_log!(
"({:p}) Failed to get VoiceProcessingIO AudioUnit. Trying a regular one.",
self.stm_ptr
);
}
if should_use_aggregate_device {
if let Ok(device) = AggregateDevice::new(self.input_device.id, self.output_device.id) {
let in_dev_info = {
device_info {
id: device.get_device_id(),
..self.input_device
}
};
let out_dev_info = {
device_info {
id: device.get_device_id(),
..self.output_device
}
};
match (
create_audiounit(&in_dev_info),
create_audiounit(&out_dev_info),
) {
(Ok(in_au), Ok(out_au)) => {
cubeb_log!(
"({:p}) Using an aggregate device {} for input and output.",
self.stm_ptr,
device.get_device_id()
);
self.aggregate_device = Some(device);
self.input_unit = in_au;
self.output_unit = out_au;
return Ok((in_dev_info, out_dev_info));
}
(Err(e), Ok(au)) => {
cubeb_log!(
"({:p}) Failed to create input AudioUnit for aggregate device. Error: {}.",
self.stm_ptr,
e
);
dispose_audio_unit(au);
}
(Ok(au), Err(e)) => {
cubeb_log!(
"({:p}) Failed to create output AudioUnit for aggregate device. Error: {}.",
self.stm_ptr,
e
);
dispose_audio_unit(au);
}
(Err(e), _) => {
cubeb_log!(
"({:p}) Failed to create AudioUnits for aggregate device. Error: {}.",
self.stm_ptr,
e
);
}
}
}
cubeb_log!(
"({:p}) Failed to set up aggregate device. Using regular AudioUnits.",
self.stm_ptr
);
}
if self.has_input() {
match create_audiounit(&self.input_device) {
Ok(in_au) => self.input_unit = in_au,
Err(e) => {
cubeb_log!(
"({:p}) Failed to create regular AudioUnit for input. Error: {}",
self.stm_ptr,
e
);
return Err(e);
}
}
}
if self.has_output() {
match create_audiounit(&self.output_device) {
Ok(out_au) => self.output_unit = out_au,
Err(e) => {
cubeb_log!(
"({:p}) Failed to create regular AudioUnit for output. Error: {}",
self.stm_ptr,
e
);
if !self.input_unit.is_null() {
dispose_audio_unit(self.input_unit);
self.input_unit = ptr::null_mut();
}
return Err(e);
}
}
}
Ok((self.input_device.clone(), self.output_device.clone()))
}