diff --git a/src/wrapper/standalone/backend.rs b/src/wrapper/standalone/backend.rs index 1ef8ae7e..c31d1adf 100644 --- a/src/wrapper/standalone/backend.rs +++ b/src/wrapper/standalone/backend.rs @@ -1,3 +1,4 @@ +use crate::audio_setup::AuxiliaryBuffers; use crate::context::process::Transport; use crate::midi::PluginNoteEvent; @@ -17,12 +18,11 @@ pub trait Backend: 'static + Send + Sync { /// there's a new block of audio to be processed. The process callback receives the audio /// buffers for the wrapped plugin's outputs. Any inputs will have already been copied to this /// buffer. This will block until the process callback returns `false`. - /// - /// TODO: Auxiliary inputs and outputs fn run( &mut self, cb: impl FnMut( &mut Buffer, + &mut AuxiliaryBuffers, Transport, &[PluginNoteEvent

], &mut Vec>, diff --git a/src/wrapper/standalone/backend/cpal.rs b/src/wrapper/standalone/backend/cpal.rs index 9aa664d0..86e4abcd 100644 --- a/src/wrapper/standalone/backend/cpal.rs +++ b/src/wrapper/standalone/backend/cpal.rs @@ -10,7 +10,7 @@ use rtrb::RingBuffer; use super::super::config::WrapperConfig; use super::Backend; -use crate::audio_setup::AudioIOLayout; +use crate::audio_setup::{AudioIOLayout, AuxiliaryBuffers}; use crate::buffer::Buffer; use crate::context::process::Transport; use crate::midi::{MidiConfig, PluginNoteEvent}; @@ -34,6 +34,7 @@ impl Backend

