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:
parent
c04c5812b6
commit
ee4b6a8db4
1 changed files with 94 additions and 7 deletions
|
@ -163,9 +163,8 @@ impl AudioStack {
|
||||||
sample_rate: u32,
|
sample_rate: u32,
|
||||||
num_channels: u32,
|
num_channels: u32,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut default_change_listener = DeviceChangeListener::new(false)?;
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
let mut device_change_listener = DeviceChangeListener::new(false)?;
|
||||||
let (output_device, output_config) = default_device(false)?;
|
let (output_device, output_config) = default_device(false)?;
|
||||||
let (end_on_drop_tx, end_on_drop_rx) = std::sync::mpsc::channel::<()>();
|
let (end_on_drop_tx, end_on_drop_rx) = std::sync::mpsc::channel::<()>();
|
||||||
let mixer = mixer.clone();
|
let mixer = mixer.clone();
|
||||||
|
@ -225,7 +224,7 @@ impl AudioStack {
|
||||||
end_on_drop_rx.recv().ok();
|
end_on_drop_rx.recv().ok();
|
||||||
});
|
});
|
||||||
|
|
||||||
default_change_listener.next().await;
|
device_change_listener.next().await;
|
||||||
drop(end_on_drop_tx)
|
drop(end_on_drop_tx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,8 +235,8 @@ impl AudioStack {
|
||||||
sample_rate: u32,
|
sample_rate: u32,
|
||||||
num_channels: u32,
|
num_channels: u32,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut default_change_listener = DeviceChangeListener::new(true)?;
|
|
||||||
loop {
|
loop {
|
||||||
|
let mut device_change_listener = DeviceChangeListener::new(true)?;
|
||||||
let (device, config) = default_device(true)?;
|
let (device, config) = default_device(true)?;
|
||||||
let (end_on_drop_tx, end_on_drop_rx) = std::sync::mpsc::channel::<()>();
|
let (end_on_drop_tx, end_on_drop_rx) = std::sync::mpsc::channel::<()>();
|
||||||
let apm = apm.clone();
|
let apm = apm.clone();
|
||||||
|
@ -310,7 +309,7 @@ impl AudioStack {
|
||||||
.log_err();
|
.log_err();
|
||||||
});
|
});
|
||||||
|
|
||||||
default_change_listener.next().await;
|
device_change_listener.next().await;
|
||||||
drop(end_on_drop_tx)
|
drop(end_on_drop_tx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -646,6 +645,7 @@ mod macos {
|
||||||
rx: UnboundedReceiver<()>,
|
rx: UnboundedReceiver<()>,
|
||||||
callback: Box<PropertyListenerCallbackWrapper>,
|
callback: Box<PropertyListenerCallbackWrapper>,
|
||||||
input: bool,
|
input: bool,
|
||||||
|
device_id: AudioObjectID, // Store the device ID to properly remove listeners
|
||||||
}
|
}
|
||||||
|
|
||||||
trait _AssertSend: Send {}
|
trait _AssertSend: Send {}
|
||||||
|
@ -672,7 +672,9 @@ mod macos {
|
||||||
tx.unbounded_send(()).ok();
|
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(
|
coreaudio::Error::from_os_status(AudioObjectAddPropertyListener(
|
||||||
kAudioObjectSystemObject,
|
kAudioObjectSystemObject,
|
||||||
&AudioObjectPropertyAddress {
|
&AudioObjectPropertyAddress {
|
||||||
|
@ -687,12 +689,78 @@ mod macos {
|
||||||
Some(property_listener_handler_shim),
|
Some(property_listener_handler_shim),
|
||||||
&*callback as *const _ as *mut _,
|
&*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 {
|
Ok(Self {
|
||||||
rx,
|
rx,
|
||||||
callback,
|
callback,
|
||||||
input,
|
input,
|
||||||
|
device_id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -700,6 +768,7 @@ mod macos {
|
||||||
impl Drop for CoreAudioDefaultDeviceChangeListener {
|
impl Drop for CoreAudioDefaultDeviceChangeListener {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
// Remove the system-level property listener
|
||||||
AudioObjectRemovePropertyListener(
|
AudioObjectRemovePropertyListener(
|
||||||
kAudioObjectSystemObject,
|
kAudioObjectSystemObject,
|
||||||
&AudioObjectPropertyAddress {
|
&AudioObjectPropertyAddress {
|
||||||
|
@ -714,6 +783,24 @@ mod macos {
|
||||||
Some(property_listener_handler_shim),
|
Some(property_listener_handler_shim),
|
||||||
&*self.callback as *const _ as *mut _,
|
&*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 _,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue