Skip to content

Conversation

@Sin-tel
Copy link
Contributor

@Sin-tel Sin-tel commented Dec 13, 2025

Just putting this here for now so people know it is at least possible.

Will have to think about a proper API if we want to include this. (extension traits?)
I would also be fine having this only exposed in asio-sys for now, but you'd need to expose driver internals if you want to use it from cpal.

Also, I had to modify the build script to get rid of linker errors, not sure what's up with that.

@roderickvd
Copy link
Member

Hey! I think the asio-sys part is worth having regardless of the cpal API discussion. While I believe the ASIO control panels are blocking modals, it seems that PortAudio and JUCE also expose them.

For the build script changes: the linker errors make sense - ASIOControlPanel() uses Windows registry APIs (Advapi32) and UI functions (User32). Just noting that you have user32 on line 66 and User32 on line 68 - they're the same lib on Windows (case-insensitive) so it works, just redundant.

Re: the cpal API, I agree we should use extension traits rather than exposing this through as_inner() downcasting. Here's what I'm thinking:

// In src/platform/asio.rs or similar
#[cfg(all(target_os = "windows", feature = "asio"))]
pub trait AsioDeviceExt {
    fn asio_open_control_panel(&self) -> Result<(), BackendSpecificError>;
}

impl AsioDeviceExt for Device {
    fn asio_open_control_panel(&self) -> Result<(), BackendSpecificError> {
        if let DeviceInner::Asio(ref d) = self.as_inner() {
            d.open_control_panel()
        } else {
            // or maybe introduce a new error variant
            Err(BackendSpecificError {
                description: "Not an ASIO device".to_string(),
            })
        }
    }
}

Usage would be like:

use cpal::platform::asio::AsioDeviceExt;

let device = host.default_output_device()?;
device.asio_open_control_panel()?;

This keeps the main Device trait platform-agnostic while allowing opt-in access to host-specific features. We could do similar for WASAPI exclusive mode, ALSA period counts, etc.

@roderickvd
Copy link
Member

Had the above extension traits in mind for v0.18 by the way, not v0.17.

@Sin-tel
Copy link
Contributor Author

Sin-tel commented Dec 18, 2025

Cool, trying out the extension trait now. It seems pretty nice! How I did it right now still has the problem with docs not generating though.

@Sin-tel Sin-tel marked this pull request as ready for review December 21, 2025 15:02
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 considerations inline. Let's pay particular attention to getting the extension traits right. When we start adding them, it'd be highly desirable to keep them consistent and stable for the other hosts and time to come.

Also don't forget about a changelog entry.

Copy link
Member

Choose a reason for hiding this comment

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

What do you think about making this an example in asio-sys/examples instead?

use crate::BackendSpecificError;
use crate::Device;

pub trait AsioDeviceExt {
Copy link
Member

Choose a reason for hiding this comment

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

I'd like the public API to have good documentation. Proposal:

/// Extension trait for ASIO-specific device functionality.

use crate::Device;

pub trait AsioDeviceExt {
fn asio_open_control_panel(&self) -> Result<(), BackendSpecificError>;
Copy link
Member

Choose a reason for hiding this comment

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

/// Opens the ASIO driver's control panel window.
///
/// This provides access to device-specific settings like buffer size,
/// sample rate, input/output routing, and hardware-specific features.
///
/// # Blocking Behavior
///
/// **WARNING**: This call blocks until the user closes the control panel.
/// Consider spawning a thread to avoid blocking the main thread.
///
/// # Errors
///
/// Returns an error if this device is not an ASIO device.

#[cfg(feature = "custom")]
pub use crate::host::custom::{Device as CustomDevice, Host as CustomHost, Stream as CustomStream};

#[cfg(all(target_os = "windows", feature = "asio"))]
Copy link
Member

Choose a reason for hiding this comment

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

For cross-platform compatibility in downstream code, the configuration guard should be moved from here into platform/asio.rs like so:

#[cfg(all(target_os = "windows", feature = "asio"))]
impl AsioDeviceExt for Device {
    // ...
}

#[cfg(not(all(target_os = "windows", feature = "asio")))]
impl AsioDeviceExt for Device {
    fn is_asio_device(&self) -> bool {
        false
    }
    
    fn asio_open_control_panel(&self) -> Result<(), BackendSpecificError> {
        Err(not_available())
    }

    fn asio_reset_device(&self) -> Result<(), BackendSpecificError> {
        Err(not_available())
    }
}

fn not_available() -> BackendSpecificError {
    BackendSpecificError {
        description: "ASIO is not available on this platform".to_string(),
    }
}

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.

2 participants