Skip to content

Conversation

@xephyris
Copy link
Contributor

@xephyris xephyris commented Sep 23, 2025

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 upon cpal 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 and String for WASAPI.

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum DeviceId {
    CoreAudio(String),
    WASAPI(String),
    // ..
}

To handle any errors that might be outputted in when running Device::id(), there is also a new DeviceIdError enum

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum DeviceIdError {
    /// See the [`BackendSpecificError`] docs for more information about this error variant.
    BackendSpecific { err: BackendSpecificError },
    UnsupportedOS,
    ParseError,
}

Calling Device::id() will return a Result<DeviceId, DeviceIdError>. If the OS is supported and the device id is obtained, it will return a DeviceId. If the OS is unsupported, a DeviceIdError::UnsupportedOS error will be returned.

Current Status

Currently, support for Windows WASAPI String Ids has been added, along with support for macOS u32 Ids. I am working to add support for ALSA and maybe pulseaudio (once that gets merged).

For other APIs, I do not have the hardware to test or program for them, so they currently return a DeviceIdError::UnsupportedOS error.

Testing

Device::id() for macOS should return the standard u32 device uid that can be used anywhere.
For Windows, to convert the Rust String type to a PWSTR, the following code can be used

// device_id is the id returned by cpal
 let mut id = format!("{}\0", device_id).encode_utf16().collect::<Vec<u16>>(); 
 let pwstr = PWSTR(id.as_mut_ptr());

Copy link
Member

@roderickvd roderickvd left a 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 😄

@xephyris
Copy link
Contributor Author

Hello @roderickvd !

I have implemented most of the changes requested and renamed the enum variants to the suggested names.

For jack and aaudio, I just copied the code that you provided, and because I cannot test it, there may or may not be some errors.

I have also added alsa support which currently just returns the same content as name, because I was unable to find any other specific identifiers that could work better.

I will work on changing the macOS CoreAudio type to kAudioDevicePropertyDeviceUID in the coming days.

The changelog has also been updated 😄.

@roderickvd
Copy link
Member

Cool stuff let me know when you're ready for me to take another look.

@xephyris
Copy link
Contributor Author

I have finished the changes and now DeviceId::CoreAudio has a String type instead of u32.

One thing that I noticed was that there was already a uid() function in loopback.rs for Device, however I wasn't sure if it should be moved into device.rs or not as it seems to rely on some types inside of loopback.rs.

The code I'm currently using is basically 90% the same as the uid() function, just with a different return type. If needed, I can attempt moving the uid() function over into device.rs

@xephyris xephyris requested a review from roderickvd September 26, 2025 02:32
Copy link
Member

@roderickvd roderickvd left a 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.

Copy link
Member

@roderickvd roderickvd left a 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!

@xephyris xephyris requested a review from roderickvd October 18, 2025 14:17
@roderickvd
Copy link
Member

Thanks, I’ll review later as I’m away for a few days.

@xephyris
Copy link
Contributor Author

Hello @roderickvd,

Just wondering if you were available to review the updates, as I was hoping to get the device_ids integrated as soon as possible.

If it comes at an inconvenient time, I'm happy to wait.

Copy link
Member

@roderickvd roderickvd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the ping and sorry for the wait. Yes lots of work done, thanks for that. Here's a few hopefully small ones.

"emscripten" => Ok(DeviceId::Emscripten(data.to_string())),
"ios" => Ok(DeviceId::IOS(data.to_string())),
"null" => Ok(DeviceId::Null),
&_ => todo!("implement DeviceId::FromStr for {platform}"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we discussed this earlier but I find myself doubting here.
Should we panic here or return DeviceIdError::UnsupportedPlatform?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a DeviceIdError::UnsuportedPlatform would be better in this case as it would leave more flexibility for the end user, if they want their application to panic if the API is unsupported that option is allowed, but we also give them the option to accept it as an error instead of crashing their app.

@roderickvd
Copy link
Member

Thanks for all the work and updating with the custom host support. I wonder if we can support custom hosts better in the future, but that can be added later if necessary.

Eager to get this out with v0.17 and see the community response!

@roderickvd roderickvd merged commit 11dea8d into RustAudio:master Nov 14, 2025
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants