From a011eaa07c4dc50940cc46c4e7c40a85a785d9ee Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Fri, 19 Aug 2022 17:11:24 +0200 Subject: [PATCH] Add most of a CPAL standalone backend For ALSA, CoreAudio, and WASAPI. --- Cargo.lock | 304 ++++++++++++++++++++++++- Cargo.toml | 3 +- src/wrapper/standalone.rs | 96 +++++++- src/wrapper/standalone/backend.rs | 9 +- src/wrapper/standalone/backend/cpal.rs | 82 +++++++ src/wrapper/standalone/config.rs | 20 ++ 6 files changed, 490 insertions(+), 24 deletions(-) create mode 100644 src/wrapper/standalone/backend/cpal.rs diff --git a/Cargo.lock b/Cargo.lock index f1599648..2dba8ac2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,6 +62,28 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" +[[package]] +name = "alsa" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5915f52fe2cf65e83924d037b6c5290b7cee097c6b5c8700746e6168a343fd6b" +dependencies = [ + "alsa-sys", + "bitflags", + "libc", + "nix 0.23.1", +] + +[[package]] +name = "alsa-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "android_glue" version = "0.2.3" @@ -330,7 +352,7 @@ dependencies = [ "cocoa", "core-foundation 0.9.3", "keyboard-types", - "nix", + "nix 0.22.3", "objc", "raw-window-handle", "uuid", @@ -348,7 +370,7 @@ dependencies = [ "cocoa", "core-foundation 0.9.3", "keyboard-types", - "nix", + "nix 0.22.3", "objc", "raw-window-handle", "uuid", @@ -358,6 +380,25 @@ dependencies = [ "xcb-util", ] +[[package]] +name = "bindgen" +version = "0.59.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", +] + [[package]] name = "bit-set" version = "0.5.2" @@ -431,6 +472,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + [[package]] name = "cache-padded" version = "1.2.0" @@ -453,6 +500,21 @@ dependencies = [ "jobserver", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "0.1.10" @@ -486,6 +548,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17cc5e6b5ab06331c33589842070416baa137e8b0eb912b008cfd4a78ada7919" +[[package]] +name = "clang-sys" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "3.2.5" @@ -597,6 +670,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "concurrent-queue" version = "1.2.2" @@ -730,6 +813,50 @@ dependencies = [ "objc", ] +[[package]] +name = "coreaudio-rs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11894b20ebfe1ff903cbdc52259693389eea03b94918a2def2c30c3bf227ad88" +dependencies = [ + "bitflags", + "coreaudio-sys", +] + +[[package]] +name = "coreaudio-sys" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dff444d80630d7073077d38d40b4501fd518bd2b922c2a55edcc8b0f7be57e6" +dependencies = [ + "bindgen", +] + +[[package]] +name = "cpal" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74117836a5124f3629e4b474eed03e479abaf98988b4bb317e29f08cfe0e4116" +dependencies = [ + "alsa", + "core-foundation-sys 0.8.3", + "coreaudio-rs", + "jni", + "js-sys", + "lazy_static", + "libc", + "mach", + "ndk 0.6.0", + "ndk-glue 0.6.2", + "nix 0.23.1", + "oboe", + "parking_lot 0.11.2", + "stdweb", + "thiserror", + "web-sys", + "winapi", +] + [[package]] name = "crc32fast" version = "1.3.2" @@ -1504,6 +1631,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + [[package]] name = "gloo-timers" version = "0.2.4" @@ -1989,6 +2122,20 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + [[package]] name = "jni-sys" version = "0.3.0" @@ -2086,6 +2233,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.126" @@ -2191,6 +2344,15 @@ dependencies = [ "lyon_path", ] +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -2334,7 +2496,20 @@ checksum = "96d868f654c72e75f8687572699cdabe755f03effbb62542768e995d5b8d699d" dependencies = [ "bitflags", "jni-sys", - "ndk-sys", + "ndk-sys 0.2.2", + "num_enum", + "thiserror", +] + +[[package]] +name = "ndk" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" +dependencies = [ + "bitflags", + "jni-sys", + "ndk-sys 0.3.0", "num_enum", "thiserror", ] @@ -2354,10 +2529,25 @@ dependencies = [ "lazy_static", "libc", "log", - "ndk", + "ndk 0.5.0", "ndk-context", "ndk-macro", - "ndk-sys", + "ndk-sys 0.2.2", +] + +[[package]] +name = "ndk-glue" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d0c4a7b83860226e6b4183edac21851f05d5a51756e97a1144b7f5a6b63e65f" +dependencies = [ + "lazy_static", + "libc", + "log", + "ndk 0.6.0", + "ndk-context", + "ndk-macro", + "ndk-sys 0.3.0", ] [[package]] @@ -2379,6 +2569,15 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" +[[package]] +name = "ndk-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" +dependencies = [ + "jni-sys", +] + [[package]] name = "nih_plug" version = "0.0.0" @@ -2393,6 +2592,7 @@ dependencies = [ "cfg-if 1.0.0", "clap", "clap-sys", + "cpal", "crossbeam", "jack", "lazy_static", @@ -2488,6 +2688,19 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nix" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +dependencies = [ + "bitflags", + "cc", + "cfg-if 1.0.0", + "libc", + "memoffset", +] + [[package]] name = "nohash-hasher" version = "0.2.0" @@ -2513,6 +2726,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -2653,6 +2877,29 @@ dependencies = [ "memchr", ] +[[package]] +name = "oboe" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f63c358b4fa0fbcfefd7c8be5cfc39c08ce2389f5325687e7762a48d30a5c1" +dependencies = [ + "jni", + "ndk 0.6.0", + "ndk-context", + "num-derive", + "num-traits", + "oboe-sys", +] + +[[package]] +name = "oboe-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3370abb7372ed744232c12954d920d1a40f1c4686de9e79e800021ef492294bd" +dependencies = [ + "cc", +] + [[package]] name = "once_cell" version = "1.12.0" @@ -2813,6 +3060,12 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "percent-encoding" version = "2.1.0" @@ -3236,6 +3489,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + [[package]] name = "renderdoc-sys" version = "0.7.1" @@ -3492,6 +3760,12 @@ dependencies = [ "libc", ] +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + [[package]] name = "sid" version = "0.6.1" @@ -3585,7 +3859,7 @@ dependencies = [ "lazy_static", "log", "memmap2", - "nix", + "nix 0.22.3", "pkg-config", "wayland-client", "wayland-cursor", @@ -3661,6 +3935,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stdweb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e" + [[package]] name = "stft" version = "0.1.0" @@ -4235,7 +4515,7 @@ dependencies = [ "bitflags", "downcast-rs", "libc", - "nix", + "nix 0.22.3", "scoped-tls", "wayland-commons", "wayland-scanner", @@ -4248,7 +4528,7 @@ version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94f6e5e340d7c13490eca867898c4cec5af56c27a5ffe5c80c6fc4708e22d33e" dependencies = [ - "nix", + "nix 0.22.3", "once_cell", "smallvec", "wayland-sys", @@ -4260,7 +4540,7 @@ version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c52758f13d5e7861fc83d942d3d99bf270c83269575e52ac29e5b73cb956a6bd" dependencies = [ - "nix", + "nix 0.22.3", "wayland-client", "xcursor", ] @@ -4576,9 +4856,9 @@ dependencies = [ "libc", "log", "mio", - "ndk", - "ndk-glue", - "ndk-sys", + "ndk 0.5.0", + "ndk-glue 0.5.2", + "ndk-sys 0.2.2", "objc", "parking_lot 0.11.2", "percent-encoding", diff --git a/Cargo.toml b/Cargo.toml index af8c7d57..af620074 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ assert_process_allocs = ["dep:assert_no_alloc"] # Enables an export target for standalone binaries through the # `nih_export_standalone()` function. Disabled by default as this requires # building additional dependencies for audio and MIDI handling. -standalone = ["dep:baseview", "dep:clap", "dep:jack"] +standalone = ["dep:baseview", "dep:clap", "dep:cpal", "dep:jack"] # Enables the `nih_export_vst3!()` macro. Enabled by default. This feature # exists mostly for GPL-compliance reasons, since even if you don't use the VST3 # wrapper you might otherwise still include a couple (unused) symbols from the @@ -97,6 +97,7 @@ assert_no_alloc = { version = "1.1", optional = true } baseview = { git = "https://github.com/robbert-vdh/baseview.git", branch = "feature/resize", features = ["opengl"], optional = true } # All the claps! clap = { version = "3.2", features = ["derive"], optional = true } +cpal = { version = "0.13.5", optional = true } # The current upstream jack panics when it can't load the JACK library, which breaks the backend fallback jack = { git = "https://github.com/robbert-vdh/rust-jack.git", branch = "feature/handle-library-failure", optional = true } diff --git a/src/wrapper/standalone.rs b/src/wrapper/standalone.rs index 7ccf1cbe..9a5ab8d1 100644 --- a/src/wrapper/standalone.rs +++ b/src/wrapper/standalone.rs @@ -70,16 +70,66 @@ pub fn nih_export_standalone_with_args match backend::Jack::new::

(config.clone()) { - Ok(backend) => { + config::BackendType::Auto => { + let result = backend::Jack::new::

(config.clone()).map(|backend| { nih_log!("Using the JACK backend"); - run_wrapper::(backend, config) - } - Err(_) => { - nih_log!("Could not initialize JACK, falling back to the dummy audio backend"); + run_wrapper::(backend, config.clone()) + }); + + #[cfg(target_os = "linux")] + let result = result.or_else(|_| { + match backend::Cpal::new::

(config.clone(), cpal::HostId::Alsa) { + Ok(backend) => { + nih_log!("Using the ALSA backend"); + Ok(run_wrapper::(backend, config.clone())) + } + Err(err) => { + nih_error!( + "Could not initialize either the JACK or the ALSA backends, falling \ + back to the dummy audio backend" + ); + Err(err) + } + } + }); + #[cfg(target_os = "macos")] + let result = result.or_else(|_| { + match backend::Cpal::new::

(config.clone(), cpal::HostId::CoreAudio) { + Ok(backend) => { + nih_log!("Using the CoreAudio backend"); + Ok(run_wrapper::(backend, config.clone())) + } + Err(err) => { + nih_error!( + "Could not initialize either the JACK or the CoreAudio backends, \ + falling back to the dummy audio backend" + ); + Err(err) + } + } + }); + #[cfg(target_os = "windows")] + let result = result.or_else(|_| { + match backend::Cpal::new::

(config.clone(), cpal::HostId::Wasapi) { + Ok(backend) => { + nih_log!("Using the WASAPI backend"); + Ok(run_wrapper::(backend, config.clone())) + } + Err(err) => { + nih_error!( + "Could not initialize either the JACK or the WASAPI backends, falling \ + back to the dummy audio backend" + ); + Err(err) + } + } + }); + + result.unwrap_or_else(|_| { + nih_error!("Falling back to the dummy audio backend, audio and MIDI will not work"); run_wrapper::(backend::Dummy::new::

(config.clone()), config) - } - }, + }) + } config::BackendType::Jack => match backend::Jack::new::

