Listen for changes to the configuration of the attached device too (#28045)

Release Notes:

- Fixed an issue causing "robot voice" when enabling the microphone on
some bluetooth headphones (hopefully).

Co-authored-by: Zed AI <ai+claude-3.7@zed.dev>
This commit is contained in:
Conrad Irwin 2025-04-03 21:05:54 -06:00 committed by GitHub
parent c04c5812b6
commit ee4b6a8db4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -163,9 +163,8 @@ impl AudioStack {
sample_rate: u32,
num_channels: u32,
) -> Result<()> {
let mut default_change_listener = DeviceChangeListener::new(false)?;
loop {
let mut device_change_listener = DeviceChangeListener::new(false)?;
let (output_device, output_config) = default_device(false)?;
let (end_on_drop_tx, end_on_drop_rx) = std::sync::mpsc::channel::<()>();
let mixer = mixer.clone();
@ -225,7 +224,7 @@ impl AudioStack {
end_on_drop_rx.recv().ok();
});
default_change_listener.next().await;
device_change_listener.next().await;
drop(end_on_drop_tx)
}
}
@ -236,8 +235,8 @@ impl AudioStack {
sample_rate: u32,
num_channels: u32,
) -> Result<()> {
let mut default_change_listener = DeviceChangeListener::new(true)?;
loop {
let mut device_change_listener = DeviceChangeListener::new(true)?;
let (device, config) = default_device(true)?;
let (end_on_drop_tx, end_on_drop_rx) = std::sync::mpsc::channel::<()>();
let apm = apm.clone();
@ -310,7 +309,7 @@ impl AudioStack {
.log_err();
});
default_change_listener.next().await;
device_change_listener.next().await;
drop(end_on_drop_tx)
}
}
@ -646,6 +645,7 @@ mod macos {
rx: UnboundedReceiver<()>,
callback: Box<PropertyListenerCallbackWrapper>,
input: bool,
device_id: AudioObjectID, // Store the device ID to properly remove listeners
}
trait _AssertSend: Send {}
@ -672,7 +672,9 @@ mod macos {
tx.unbounded_send(()).ok();
})));
unsafe {
// Get the current default device ID
let device_id = unsafe {
// Listen for default device changes
coreaudio::Error::from_os_status(AudioObjectAddPropertyListener(
kAudioObjectSystemObject,
&AudioObjectPropertyAddress {
@ -687,12 +689,78 @@ mod macos {
Some(property_listener_handler_shim),
&*callback as *const _ as *mut _,
))?;
}
// Also listen for changes to the device configuration
let device_id = if input {
let mut input_device: AudioObjectID = 0;
let mut prop_size = std::mem::size_of::<AudioObjectID>() as u32;
let result = coreaudio::sys::AudioObjectGetPropertyData(
kAudioObjectSystemObject,
&AudioObjectPropertyAddress {
mSelector: kAudioHardwarePropertyDefaultInputDevice,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
},
0,
std::ptr::null(),
&mut prop_size as *mut _,
&mut input_device as *mut _ as *mut _,
);
if result != 0 {
log::warn!("Failed to get default input device ID");
0
} else {
input_device
}
} else {
let mut output_device: AudioObjectID = 0;
let mut prop_size = std::mem::size_of::<AudioObjectID>() as u32;
let result = coreaudio::sys::AudioObjectGetPropertyData(
kAudioObjectSystemObject,
&AudioObjectPropertyAddress {
mSelector: kAudioHardwarePropertyDefaultOutputDevice,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
},
0,
std::ptr::null(),
&mut prop_size as *mut _,
&mut output_device as *mut _ as *mut _,
);
if result != 0 {
log::warn!("Failed to get default output device ID");
0
} else {
output_device
}
};
if device_id != 0 {
// Listen for format changes on the device
coreaudio::Error::from_os_status(AudioObjectAddPropertyListener(
device_id,
&AudioObjectPropertyAddress {
mSelector: coreaudio::sys::kAudioDevicePropertyStreamFormat,
mScope: if input {
coreaudio::sys::kAudioObjectPropertyScopeInput
} else {
coreaudio::sys::kAudioObjectPropertyScopeOutput
},
mElement: kAudioObjectPropertyElementMaster,
},
Some(property_listener_handler_shim),
&*callback as *const _ as *mut _,
))?;
}
device_id
};
Ok(Self {
rx,
callback,
input,
device_id,
})
}
}
@ -700,6 +768,7 @@ mod macos {
impl Drop for CoreAudioDefaultDeviceChangeListener {
fn drop(&mut self) {
unsafe {
// Remove the system-level property listener
AudioObjectRemovePropertyListener(
kAudioObjectSystemObject,
&AudioObjectPropertyAddress {
@ -714,6 +783,24 @@ mod macos {
Some(property_listener_handler_shim),
&*self.callback as *const _ as *mut _,
);
// Remove the device-specific property listener if we have a valid device ID
if self.device_id != 0 {
AudioObjectRemovePropertyListener(
self.device_id,
&AudioObjectPropertyAddress {
mSelector: coreaudio::sys::kAudioDevicePropertyStreamFormat,
mScope: if self.input {
coreaudio::sys::kAudioObjectPropertyScopeInput
} else {
coreaudio::sys::kAudioObjectPropertyScopeOutput
},
mElement: kAudioObjectPropertyElementMaster,
},
Some(property_listener_handler_shim),
&*self.callback as *const _ as *mut _,
);
}
}
}
}