From 5c4b6268eb7d383fa81ae5c16e8c8ab5476173ae Mon Sep 17 00:00:00 2001 From: Nicolas Franco Gomez <80042895+nico-franco-gomez@users.noreply.github.com> Date: Mon, 27 Apr 2026 17:20:46 +0200 Subject: [PATCH 1/8] introduce new function to trait doc --- src/platform/mod.rs | 9 +++++++++ src/traits.rs | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/platform/mod.rs b/src/platform/mod.rs index b3f618007..29ec726b7 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -537,6 +537,15 @@ macro_rules! impl_platform_host { )* } } + + fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { + match self.0 { + $( + $(#[cfg($feat)])? + DeviceInner::$HostVariant(ref d) => d.get_channel_name(channel_index, input), + )* + } + } } impl crate::traits::HostTrait for Host { diff --git a/src/traits.rs b/src/traits.rs index 346707e10..53c6375a8 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -414,6 +414,30 @@ pub trait DeviceTrait { where D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static; + + /// Obtain the associated string name for a channel index. + /// + /// This method is only implemented for CoreAudio (macOS) and ASIO (Windows). All other + /// backends will return [`ErrorKind::UnsupportedOperation`]. + /// + /// # Parameters + /// + /// * `channel_index` - Channel index to query name for. + /// * `input` - Whether to query an input channel (true) or output channel (false). + /// + /// # Errors + /// + /// - [`ErrorKind::UnsupportedOperation`] if the backend does not implement channel name + /// queries. + /// - [`ErrorKind::InvalidInput`] if the channel index is out of range for the device, + /// or if the device does not support the requested direction (input/output). + /// - [`ErrorKind::Other`] for unclassifiable backend failures (e.g., the channel name could + /// not be retrieved from the device). + /// + /// [`ErrorKind::UnsupportedOperation`]: crate::ErrorKind::UnsupportedOperation + /// [`ErrorKind::InvalidInput`]: crate::ErrorKind::InvalidInput + /// [`ErrorKind::Other`]: crate::ErrorKind::Other + fn get_channel_name(&self, channel_index: u16, input: bool) -> Result; } /// A stream created from [`Device`](DeviceTrait), with methods to control playback. From 6a5c674450d30636181b939e4596065267a6c2c9 Mon Sep 17 00:00:00 2001 From: Nicolas Franco Gomez <80042895+nico-franco-gomez@users.noreply.github.com> Date: Mon, 27 Apr 2026 17:21:35 +0200 Subject: [PATCH 2/8] implement error for unsupported hosts --- src/host/aaudio/mod.rs | 4 ++++ src/host/alsa/mod.rs | 4 ++++ src/host/audioworklet/mod.rs | 4 ++++ src/host/custom/mod.rs | 4 ++++ src/host/jack/device.rs | 4 ++++ src/host/null/mod.rs | 4 ++++ src/host/pipewire/device.rs | 4 ++++ src/host/pulseaudio/mod.rs | 4 ++++ src/host/wasapi/device.rs | 4 ++++ src/host/webaudio/mod.rs | 4 ++++ 10 files changed, 40 insertions(+) diff --git a/src/host/aaudio/mod.rs b/src/host/aaudio/mod.rs index 5c1292e2b..dd4df99b8 100644 --- a/src/host/aaudio/mod.rs +++ b/src/host/aaudio/mod.rs @@ -679,6 +679,10 @@ impl DeviceTrait for Device { sample_format, ) } + + fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { + Err(Error::UnsupportedOperation) + } } impl StreamTrait for Stream { diff --git a/src/host/alsa/mod.rs b/src/host/alsa/mod.rs index 3661c2004..016a4a574 100644 --- a/src/host/alsa/mod.rs +++ b/src/host/alsa/mod.rs @@ -266,6 +266,10 @@ impl DeviceTrait for Device { ); Ok(stream) } + + fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { + Err(Error::UnsupportedOperation) + } } #[derive(Debug)] diff --git a/src/host/audioworklet/mod.rs b/src/host/audioworklet/mod.rs index 8e1b12058..04bf65f5f 100644 --- a/src/host/audioworklet/mod.rs +++ b/src/host/audioworklet/mod.rs @@ -350,6 +350,10 @@ impl DeviceTrait for Device { buffer_size_frames, }) } + + fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { + Err(Error::UnsupportedOperation) + } } impl StreamTrait for Stream { diff --git a/src/host/custom/mod.rs b/src/host/custom/mod.rs index 44ecdc322..86b691ba7 100644 --- a/src/host/custom/mod.rs +++ b/src/host/custom/mod.rs @@ -431,6 +431,10 @@ impl DeviceTrait for Device { timeout, ) } + + fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { + Err(Error::UnsupportedOperation) + } } impl StreamTrait for Stream { diff --git a/src/host/jack/device.rs b/src/host/jack/device.rs index d6b12e6ff..cc92b0844 100644 --- a/src/host/jack/device.rs +++ b/src/host/jack/device.rs @@ -324,6 +324,10 @@ impl DeviceTrait for Device { build() } } + + fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { + Err(Error::UnsupportedOperation) + } } impl PartialEq for Device { diff --git a/src/host/null/mod.rs b/src/host/null/mod.rs index a421ab60f..4f28ab3e5 100644 --- a/src/host/null/mod.rs +++ b/src/host/null/mod.rs @@ -103,6 +103,10 @@ impl DeviceTrait for Device { { unimplemented!() } + + fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { + Err(Error::UnsupportedOperation) + } } impl HostTrait for Host { diff --git a/src/host/pipewire/device.rs b/src/host/pipewire/device.rs index e5f9b6ad3..498a3f590 100644 --- a/src/host/pipewire/device.rs +++ b/src/host/pipewire/device.rs @@ -454,6 +454,10 @@ impl DeviceTrait for Device { )), } } + + fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { + Err(Error::UnsupportedOperation) + } } #[derive(Debug, Clone, Default)] diff --git a/src/host/pulseaudio/mod.rs b/src/host/pulseaudio/mod.rs index 882994f65..58f4e1676 100644 --- a/src/host/pulseaudio/mod.rs +++ b/src/host/pulseaudio/mod.rs @@ -468,6 +468,10 @@ impl DeviceTrait for Device { Ok(DeviceId(HostId::PulseAudio, id.to_string())) } + + fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { + Err(Error::UnsupportedOperation) + } } fn make_sample_spec(config: StreamConfig, format: protocol::SampleFormat) -> protocol::SampleSpec { diff --git a/src/host/wasapi/device.rs b/src/host/wasapi/device.rs index dd7ce6529..608062fe2 100644 --- a/src/host/wasapi/device.rs +++ b/src/host/wasapi/device.rs @@ -154,6 +154,10 @@ impl DeviceTrait for Device { error_callback, )) } + + fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { + Err(Error::UnsupportedOperation) + } } struct Endpoint { diff --git a/src/host/webaudio/mod.rs b/src/host/webaudio/mod.rs index b7aa648fc..bca512b6e 100644 --- a/src/host/webaudio/mod.rs +++ b/src/host/webaudio/mod.rs @@ -508,6 +508,10 @@ impl DeviceTrait for Device { is_started, }) } + + fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { + Err(Error::UnsupportedOperation) + } } impl Stream { From 97c4a3cd2d13dc00fc567d32e9ff7238264f6061 Mon Sep 17 00:00:00 2001 From: Nicolas Franco Gomez <80042895+nico-franco-gomez@users.noreply.github.com> Date: Mon, 27 Apr 2026 17:21:49 +0200 Subject: [PATCH 3/8] update example device --- examples/custom.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/custom.rs b/examples/custom.rs index 4787a0f9c..0fc7b4501 100644 --- a/examples/custom.rs +++ b/examples/custom.rs @@ -187,6 +187,13 @@ impl DeviceTrait for MyDevice { handle: Some(handle), }) } + + fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { + Ok(format!( + "{} {channel_index}", + if input { "Input" } else { "Output" } + )) + } } impl StreamTrait for MyStream { From 646152ce3fb960ec744b2fbd7bca447fee48d872 Mon Sep 17 00:00:00 2001 From: Nicolas Franco Gomez <80042895+nico-franco-gomez@users.noreply.github.com> Date: Mon, 27 Apr 2026 17:22:28 +0200 Subject: [PATCH 4/8] feature(coreaudio): implement channel names # Conflicts: # src/host/coreaudio/macos/device.rs # Conflicts: # src/host/coreaudio/macos/device.rs a a a --- src/host/coreaudio/macos/device.rs | 78 ++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 9 deletions(-) diff --git a/src/host/coreaudio/macos/device.rs b/src/host/coreaudio/macos/device.rs index 21187d2a1..f8baff236 100644 --- a/src/host/coreaudio/macos/device.rs +++ b/src/host/coreaudio/macos/device.rs @@ -25,18 +25,17 @@ use objc2_audio_toolbox::{ kAudioOutputUnitProperty_CurrentDevice, kAudioOutputUnitProperty_EnableIO, kAudioUnitProperty_StreamFormat, }; -use objc2_core_audio::kAudioDevicePropertyDeviceUID; -use objc2_core_audio::kAudioObjectPropertyElementMain; use objc2_core_audio::{ kAudioAggregateDeviceClassID, kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyBufferFrameSize, kAudioDevicePropertyBufferFrameSizeRange, - kAudioDevicePropertyLatency, kAudioDevicePropertyNominalSampleRate, - kAudioDevicePropertySafetyOffset, kAudioDevicePropertyStreamConfiguration, - kAudioDevicePropertyStreamFormat, kAudioObjectPropertyClass, kAudioObjectPropertyElementMaster, - kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput, - kAudioObjectPropertyScopeOutput, AudioClassID, AudioDeviceID, AudioObjectGetPropertyData, - AudioObjectGetPropertyDataSize, AudioObjectID, AudioObjectPropertyAddress, - AudioObjectPropertyScope, AudioObjectSetPropertyData, + kAudioDevicePropertyDeviceUID, kAudioDevicePropertyLatency, + kAudioDevicePropertyNominalSampleRate, kAudioDevicePropertySafetyOffset, + kAudioDevicePropertyStreamConfiguration, kAudioDevicePropertyStreamFormat, + kAudioObjectPropertyClass, kAudioObjectPropertyElementMain, kAudioObjectPropertyElementMaster, + kAudioObjectPropertyElementName, kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyScopeInput, kAudioObjectPropertyScopeOutput, AudioClassID, AudioDeviceID, + AudioObjectGetPropertyData, AudioObjectGetPropertyDataSize, AudioObjectID, + AudioObjectPropertyAddress, AudioObjectPropertyScope, AudioObjectSetPropertyData, }; use objc2_core_audio_types::{ AudioBuffer, AudioBufferList, AudioStreamBasicDescription, AudioValueRange, @@ -344,6 +343,10 @@ impl DeviceTrait for Device { timeout, ) } + + fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { + Device::get_channel_name(self, channel_index, input) + } } #[derive(Clone, Eq, Hash, PartialEq)] @@ -703,6 +706,24 @@ impl Device { .map(|mut configs| configs.next().is_some()) .unwrap_or(false) } + + fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { + if input && !self.supports_input() { + return Err(Error::with_message( + ErrorKind::InvalidInput, + "Device does not support input", + )); + } + + if !input && !self.supports_output() { + return Err(Error::with_message( + ErrorKind::InvalidInput, + "Device does not support output", + )); + } + + unsafe { get_channel_name_for_device(self.audio_device_id, channel_index, input) } + } } impl fmt::Debug for Device { @@ -1029,3 +1050,42 @@ pub(crate) fn get_device_buffer_frame_size( )?; Ok(frames as usize) } + +unsafe fn get_channel_name_for_device( + device_id: AudioDeviceID, + channel_index: u16, + input: bool, +) -> Result { + let mut channel_name: *mut CFString = std::ptr::null_mut(); + let mut data_size = size_of::<*mut CFString>() as u32; + + let property_address = AudioObjectPropertyAddress { + mSelector: kAudioObjectPropertyElementName, + mScope: if input { + kAudioObjectPropertyScopeInput + } else { + kAudioObjectPropertyScopeOutput + }, + // Channels numbers start at 1 here + mElement: channel_index as u32 + 1, + }; + + let status = AudioObjectGetPropertyData( + device_id, + NonNull::from(&property_address), + 0, + null(), + NonNull::from(&mut data_size), + NonNull::from(&mut channel_name).cast(), + ); + check_os_status(status)?; + + if !channel_name.is_null() { + Ok(CFString::wrap_under_create_rule(channel_name).to_string()) + } else { + Err(Error::with_message( + ErrorKind::Other, + "channel name is null", + )) + } +} From 482bf398397d79a38a22084f6e3260529d1afc85 Mon Sep 17 00:00:00 2001 From: Nicolas Franco Gomez <80042895+nico-franco-gomez@users.noreply.github.com> Date: Mon, 27 Apr 2026 17:24:07 +0200 Subject: [PATCH 5/8] feature(asio-sys): make channel name available --- asio-sys/src/bindings/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/asio-sys/src/bindings/mod.rs b/asio-sys/src/bindings/mod.rs index 360546754..d5ebd5d0b 100644 --- a/asio-sys/src/bindings/mod.rs +++ b/asio-sys/src/bindings/mod.rs @@ -926,6 +926,18 @@ impl Driver { let mut dcb = DRIVER_EVENT_CALLBACKS.lock().unwrap(); dcb.retain(|&(id, _)| id != rem_id); } + + /// Returns the name of the channel at the given index. + /// + /// `channel` is a 0-based channel index. `is_input` selects the input (`true`) or output + /// (`false`) direction. + /// + /// The driver must already be loaded (i.e. this `Driver` instance must be alive). + pub fn channel_name(&self, channel: i32, is_input: bool) -> Result { + let _guard = self.inner.lock_state(); + let info = asio_channel_info(channel, is_input)?; + Ok(driver_name_to_utf8(&info.name).into_owned()) + } } impl DriverState { From a33caf90bee3d70d9e3495757032d167c406d60a Mon Sep 17 00:00:00 2001 From: Nicolas Franco Gomez <80042895+nico-franco-gomez@users.noreply.github.com> Date: Mon, 27 Apr 2026 17:24:29 +0200 Subject: [PATCH 6/8] feature(asio): implement channel name with cache on enumeration --- src/host/asio/device.rs | 31 +++++++++++++++++++++++++++++++ src/host/asio/mod.rs | 4 ++++ 2 files changed, 35 insertions(+) diff --git a/src/host/asio/device.rs b/src/host/asio/device.rs index 26fc0c658..5c030d6e7 100644 --- a/src/host/asio/device.rs +++ b/src/host/asio/device.rs @@ -25,6 +25,8 @@ pub struct Device { input_sample_format: Option, output_sample_format: Option, supported_sample_rates: Vec, + input_channel_names: Vec, + output_channel_names: Vec, // Input and/or Output stream. // A driver can only have one of each. @@ -140,6 +142,26 @@ impl Device { } configs } + + pub fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { + let names = if input { + &self.input_channel_names + } else { + &self.output_channel_names + }; + + names.get(channel_index as usize).cloned().ok_or_else(|| { + Error::with_message( + ErrorKind::InvalidInput, + format!( + "channel index {} is out of range (device has {} {} channels)", + channel_index, + names.len(), + if input { "input" } else { "output" }, + ), + ) + }) + } } impl Devices { @@ -197,6 +219,13 @@ impl Iterator for Devices { .filter(|&r| driver.can_sample_rate(r.into()).unwrap_or(false)) .collect(); + let input_channel_names: Vec = (0..channels.ins) + .map(|ch| driver.channel_name(ch, true).unwrap_or_default()) + .collect(); + let output_channel_names: Vec = (0..channels.outs) + .map(|ch| driver.channel_name(ch, false).unwrap_or_default()) + .collect(); + self.current_driver = Some(driver); let asio_streams = Arc::new(Mutex::new(sys::AsioStreams { @@ -214,6 +243,8 @@ impl Iterator for Devices { input_sample_format, output_sample_format, supported_sample_rates, + input_channel_names, + output_channel_names, asio_streams, // Initialize with sentinel value so it never matches global flag state (0 or 1). current_callback_flag: Arc::new(AtomicU32::new(u32::MAX)), diff --git a/src/host/asio/mod.rs b/src/host/asio/mod.rs index c529ecee3..67591e2d7 100644 --- a/src/host/asio/mod.rs +++ b/src/host/asio/mod.rs @@ -143,6 +143,10 @@ impl DeviceTrait for Device { timeout, ) } + + fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { + Device::get_channel_name(self, channel_index, input) + } } impl StreamTrait for Stream { From 08a9059f97c6cc34650098569c0d2104ac50429d Mon Sep 17 00:00:00 2001 From: Nicolas Franco Gomez <80042895+nico-franco-gomez@users.noreply.github.com> Date: Thu, 25 Jun 2026 13:26:33 +0200 Subject: [PATCH 7/8] correct errors for other hosts --- src/host/aaudio/mod.rs | 2 +- src/host/alsa/mod.rs | 2 +- src/host/audioworklet/mod.rs | 2 +- src/host/custom/mod.rs | 2 +- src/host/jack/device.rs | 2 +- src/host/null/mod.rs | 2 +- src/host/pipewire/device.rs | 2 +- src/host/pulseaudio/mod.rs | 2 +- src/host/wasapi/device.rs | 2 +- src/host/webaudio/mod.rs | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/host/aaudio/mod.rs b/src/host/aaudio/mod.rs index ec6e85fc1..4b41d3dfa 100644 --- a/src/host/aaudio/mod.rs +++ b/src/host/aaudio/mod.rs @@ -738,7 +738,7 @@ impl DeviceTrait for Device { } fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { - Err(Error::UnsupportedOperation) + Err(Error::new(ErrorKind::UnsupportedOperation)) } } diff --git a/src/host/alsa/mod.rs b/src/host/alsa/mod.rs index ae4c61024..cf4b4d72c 100644 --- a/src/host/alsa/mod.rs +++ b/src/host/alsa/mod.rs @@ -306,7 +306,7 @@ impl DeviceTrait for Device { } fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { - Err(Error::UnsupportedOperation) + Err(Error::new(ErrorKind::UnsupportedOperation)) } } diff --git a/src/host/audioworklet/mod.rs b/src/host/audioworklet/mod.rs index d090dbaf6..16b4163a8 100644 --- a/src/host/audioworklet/mod.rs +++ b/src/host/audioworklet/mod.rs @@ -445,7 +445,7 @@ impl DeviceTrait for Device { } fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { - Err(Error::UnsupportedOperation) + Err(Error::new(ErrorKind::UnsupportedOperation)) } } diff --git a/src/host/custom/mod.rs b/src/host/custom/mod.rs index b5bab0d1b..370ef5f4f 100644 --- a/src/host/custom/mod.rs +++ b/src/host/custom/mod.rs @@ -453,7 +453,7 @@ impl DeviceTrait for Device { } fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { - Err(Error::UnsupportedOperation) + Err(Error::new(ErrorKind::UnsupportedOperation)) } } diff --git a/src/host/jack/device.rs b/src/host/jack/device.rs index 0f128be49..ea0763eed 100644 --- a/src/host/jack/device.rs +++ b/src/host/jack/device.rs @@ -349,7 +349,7 @@ impl DeviceTrait for Device { } fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { - Err(Error::UnsupportedOperation) + Err(Error::new(ErrorKind::UnsupportedOperation)) } } diff --git a/src/host/null/mod.rs b/src/host/null/mod.rs index da48d2d43..56ea7378c 100644 --- a/src/host/null/mod.rs +++ b/src/host/null/mod.rs @@ -106,7 +106,7 @@ impl DeviceTrait for Device { } fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { - Err(Error::UnsupportedOperation) + Err(Error::new(ErrorKind::UnsupportedOperation)) } } diff --git a/src/host/pipewire/device.rs b/src/host/pipewire/device.rs index 1b113df0f..d0d4b8e5f 100644 --- a/src/host/pipewire/device.rs +++ b/src/host/pipewire/device.rs @@ -694,7 +694,7 @@ impl DeviceTrait for Device { } fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { - Err(Error::UnsupportedOperation) + Err(Error::new(ErrorKind::UnsupportedOperation)) } } diff --git a/src/host/pulseaudio/mod.rs b/src/host/pulseaudio/mod.rs index 80b70f579..6f59b5ac7 100644 --- a/src/host/pulseaudio/mod.rs +++ b/src/host/pulseaudio/mod.rs @@ -526,7 +526,7 @@ impl DeviceTrait for Device { } fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { - Err(Error::UnsupportedOperation) + Err(Error::new(ErrorKind::UnsupportedOperation)) } } diff --git a/src/host/wasapi/device.rs b/src/host/wasapi/device.rs index 83ceb8f78..9c90439bb 100644 --- a/src/host/wasapi/device.rs +++ b/src/host/wasapi/device.rs @@ -158,7 +158,7 @@ impl DeviceTrait for Device { } fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { - Err(Error::UnsupportedOperation) + Err(Error::new(ErrorKind::UnsupportedOperation)) } } diff --git a/src/host/webaudio/mod.rs b/src/host/webaudio/mod.rs index 86c0e2a76..c0580ba90 100644 --- a/src/host/webaudio/mod.rs +++ b/src/host/webaudio/mod.rs @@ -578,7 +578,7 @@ impl DeviceTrait for Device { } fn get_channel_name(&self, channel_index: u16, input: bool) -> Result { - Err(Error::UnsupportedOperation) + Err(Error::new(ErrorKind::UnsupportedOperation)) } } From 9c5c5f57a6c996e5dd1f9beeb4b56f5297fe93d4 Mon Sep 17 00:00:00 2001 From: Nicolas Franco Gomez <80042895+nico-franco-gomez@users.noreply.github.com> Date: Thu, 25 Jun 2026 13:29:57 +0200 Subject: [PATCH 8/8] correct usage of deprecated functions --- src/host/coreaudio/macos/device.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/host/coreaudio/macos/device.rs b/src/host/coreaudio/macos/device.rs index 264752d3b..0e3ea76d3 100644 --- a/src/host/coreaudio/macos/device.rs +++ b/src/host/coreaudio/macos/device.rs @@ -28,11 +28,11 @@ use objc2_core_audio::{ kAudioDevicePropertyDeviceUID, kAudioDevicePropertyLatency, kAudioDevicePropertyNominalSampleRate, kAudioDevicePropertySafetyOffset, kAudioDevicePropertyStreamConfiguration, kAudioDevicePropertyStreamFormat, - kAudioObjectPropertyClass, kAudioObjectPropertyElementMain, kAudioObjectPropertyElementMaster, - kAudioObjectPropertyElementName, kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyScopeInput, kAudioObjectPropertyScopeOutput, AudioClassID, AudioDeviceID, - AudioObjectGetPropertyData, AudioObjectGetPropertyDataSize, AudioObjectID, - AudioObjectPropertyAddress, AudioObjectPropertyScope, AudioObjectSetPropertyData, + kAudioObjectPropertyClass, kAudioObjectPropertyElementMain, kAudioObjectPropertyElementName, + kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput, + kAudioObjectPropertyScopeOutput, AudioClassID, AudioDeviceID, AudioObjectGetPropertyData, + AudioObjectGetPropertyDataSize, AudioObjectID, AudioObjectPropertyAddress, + AudioObjectPropertyScope, AudioObjectSetPropertyData, }; use objc2_core_audio_types::{ AudioBuffer, AudioBufferList, AudioStreamBasicDescription, AudioValueRange, @@ -1138,7 +1138,7 @@ unsafe fn get_channel_name_for_device( check_os_status(status)?; if !channel_name.is_null() { - Ok(CFString::wrap_under_create_rule(channel_name).to_string()) + Ok(CFRetained::from_raw(NonNull::new(channel_name).unwrap()).to_string()) } else { Err(Error::with_message( ErrorKind::Other,