(config.clone()) { Ok(backend) => run_wrapper::(backend, config), Err(err) => { @@ -87,6 +137,36 @@ pub fn nih_export_standalone_with_args { + match backend::Cpal::new::

(config.clone(), cpal::HostId::Alsa) { + Ok(backend) => run_wrapper::(backend, config), + Err(err) => { + nih_error!("Could not initialize the ALSA backend: {:#}", err); + false + } + } + } + #[cfg(target_os = "macos")] + config::BackendType::CoreAudio => { + match backend::Cpal::new::

(config.clone(), cpal::HostId::CoreAudio) { + Ok(backend) => run_wrapper::(backend, config), + Err(err) => { + nih_error!("Could not initialize the CoreAudio backend: {:#}", err); + false + } + } + } + #[cfg(target_os = "windows")] + config::BackendType::Wasapi => { + match backend::Cpal::new::

(config.clone(), cpal::HostId::Wasapi) { + Ok(backend) => run_wrapper::(backend, config), + Err(err) => { + nih_error!("Could not initialize the WASAPI backend: {:#}", err); + false + } + } + } config::BackendType::Dummy => { run_wrapper::(backend::Dummy::new::

(config.clone()), config) } diff --git a/src/wrapper/standalone/backend.rs b/src/wrapper/standalone/backend.rs index 448da915..7778b4c8 100644 --- a/src/wrapper/standalone/backend.rs +++ b/src/wrapper/standalone/backend.rs @@ -1,12 +1,15 @@ -pub use self::dummy::Dummy; -pub use self::jack::Jack; -pub use crate::buffer::Buffer; use crate::context::Transport; use crate::midi::NoteEvent; +mod cpal; mod dummy; mod jack; +pub use self::cpal::Cpal; +pub use self::dummy::Dummy; +pub use self::jack::Jack; +pub use crate::buffer::Buffer; + /// An audio+MIDI backend for the standalone wrapper. pub trait Backend: 'static + Send + Sync { /// Start processing audio and MIDI on this thread. The process callback will be called whenever diff --git a/src/wrapper/standalone/backend/cpal.rs b/src/wrapper/standalone/backend/cpal.rs new file mode 100644 index 00000000..5cc374b3 --- /dev/null +++ b/src/wrapper/standalone/backend/cpal.rs @@ -0,0 +1,82 @@ +use anyhow::{Context, Result}; +use cpal::traits::*; + +use super::super::config::WrapperConfig; +use super::Backend; +use crate::buffer::Buffer; +use crate::context::Transport; +use crate::midi::NoteEvent; +use crate::plugin::Plugin; + +/// Uses CPAL for audio and midir for MIDI. +pub struct Cpal { + // TODO: + // TODO: MIDI +} + +impl Backend for Cpal { + fn run( + &mut self, + mut cb: impl FnMut(&mut Buffer, Transport, &[NoteEvent], &mut Vec) -> bool + + 'static + + Send, + ) { + // TODO: + } +} + +impl Cpal { + /// Initialize the backend with the specified host. Returns an error if this failed for whatever + /// reason. + pub fn new(config: WrapperConfig, cpal_host_id: cpal::HostId) -> Result { + let host = cpal::host_from_id(cpal_host_id).context("The Audio API is unavailable")?; + + // No input device is connected unless requested by the user to avoid feedback loops + let input_device = config + .input_device + .map(|name| -> Result { + let device = host + .input_devices() + .context("No audio input devices available")? + // `.name()` returns a `Result` with a non-Eq error type so you can't compare this + // directly + .find(|d| d.name().as_deref().map(|n| n == name).unwrap_or(false)) + .with_context(|| { + // This is a bit awkward, but instead of adding a dedicated option we'll just + // list all of the available devices in the error message when the chosen device + // does not exist + let mut message = + format!("Unknown input device '{name}'. Available devices are:"); + for device_name in host.input_devices().unwrap().flat_map(|d| d.name()) { + message.push_str(&format!("\n{device_name}")) + } + + message + })?; + + Ok(device) + }) + .transpose()?; + + let output_device = match config.output_device { + Some(name) => host + .output_devices() + .context("No audio output devices available")? + .find(|d| d.name().as_deref().map(|n| n == name).unwrap_or(false)) + .with_context(|| { + let mut message = + format!("Unknown output device '{name}'. Available devices are:"); + for device_name in host.output_devices().unwrap().flat_map(|d| d.name()) { + message.push_str(&format!("\n{device_name}")) + } + + message + })?, + None => host + .default_output_device() + .context("No default audio output device available")?, + }; + + anyhow::bail!("Not yet implemented"); + } +} diff --git a/src/wrapper/standalone/config.rs b/src/wrapper/standalone/config.rs index 60851203..f4b059aa 100644 --- a/src/wrapper/standalone/config.rs +++ b/src/wrapper/standalone/config.rs @@ -10,6 +10,17 @@ pub struct WrapperConfig { /// no audio input or output if the other backends are not available. #[clap(value_parser, short = 'b', long, default_value = "auto")] pub backend: BackendType, + /// The input device for the ALSA, CoreAudio, and WASAPI backends. No input will be connected if + /// this is not specified. + /// + /// Specifying an empty string or other invalid value will list all available input devices. + #[clap(value_parser, long)] + pub input_device: Option, + /// The output device for the ALSA, CoreAudio, and WASAPI backends. + /// + /// Specifying an empty string or other invalid value will list all available output devices. + #[clap(value_parser, long)] + pub output_device: Option, // These will default to the plugin's default input and output channel count. We could set the // default value here to match those, but that would require a custom Args+FromArgMatches @@ -81,6 +92,15 @@ pub enum BackendType { Auto, /// Use JACK for audio and MIDI. Jack, + /// Use ALSA for audio and MIDI. + #[cfg(target_os = "linux")] + Alsa, + /// Use CoreAudio for audio and MIDI. + #[cfg(target_os = "macos")] + CoreAudio, + /// Use WASAPI for audio and MIDI. + #[cfg(target_os = "windows")] + Wasapi, /// Does not playback or receive any audio or MIDI. Dummy, }