for Cpal { &mut self, cb: impl FnMut( &mut Buffer, + &mut AuxiliaryBuffers, Transport, &[PluginNoteEvent

], &mut Vec>, @@ -321,6 +322,7 @@ impl Cpal { mut input_rb_consumer: Option>, mut cb: impl FnMut( &mut Buffer, + &mut AuxiliaryBuffers, Transport, &[PluginNoteEvent

], &mut Vec>, @@ -404,6 +406,11 @@ impl Cpal { midi_output_events.clear(); if !cb( &mut buffer, + // FIXME: Use zero filled buffers with the correct size instead + &mut AuxiliaryBuffers { + inputs: &mut [], + outputs: &mut [], + }, transport, &midi_input_events, &mut midi_output_events, diff --git a/src/wrapper/standalone/backend/dummy.rs b/src/wrapper/standalone/backend/dummy.rs index 704d094f..63d805bb 100644 --- a/src/wrapper/standalone/backend/dummy.rs +++ b/src/wrapper/standalone/backend/dummy.rs @@ -3,7 +3,7 @@ use std::time::{Duration, Instant}; use super::super::config::WrapperConfig; use super::Backend; -use crate::audio_setup::AudioIOLayout; +use crate::audio_setup::{AudioIOLayout, AuxiliaryBuffers}; use crate::buffer::Buffer; use crate::context::process::Transport; use crate::midi::PluginNoteEvent; @@ -22,6 +22,7 @@ impl Backend

for Dummy { &mut self, mut cb: impl FnMut( &mut Buffer, + &mut AuxiliaryBuffers, Transport, &[PluginNoteEvent

], &mut Vec>, @@ -52,6 +53,50 @@ impl Backend

for Dummy { }) } + // We'll do the same thing for auxiliary inputs and outputs, so the plugin always gets the + // buffers it expects + let mut aux_input_storage: Vec>> = Vec::new(); + let mut aux_input_buffers: Vec = Vec::new(); + for channel_count in self.audio_io_layout.aux_input_ports { + aux_input_storage.push(vec![ + vec![0.0f32; self.config.period_size as usize]; + channel_count.get() as usize + ]); + + let aux_storage = aux_input_storage.last_mut().unwrap(); + let mut aux_buffer = Buffer::default(); + unsafe { + aux_buffer.set_slices(self.config.period_size as usize, |output_slices| { + // SAFETY: `aux_storage` is no longer used directly after this + *output_slices = aux_storage + .iter_mut() + .map(|channel| &mut *(channel.as_mut_slice() as *mut [f32])) + .collect(); + }) + } + } + + let mut aux_output_storage: Vec>> = Vec::new(); + let mut aux_output_buffers: Vec = Vec::new(); + for channel_count in self.audio_io_layout.aux_output_ports { + aux_output_storage.push(vec![ + vec![0.0f32; self.config.period_size as usize]; + channel_count.get() as usize + ]); + + let aux_storage = aux_output_storage.last_mut().unwrap(); + let mut aux_buffer = Buffer::default(); + unsafe { + aux_buffer.set_slices(self.config.period_size as usize, |output_slices| { + // SAFETY: `aux_storage` is no longer used directly after this + *output_slices = aux_storage + .iter_mut() + .map(|channel| &mut *(channel.as_mut_slice() as *mut [f32])) + .collect(); + }) + } + } + // This queue will never actually be used let mut midi_output_events = Vec::with_capacity(1024); let mut num_processed_samples = 0; @@ -68,9 +113,35 @@ impl Backend

for Dummy { for channel in buffer.as_slice() { channel.fill(0.0); } + for aux_buffer in &mut aux_input_buffers { + for channel in aux_buffer.as_slice() { + channel.fill(0.0); + } + } + for aux_buffer in &mut aux_output_buffers { + for channel in aux_buffer.as_slice() { + channel.fill(0.0); + } + } + + // SAFETY: Shortening these borrows is safe as even if the plugin overwrites the + // slices (which it cannot do without using unsafe code), then they + // would still be reset on the next iteration + let mut aux = unsafe { + AuxiliaryBuffers { + inputs: &mut *(aux_input_buffers.as_mut_slice() as *mut [Buffer]), + outputs: &mut *(aux_output_buffers.as_mut_slice() as *mut [Buffer]), + } + }; midi_output_events.clear(); - if !cb(&mut buffer, transport, &[], &mut midi_output_events) { + if !cb( + &mut buffer, + &mut aux, + transport, + &[], + &mut midi_output_events, + ) { break; } diff --git a/src/wrapper/standalone/backend/jack.rs b/src/wrapper/standalone/backend/jack.rs index a9c5642d..374c1886 100644 --- a/src/wrapper/standalone/backend/jack.rs +++ b/src/wrapper/standalone/backend/jack.rs @@ -12,6 +12,7 @@ use parking_lot::Mutex; use super::super::config::WrapperConfig; use super::Backend; +use crate::audio_setup::AuxiliaryBuffers; use crate::buffer::Buffer; use crate::context::process::Transport; use crate::midi::{MidiConfig, MidiResult, NoteEvent, PluginNoteEvent}; @@ -39,6 +40,7 @@ impl Backend

for Jack { &mut self, mut cb: impl FnMut( &mut Buffer, + &mut AuxiliaryBuffers, Transport, &[PluginNoteEvent

], &mut Vec>, @@ -134,7 +136,17 @@ impl Backend

for Jack { } output_events.clear(); - if cb(&mut buffer, transport, &input_events, &mut output_events) { + if cb( + &mut buffer, + // TODO: Support auxiliary IO in the JACK backend + &mut AuxiliaryBuffers { + inputs: &mut [], + outputs: &mut [], + }, + transport, + &input_events, + &mut output_events, + ) { if let Some(midi_output) = &midi_output { let mut midi_output = midi_output.lock(); let mut midi_writer = midi_output.writer(ps); diff --git a/src/wrapper/standalone/wrapper.rs b/src/wrapper/standalone/wrapper.rs index 6516d9c7..760f459f 100644 --- a/src/wrapper/standalone/wrapper.rs +++ b/src/wrapper/standalone/wrapper.rs @@ -13,7 +13,6 @@ use std::thread; use super::backend::Backend; use super::config::WrapperConfig; use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext}; -use crate::audio_setup::AuxiliaryBuffers; use crate::audio_setup::{AudioIOLayout, BufferConfig, ProcessMode}; use crate::context::gui::AsyncExecutor; use crate::context::process::Transport; @@ -466,7 +465,7 @@ impl> Wrapper { gui_task_sender: channel::Sender, ) { self.clone().backend.borrow_mut().run( - move |buffer, transport, input_events, output_events| { + move |buffer, aux, transport, input_events, output_events| { // TODO: This process wrapper should actually be in the backends (since the backends // should also not allocate in their audio callbacks), but that's a bit more // error prone @@ -479,11 +478,7 @@ impl> Wrapper { let mut plugin = self.plugin.lock(); if let ProcessStatus::Error(err) = plugin.process( buffer, - // TODO: Provide extra inputs and outputs in the JACk backend - &mut AuxiliaryBuffers { - inputs: &mut [], - outputs: &mut [], - }, + aux, &mut self.make_process_context(transport, input_events, output_events), ) { nih_error!("The plugin returned an error while processing:");