diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dce15ba3..f89c70c0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `DeviceBusy` error variant for retriable device access errors (EBUSY, EAGAIN). - **ALSA**: `Debug` implementations for `Host`, `Device`, `Stream`, and internal types. - **ALSA**: Example demonstrating ALSA error suppression during enumeration. +- **ASIO**: Extension trait for ASIO devices, which allows opening the control panel. - **WASAPI**: Allow non-native sample rates to be used via as-necessary resampling in the WASAPI server process. ### Changed diff --git a/asio-sys/build.rs b/asio-sys/build.rs index 53b446e98..9dfb08f6b 100644 --- a/asio-sys/build.rs +++ b/asio-sys/build.rs @@ -72,6 +72,7 @@ fn main() { // Print out links to needed libraries println!("cargo:rustc-link-lib=dylib=ole32"); println!("cargo:rustc-link-lib=dylib=user32"); + println!("cargo:rustc-link-lib=dylib=Advapi32"); println!("cargo:rustc-link-search={}", out_dir.display()); println!("cargo:rustc-link-lib=static=asio"); println!("cargo:rustc-cfg=asio"); @@ -237,6 +238,7 @@ fn create_bindings(cpal_asio_dir: &PathBuf) { .allowlist_function("ASIOStart") .allowlist_function("ASIOStop") .allowlist_function("ASIODisposeBuffers") + .allowlist_function("ASIOControlPanel") .allowlist_function("ASIOExit") .allowlist_function("load_asio_driver") .allowlist_function("remove_current_driver") diff --git a/asio-sys/src/bindings/mod.rs b/asio-sys/src/bindings/mod.rs index 83b5d3ef5..fd07ffadb 100644 --- a/asio-sys/src/bindings/mod.rs +++ b/asio-sys/src/bindings/mod.rs @@ -788,6 +788,11 @@ impl Driver { let mut mcb = MESSAGE_CALLBACKS.lock().unwrap(); mcb.retain(|&(id, _)| id != rem_id); } + + /// Opens the ASIO driver's control panel window. + pub fn open_control_panel(&self) -> Result<(), AsioError> { + unsafe { asio_result!(ai::ASIOControlPanel()) } + } } impl DriverState { diff --git a/src/platform/asio.rs b/src/platform/asio.rs new file mode 100644 index 000000000..a8c4ef154 --- /dev/null +++ b/src/platform/asio.rs @@ -0,0 +1,67 @@ +//! Implementations for ASIO-specific device functionality. + +use crate::BackendSpecificError; +use crate::Device; + +/// Extension trait for ASIO-specific device functionality. +pub trait AsioDeviceExt { + /// Returns `true` if this device is an ASIO device. + fn is_asio_device(&self) -> bool; + + /// 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 may block 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. + fn asio_open_control_panel(&self) -> Result<(), BackendSpecificError>; +} + +#[cfg(all(target_os = "windows", feature = "asio"))] +impl AsioDeviceExt for Device { + fn is_asio_device(&self) -> bool { + matches!(self.as_inner(), crate::platform::DeviceInner::Asio(_)) + } + + fn asio_open_control_panel(&self) -> Result<(), BackendSpecificError> { + use crate::platform::DeviceInner; + + if let DeviceInner::Asio(ref asio_device) = self.as_inner() { + asio_device + .driver + .open_control_panel() + .map_err(|e| BackendSpecificError { + description: format!("Failed to open control panel: {:?}", e), + }) + } else { + Err(BackendSpecificError { + description: "Not an ASIO device".to_string(), + }) + } + } +} + +#[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()) + } +} + +#[cfg(not(all(target_os = "windows", feature = "asio")))] +fn not_available() -> BackendSpecificError { + BackendSpecificError { + description: "ASIO is not available on this platform".to_string(), + } +} diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 0f62026d7..da5f5f973 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -10,6 +10,8 @@ pub use self::platform_impl::*; #[cfg(feature = "custom")] pub use crate::host::custom::{Device as CustomDevice, Host as CustomHost, Stream as CustomStream}; +pub mod asio; + /// A macro to assist with implementing a platform's dynamically dispatched [`Host`] type. /// /// These dynamically dispatched types are necessary to allow for users to switch between hosts at