From 10a55e1f002b981653ffa43fd79b82343820da52 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Tue, 14 Jun 2022 17:30:57 +0200 Subject: [PATCH] Move standalone backends to their own modules --- src/wrapper/standalone/backend.rs | 92 ++----------------------- src/wrapper/standalone/backend/dummy.rs | 57 +++++++++++++++ src/wrapper/standalone/backend/jack.rs | 36 ++++++++++ 3 files changed, 98 insertions(+), 87 deletions(-) create mode 100644 src/wrapper/standalone/backend/dummy.rs create mode 100644 src/wrapper/standalone/backend/jack.rs diff --git a/src/wrapper/standalone/backend.rs b/src/wrapper/standalone/backend.rs index 1158d32f..6c8e3f29 100644 --- a/src/wrapper/standalone/backend.rs +++ b/src/wrapper/standalone/backend.rs @@ -1,9 +1,9 @@ -use anyhow::{Context, Result}; -use std::time::{Duration, Instant}; +pub use self::dummy::Dummy; +pub use self::jack::Jack; +pub use crate::buffer::Buffer; -use crate::buffer::Buffer; - -use super::config::WrapperConfig; +mod dummy; +mod jack; /// An audio+MIDI backend for the standalone wrapper. pub trait Backend: 'static + Send + Sync { @@ -15,85 +15,3 @@ pub trait Backend: 'static + Send + Sync { /// TODO: MIDI fn run(&mut self, cb: impl FnMut(&mut Buffer) -> bool); } - -/// Uses JACK audio and MIDI. -pub struct Jack { - config: WrapperConfig, - client: jack::Client, -} - -/// 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 -/// useful for testing plugin GUIs. -pub struct Dummy { - config: WrapperConfig, -} - -impl Backend for Jack { - fn run(&mut self, cb: impl FnMut(&mut Buffer) -> bool) { - // TODO: Create an async client and do The Thing (tm) - todo!() - } -} - -impl Backend for Dummy { - fn run(&mut self, mut cb: impl FnMut(&mut Buffer) -> bool) { - // We can't really do anything meaningful here, so we'll simply periodically call the - // callback with empty buffers. - let interval = - Duration::from_secs_f32(self.config.period_size as f32 / self.config.sample_rate); - - let mut channels = vec![ - vec![0.0f32; self.config.period_size as usize]; - self.config.output_channels as usize - ]; - let mut buffer = Buffer::default(); - unsafe { - buffer.with_raw_vec(|output_slices| { - // SAFETY: `channels` is no longer used directly after this - *output_slices = channels - .iter_mut() - .map(|channel| &mut *(channel.as_mut_slice() as *mut [f32])) - .collect(); - }) - } - - loop { - let period_start = Instant::now(); - - for channel in buffer.as_slice() { - channel.fill(0.0); - } - - if !cb(&mut buffer) { - break; - } - - let period_end = Instant::now(); - std::thread::sleep((period_start + interval).saturating_duration_since(period_end)); - } - } -} - -impl Jack { - /// Initialize the JACK backend. Returns an error if this failed for whatever reason. - pub fn new(name: &str, config: WrapperConfig) -> Result { - let (client, status) = jack::Client::new(name, jack::ClientOptions::NO_START_SERVER) - .context("Error while initializing the JACK client")?; - if !status.is_empty() { - anyhow::bail!("The JACK server returned an error: {status:?}"); - } - - // TODO: Register ports - // TODO: Connect output - // TODO: Command line argument to connect the inputs? - - Ok(Self { config, client }) - } -} - -impl Dummy { - pub fn new(config: WrapperConfig) -> Self { - Self { config } - } -} diff --git a/src/wrapper/standalone/backend/dummy.rs b/src/wrapper/standalone/backend/dummy.rs new file mode 100644 index 00000000..89ff9b0a --- /dev/null +++ b/src/wrapper/standalone/backend/dummy.rs @@ -0,0 +1,57 @@ +use std::time::{Duration, Instant}; + +use super::super::config::WrapperConfig; +use super::Backend; +use crate::buffer::Buffer; + +/// 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 +/// useful for testing plugin GUIs. +pub struct Dummy { + config: WrapperConfig, +} + +impl Backend for Dummy { + fn run(&mut self, mut cb: impl FnMut(&mut Buffer) -> bool) { + // We can't really do anything meaningful here, so we'll simply periodically call the + // callback with empty buffers. + let interval = + Duration::from_secs_f32(self.config.period_size as f32 / self.config.sample_rate); + + let mut channels = vec![ + vec![0.0f32; self.config.period_size as usize]; + self.config.output_channels as usize + ]; + let mut buffer = Buffer::default(); + unsafe { + buffer.with_raw_vec(|output_slices| { + // SAFETY: `channels` is no longer used directly after this + *output_slices = channels + .iter_mut() + .map(|channel| &mut *(channel.as_mut_slice() as *mut [f32])) + .collect(); + }) + } + + loop { + let period_start = Instant::now(); + + for channel in buffer.as_slice() { + channel.fill(0.0); + } + + if !cb(&mut buffer) { + break; + } + + let period_end = Instant::now(); + std::thread::sleep((period_start + interval).saturating_duration_since(period_end)); + } + } +} + +impl Dummy { + pub fn new(config: WrapperConfig) -> Self { + Self { config } + } +} diff --git a/src/wrapper/standalone/backend/jack.rs b/src/wrapper/standalone/backend/jack.rs new file mode 100644 index 00000000..2a600f4b --- /dev/null +++ b/src/wrapper/standalone/backend/jack.rs @@ -0,0 +1,36 @@ +use anyhow::{Context, Result}; +use jack::{Client, ClientOptions}; + +use super::super::config::WrapperConfig; +use super::Backend; +use crate::buffer::Buffer; + +/// Uses JACK audio and MIDI. +pub struct Jack { + config: WrapperConfig, + client: Client, +} + +impl Backend for Jack { + fn run(&mut self, cb: impl FnMut(&mut Buffer) -> bool) { + // TODO: Create an async client and do The Thing (tm) + todo!() + } +} + +impl Jack { + /// Initialize the JACK backend. Returns an error if this failed for whatever reason. + pub fn new(name: &str, config: WrapperConfig) -> Result { + let (client, status) = Client::new(name, ClientOptions::NO_START_SERVER) + .context("Error while initializing the JACK client")?; + if !status.is_empty() { + anyhow::bail!("The JACK server returned an error: {status:?}"); + } + + // TODO: Register ports + // TODO: Connect output + // TODO: Command line argument to connect the inputs? + + Ok(Self { config, client }) + } +}