-
Notifications
You must be signed in to change notification settings - Fork 442
feat: stable Device::id() #1014
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great! Your proposal literally crossed paths as I was working on something also. No matter, all the better I could provide some review comments with actual code suggestions.
Please also add a changelog, it's worth it 😄
Hello @roderickvd ! I have implemented most of the changes requested and renamed the enum variants to the suggested names. For I have also added I will work on changing the macOS CoreAudio type to The changelog has also been updated 😄. |
Cool stuff let me know when you're ready for me to take another look. |
I have finished the changes and now One thing that I noticed was that there was already a The code I'm currently using is basically 90% the same as the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some ideas and one point about the macOS implementation.
I have updated the I have also made Currently, I think it is fine as all three of those APIs seem to only return the default device, however I think if they were expanded to be able to select audio devices, it would be better to leave them returning As for the syncing issue, I'm unsure about what you mean, would you care to elaborate? |
From your PR description, which I edited to show the progress made since:
Is that still necessary? |
Thanks for the update @roderickvd ! I'll try getting the code updated soon for another review.
Currently I believe it is still necessary to convert it to a This is primarily for if you are using the |
But it already does I don't have a Windows machine myself, so am trying to understand. |
So right now I'm having it return the The snippet about That code is more about external access using the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for sticking through 👍 this is a pretty important feature so let's get it right!
use super::property_listener::AudioObjectPropertyListener; | ||
use coreaudio::audio_unit::macos_helpers::get_device_name; | ||
|
||
type CFStringRef = *mut std::os::raw::c_void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we remove this in favor of plain CFString
and be less unsafe?
fn id(&self) -> Result<DeviceId, DeviceIdError> {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyDeviceUID,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMain,
};
// CFString is retained by the audio object, use wrap_under_get_rule
let mut uid: *const CFString = std::ptr::null();
let data_size = size_of::<*const CFString>() as u32;
// SAFETY: AudioObjectGetPropertyData is documented to write a CFString pointer
// for kAudioDevicePropertyDeviceUID. We check the status code before use.
let status = unsafe {
AudioObjectGetPropertyData(
self.audio_device_id,
NonNull::from(&property_address),
0,
null(),
NonNull::from(&data_size),
NonNull::from(&mut uid).cast(),
)
};
check_os_status(status)?;
if uid.is_null() {
return Err(DeviceIdError::BackendSpecific {
err: BackendSpecificError {
description: "Device UID is null".to_string(),
},
});
}
// SAFETY: We verified uid is non-null and the status was successful
let uid_string = unsafe { CFString::wrap_under_get_rule(uid).to_string() };
Ok(DeviceId::CoreAudio(uid_string))
}
fn id(&self) -> Result<DeviceId, DeviceIdError> { | ||
unsafe { | ||
match self.device.GetId() { | ||
Ok(pwstr) => match pwstr.to_string() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of silently discarding, we could:
fn id(&self) -> Result<DeviceId, DeviceIdError> {
// SAFETY: Calling GetId on a valid IMMDevice is safe
unsafe {
let pwstr = self.device.GetId().map_err(|e| DeviceIdError::BackendSpecific {
err: e.into(),
})?;
let id_str = pwstr.to_string().map_err(|e| DeviceIdError::BackendSpecific {
err: BackendSpecificError { description: format!("Failed to convert device ID to string: {}", e) },
})?;
Ok(DeviceId::WASAPI(id_str))
}
}
} | ||
|
||
fn id(&self) -> Result<DeviceId, DeviceIdError> { | ||
Ok(DeviceId::IOS("default".to_string())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"default" differs from "Default Device" in name
. Maybe use shared const?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm slightly unsure about this change, as in most conventions an id would not have spaces inside of it, so I feel like "default"
does a better job of this. DeviceId
s and name
s also do not intermix so I don't believe this will be much of an issue either.
Of course if the general consensus is that "Default Device"
is better than "default"
, I would be happy to change it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good rationale, let’s keep it like it this then.
How do you feel this’ll be for ALSA? Spaces in device names (e.g. for USB devices) would not be impossible.
This PR adds a
Device::id()
command that returns the OS native device id on supported operating systems.Currently, only macOS and Windows (WASAPI) are supported.Function
By returning a
Device::id()
, it will be easier to ensure that the same device gets connected on application startup. This also allows for greater extension capability for other libraries to extend uponcpal
function.Architecture
The OS specific device ids are all stored in a
DeviceId
enum, that allows for support for the various data types that different operating systems and APIs use to store device ids (e.g.u32
for macOS andString
for WASAPI.To handle any errors that might be outputted in when running
Device::id()
, there is also a newDeviceIdError
enumCalling
Device::id()
will return aResult<DeviceId, DeviceIdError>
. If the OS is supported and the device id is obtained, it will return aDeviceId
. If the OS is unsupported, aDeviceIdError::UnsupportedOS
error will be returned.Current Status
Currently, support for Windows WASAPIString
Ids has been added, along with support for macOSu32
Ids. I am working to add support forALSA
and maybepulseaudio
(once that gets merged).For other APIs, I do not have the hardware to test or program for them,
so they currently return aDeviceIdError::UnsupportedOS
error.Testing
Device::id()
for macOS should return thestandarddevice uid that can be used anywhere.u32
For Windows, to convert the Rust
String
type to aPWSTR
, the following code can be used