From 3d5f44764e5269b0dd477e811c39092aaecbd89a Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Tue, 14 Jun 2022 16:27:35 +0200 Subject: [PATCH] Add boilerplate for a JACK backend --- Cargo.lock | 1 + Cargo.toml | 3 ++- src/wrapper/standalone.rs | 29 ++++++++++++++++++++++------- src/wrapper/standalone/backend.rs | 29 +++++++++++++++++++---------- src/wrapper/standalone/config.rs | 2 ++ 5 files changed, 46 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be13b962..1b455c91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2096,6 +2096,7 @@ dependencies = [ name = "nih_plug" version = "0.0.0" dependencies = [ + "anyhow", "assert_no_alloc", "atomic_float", "atomic_refcell", diff --git a/Cargo.toml b/Cargo.toml index b9c57bee..baeddfb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,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"] +standalone = ["dep:anyhow", "dep:baseview", "dep:clap"] # 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 @@ -83,6 +83,7 @@ widestring = "1.0.0-beta.1" assert_no_alloc = { version = "1.1", optional = true } # Used for the `standalone` feature +anyhow = { version = "1.0", optional = true } # NOTE: OpenGL support is not needed here, but rust-analyzer gets confused when # some crates do use it and others don't baseview = { git = "https://github.com/robbert-vdh/baseview.git", branch = "feature/resize", features = ["opengl"], optional = true } diff --git a/src/wrapper/standalone.rs b/src/wrapper/standalone.rs index 515b7120..d3381de6 100644 --- a/src/wrapper/standalone.rs +++ b/src/wrapper/standalone.rs @@ -3,6 +3,7 @@ use clap::{FromArgMatches, IntoApp}; +use self::backend::Backend; use self::config::WrapperConfig; use self::wrapper::{Wrapper, WrapperError}; use super::util::setup_logger; @@ -68,14 +69,28 @@ pub fn nih_export_standalone_with_args match backend::Jack::new(config.clone()) { + Ok(backend) => run_wrapper::(backend, config), + Err(_) => { + nih_log!("Could not initialize JACK, falling back to the dummy audio backend"); + 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) => { + nih_log!("{:#}", err); + false + } + }, + config::BackendType::Dummmy => { + run_wrapper::(backend::Dummy::new(config.clone()), config) + } + } +} - // TODO: We should try JACK first, then CPAL, and then fall back to the dummy backend. With a - // command line option to override this behavior. - let backend = backend::Dummy::new(config.clone()); +fn run_wrapper(backend: B, config: WrapperConfig) -> bool { let wrapper = match Wrapper::::new(backend, config.clone()) { Ok(wrapper) => wrapper, Err(err) => { diff --git a/src/wrapper/standalone/backend.rs b/src/wrapper/standalone/backend.rs index b6cdf2e9..a51a9726 100644 --- a/src/wrapper/standalone/backend.rs +++ b/src/wrapper/standalone/backend.rs @@ -1,3 +1,4 @@ +use anyhow::Result; use std::time::{Duration, Instant}; use crate::buffer::Buffer; @@ -15,10 +16,10 @@ pub trait Backend: 'static + Send + Sync { fn run(&mut self, cb: impl FnMut(&mut Buffer) -> bool); } -// /// Uses JACK audio and MIDI. -// pub struct Jack { -// // TODO -// } +/// Uses JACK audio and MIDI. +pub struct Jack { + config: WrapperConfig, +} /// This backend doesn't input or output any audio or MIDI. It only exists so the standalone /// application can continue to run even when there is no audio backend available. This can be @@ -27,12 +28,11 @@ pub struct Dummy { config: WrapperConfig, } -// TODO: Add a JACK backend -// impl Backend for Jack { -// fn run(&mut self, cb: impl FnMut(&mut Buffer) -> bool) { -// todo!() -// } -// } +impl Backend for Jack { + fn run(&mut self, cb: impl FnMut(&mut Buffer) -> bool) { + todo!() + } +} impl Backend for Dummy { fn run(&mut self, mut cb: impl FnMut(&mut Buffer) -> bool) { @@ -73,6 +73,15 @@ impl Backend for Dummy { } } +impl Jack { + /// Initialize the JACK backend. Returns an error if this failed for whatever reason. + pub fn new(config: WrapperConfig) -> Result { + // TODO: Actually implement the JACK backend + anyhow::bail!("Could not initialize JACK backend") + // Ok(Self { config }) + } +} + impl Dummy { pub fn new(config: WrapperConfig) -> Self { Self { config } diff --git a/src/wrapper/standalone/config.rs b/src/wrapper/standalone/config.rs index 1b89f418..1ce29b4e 100644 --- a/src/wrapper/standalone/config.rs +++ b/src/wrapper/standalone/config.rs @@ -51,6 +51,8 @@ pub enum BackendType { /// /// This defaults to JACK if JACK is available, and falls back to the dummy backend if not. Auto, + /// Use JACK for audio and MIDI. + Jack, /// Does not playback or receive any audio or MIDI. Dummmy, }