diff --git a/asio-sys/build.rs b/asio-sys/build.rs index 00e49dbc5..41c1763dc 100644 --- a/asio-sys/build.rs +++ b/asio-sys/build.rs @@ -64,6 +64,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"); @@ -218,6 +219,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 70cfc3174..fe8b8390f 100644 --- a/asio-sys/src/bindings/mod.rs +++ b/asio-sys/src/bindings/mod.rs @@ -777,6 +777,10 @@ impl Driver { let mut mcb = MESSAGE_CALLBACKS.lock().unwrap(); mcb.retain(|&(id, _)| id != rem_id); } + + pub fn open_control_panel(&self) -> Result<(), AsioError> { + unsafe { asio_result!(ai::ASIOControlPanel()) } + } } impl DriverState { diff --git a/examples/asio_panel.rs b/examples/asio_panel.rs new file mode 100644 index 000000000..08ce7065d --- /dev/null +++ b/examples/asio_panel.rs @@ -0,0 +1,45 @@ +#[cfg(all(windows, feature = "asio"))] +fn main() -> anyhow::Result<()> { + use cpal::platform::asio::AsioDeviceExt; + use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; + use cpal::HostId; + + let host = cpal::host_from_id(HostId::Asio)?; + + let device = host + .default_output_device() + .ok_or_else(|| anyhow::anyhow!("No ASIO device found"))?; + + println!( + "Opening control panel for: {}", + device.description()?.name() + ); + + let config = device.default_output_config()?; + + let err_fn = move |err| println!("Stream Error: {:?}", err); + + let stream = device.build_output_stream( + &config.config(), + move |_data: &mut [i32], _: &cpal::OutputCallbackInfo| { /* play silence */ }, + err_fn, + None, + )?; + + stream.play()?; + + // This is a blocking call on some devices, so spawn it in its own thread. + std::thread::spawn(move || { + if let Err(e) = device.asio_open_control_panel() { + eprintln!("Could not open panel: {:?}", e); + } + }); + + // Keep the thread alive so the window doesn't close immediately + std::thread::sleep(std::time::Duration::from_secs(5)); + + Ok(()) +} + +#[cfg(not(all(windows, feature = "asio")))] +fn main() {} diff --git a/src/platform/asio.rs b/src/platform/asio.rs new file mode 100644 index 000000000..be7ce0f77 --- /dev/null +++ b/src/platform/asio.rs @@ -0,0 +1,24 @@ +use crate::platform::DeviceInner; +use crate::BackendSpecificError; +use crate::Device; + +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 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(), + }) + } + } +} diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 0d6a01b72..7771e3297 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -10,6 +10,9 @@ pub use self::platform_impl::*; #[cfg(feature = "custom")] pub use crate::host::custom::{Device as CustomDevice, Host as CustomHost, Stream as CustomStream}; +#[cfg(all(target_os = "windows", feature = "asio"))] +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