From a25908429f980e5bfee7ea341345903e9766b354 Mon Sep 17 00:00:00 2001 From: Sintel Date: Sat, 13 Dec 2025 06:07:48 +0100 Subject: [PATCH 1/6] get the panel to work --- asio-sys/build.rs | 3 +++ asio-sys/src/bindings/mod.rs | 4 ++++ examples/asio_panel.rs | 41 ++++++++++++++++++++++++++++++++++++ src/host/asio/device.rs | 8 +++++++ src/host/mod.rs | 2 +- src/lib.rs | 2 +- 6 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 examples/asio_panel.rs diff --git a/asio-sys/build.rs b/asio-sys/build.rs index 00e49dbc5..b14da2876 100644 --- a/asio-sys/build.rs +++ b/asio-sys/build.rs @@ -64,6 +64,8 @@ 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-lib=dylib=User32"); println!("cargo:rustc-link-search={}", out_dir.display()); println!("cargo:rustc-link-lib=static=asio"); println!("cargo:rustc-cfg=asio"); @@ -218,6 +220,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..c30695818 --- /dev/null +++ b/examples/asio_panel.rs @@ -0,0 +1,41 @@ +use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; + +// Import the specific ASIO host module directly +#[cfg(target_os = "windows")] +use cpal::host::asio; + +#[cfg(target_os = "windows")] +fn main() -> anyhow::Result<()> { + let host = asio::Host::new()?; + + let device = host + .default_output_device() + .ok_or_else(|| anyhow::anyhow!("No ASIO device found"))?; + + println!("Opening control panel for: {}", device.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()?; + + if let Err(e) = device.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(60)); + + Ok(()) +} + +#[cfg(not(target_os = "windows"))] +fn main() {} diff --git a/src/host/asio/device.rs b/src/host/asio/device.rs index 1d4ceb442..be6434ade 100644 --- a/src/host/asio/device.rs +++ b/src/host/asio/device.rs @@ -189,6 +189,14 @@ impl Device { sample_format, }) } + + pub fn open_control_panel(&self) -> Result<(), BackendSpecificError> { + self.driver + .open_control_panel() + .map_err(|e| BackendSpecificError { + description: format!("Failed to open control panel: {:?}", e), + }) + } } impl Devices { diff --git a/src/host/mod.rs b/src/host/mod.rs index 7290ce66f..27c0d6099 100644 --- a/src/host/mod.rs +++ b/src/host/mod.rs @@ -8,7 +8,7 @@ pub(crate) mod aaudio; ))] pub(crate) mod alsa; #[cfg(all(windows, feature = "asio"))] -pub(crate) mod asio; +pub mod asio; #[cfg(all( feature = "wasm-bindgen", feature = "audioworklet", diff --git a/src/lib.rs b/src/lib.rs index e2cae1c5a..a4d10ff0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -193,7 +193,7 @@ use std::time::Duration; pub mod device_description; mod error; -mod host; +pub mod host; pub mod platform; mod samples_formats; pub mod traits; From 5d0411a9958a5ac72bff7c58621728e851136ae1 Mon Sep 17 00:00:00 2001 From: Sintel Date: Sat, 13 Dec 2025 06:36:46 +0100 Subject: [PATCH 2/6] use DeviceInner instead --- examples/asio_panel.rs | 23 ++++++++++++++--------- src/host/mod.rs | 2 +- src/lib.rs | 2 +- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/examples/asio_panel.rs b/examples/asio_panel.rs index c30695818..3b1f103bd 100644 --- a/examples/asio_panel.rs +++ b/examples/asio_panel.rs @@ -1,18 +1,19 @@ +use cpal::platform::DeviceInner; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; - -// Import the specific ASIO host module directly -#[cfg(target_os = "windows")] -use cpal::host::asio; +use cpal::HostId; #[cfg(target_os = "windows")] fn main() -> anyhow::Result<()> { - let host = asio::Host::new()?; + 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.name()?); + println!( + "Opening control panel for: {}", + device.description()?.name() + ); let config = device.default_output_config()?; @@ -27,12 +28,16 @@ fn main() -> anyhow::Result<()> { stream.play()?; - if let Err(e) = device.open_control_panel() { - eprintln!("Could not open panel: {:?}", e); + let device = device.as_inner(); + + if let DeviceInner::Asio(asio) = device { + if let Err(e) = 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(60)); + std::thread::sleep(std::time::Duration::from_secs(5)); Ok(()) } diff --git a/src/host/mod.rs b/src/host/mod.rs index 27c0d6099..7290ce66f 100644 --- a/src/host/mod.rs +++ b/src/host/mod.rs @@ -8,7 +8,7 @@ pub(crate) mod aaudio; ))] pub(crate) mod alsa; #[cfg(all(windows, feature = "asio"))] -pub mod asio; +pub(crate) mod asio; #[cfg(all( feature = "wasm-bindgen", feature = "audioworklet", diff --git a/src/lib.rs b/src/lib.rs index a4d10ff0d..e2cae1c5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -193,7 +193,7 @@ use std::time::Duration; pub mod device_description; mod error; -pub mod host; +mod host; pub mod platform; mod samples_formats; pub mod traits; From f3de53f1af136de87883f781edadb18da2695cd4 Mon Sep 17 00:00:00 2001 From: Sintel <17052580+Sin-tel@users.noreply.github.com> Date: Sat, 13 Dec 2025 19:32:16 +0100 Subject: [PATCH 3/6] fix CI --- examples/asio_panel.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/asio_panel.rs b/examples/asio_panel.rs index 3b1f103bd..c9862bf0d 100644 --- a/examples/asio_panel.rs +++ b/examples/asio_panel.rs @@ -1,9 +1,9 @@ -use cpal::platform::DeviceInner; -use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; -use cpal::HostId; - -#[cfg(target_os = "windows")] +#[cfg(all(windows, feature = "asio"))] fn main() -> anyhow::Result<()> { + use cpal::platform::DeviceInner; + use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; + use cpal::HostId; + let host = cpal::host_from_id(HostId::Asio)?; let device = host @@ -42,5 +42,5 @@ fn main() -> anyhow::Result<()> { Ok(()) } -#[cfg(not(target_os = "windows"))] +#[cfg(not(all(windows, feature = "asio")))] fn main() {} From 336b168d7500fd75bb0df943d380665abb8a428f Mon Sep 17 00:00:00 2001 From: Sintel <17052580+Sin-tel@users.noreply.github.com> Date: Sat, 13 Dec 2025 22:49:38 +0100 Subject: [PATCH 4/6] spawn a thread in the example --- examples/asio_panel.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/asio_panel.rs b/examples/asio_panel.rs index c9862bf0d..84490694c 100644 --- a/examples/asio_panel.rs +++ b/examples/asio_panel.rs @@ -28,13 +28,15 @@ fn main() -> anyhow::Result<()> { stream.play()?; - let device = device.as_inner(); - - if let DeviceInner::Asio(asio) = device { - if let Err(e) = asio.open_control_panel() { - eprintln!("Could not open panel: {:?}", e); - } - } + if let DeviceInner::Asio(asio_device) = device.as_inner() { + // This is a blocking call on some devices, so spawn it in its own thread. + let asio_device = asio_device.clone(); + std::thread::spawn(move || { + if let Err(e) = asio_device.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)); From b728f520cd1f1af45f852d523f1f4084bb4ccf3b Mon Sep 17 00:00:00 2001 From: Sintel Date: Thu, 18 Dec 2025 14:20:29 +0100 Subject: [PATCH 5/6] fix redundant library in build.rs --- asio-sys/build.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/asio-sys/build.rs b/asio-sys/build.rs index b14da2876..41c1763dc 100644 --- a/asio-sys/build.rs +++ b/asio-sys/build.rs @@ -65,7 +65,6 @@ fn main() { 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-lib=dylib=User32"); println!("cargo:rustc-link-search={}", out_dir.display()); println!("cargo:rustc-link-lib=static=asio"); println!("cargo:rustc-cfg=asio"); From 330e4f78c6802eaa8547c1356436acd02db11f40 Mon Sep 17 00:00:00 2001 From: Sintel Date: Thu, 18 Dec 2025 21:31:04 +0100 Subject: [PATCH 6/6] trying out extension trait --- examples/asio_panel.rs | 17 +++++++---------- src/host/asio/device.rs | 8 -------- src/platform/asio.rs | 24 ++++++++++++++++++++++++ src/platform/mod.rs | 3 +++ 4 files changed, 34 insertions(+), 18 deletions(-) create mode 100644 src/platform/asio.rs diff --git a/examples/asio_panel.rs b/examples/asio_panel.rs index 84490694c..08ce7065d 100644 --- a/examples/asio_panel.rs +++ b/examples/asio_panel.rs @@ -1,6 +1,6 @@ #[cfg(all(windows, feature = "asio"))] fn main() -> anyhow::Result<()> { - use cpal::platform::DeviceInner; + use cpal::platform::asio::AsioDeviceExt; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; use cpal::HostId; @@ -28,15 +28,12 @@ fn main() -> anyhow::Result<()> { stream.play()?; - if let DeviceInner::Asio(asio_device) = device.as_inner() { - // This is a blocking call on some devices, so spawn it in its own thread. - let asio_device = asio_device.clone(); - std::thread::spawn(move || { - if let Err(e) = asio_device.open_control_panel() { - eprintln!("Could not open panel: {:?}", e); - } - }); - }; + // 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)); diff --git a/src/host/asio/device.rs b/src/host/asio/device.rs index be6434ade..1d4ceb442 100644 --- a/src/host/asio/device.rs +++ b/src/host/asio/device.rs @@ -189,14 +189,6 @@ impl Device { sample_format, }) } - - pub fn open_control_panel(&self) -> Result<(), BackendSpecificError> { - self.driver - .open_control_panel() - .map_err(|e| BackendSpecificError { - description: format!("Failed to open control panel: {:?}", e), - }) - } } impl Devices { 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