1
0
Fork 0

Add auxiliary buffers in dummy standalone backwend

This commit is contained in:
Robbert van der Helm 2023-02-23 19:35:49 +01:00
parent a8be77a513
commit 9e2c2b776b
5 changed files with 98 additions and 13 deletions

View file

@ -1,3 +1,4 @@
use crate::audio_setup::AuxiliaryBuffers;
use crate::context::process::Transport; use crate::context::process::Transport;
use crate::midi::PluginNoteEvent; use crate::midi::PluginNoteEvent;
@ -17,12 +18,11 @@ pub trait Backend<P: Plugin>: 'static + Send + Sync {
/// there's a new block of audio to be processed. The process callback receives the audio /// 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 /// 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`. /// buffer. This will block until the process callback returns `false`.
///
/// TODO: Auxiliary inputs and outputs
fn run( fn run(
&mut self, &mut self,
cb: impl FnMut( cb: impl FnMut(
&mut Buffer, &mut Buffer,
&mut AuxiliaryBuffers,
Transport, Transport,
&[PluginNoteEvent<P>], &[PluginNoteEvent<P>],
&mut Vec<PluginNoteEvent<P>>, &mut Vec<PluginNoteEvent<P>>,

View file

@ -10,7 +10,7 @@ use rtrb::RingBuffer;
use super::super::config::WrapperConfig; use super::super::config::WrapperConfig;
use super::Backend; use super::Backend;
use crate::audio_setup::AudioIOLayout; use crate::audio_setup::{AudioIOLayout, AuxiliaryBuffers};
use crate::buffer::Buffer; use crate::buffer::Buffer;
use crate::context::process::Transport; use crate::context::process::Transport;
use crate::midi::{MidiConfig, PluginNoteEvent}; use crate::midi::{MidiConfig, PluginNoteEvent};
@ -34,6 +34,7 @@ impl<P: Plugin> Backend<P> for Cpal {
&mut self, &mut self,
cb: impl FnMut( cb: impl FnMut(
&mut Buffer, &mut Buffer,
&mut AuxiliaryBuffers,
Transport, Transport,
&[PluginNoteEvent<P>], &[PluginNoteEvent<P>],
&mut Vec<PluginNoteEvent<P>>, &mut Vec<PluginNoteEvent<P>>,
@ -321,6 +322,7 @@ impl Cpal {
mut input_rb_consumer: Option<rtrb::Consumer<f32>>, mut input_rb_consumer: Option<rtrb::Consumer<f32>>,
mut cb: impl FnMut( mut cb: impl FnMut(
&mut Buffer, &mut Buffer,
&mut AuxiliaryBuffers,
Transport, Transport,
&[PluginNoteEvent<P>], &[PluginNoteEvent<P>],
&mut Vec<PluginNoteEvent<P>>, &mut Vec<PluginNoteEvent<P>>,
@ -404,6 +406,11 @@ impl Cpal {
midi_output_events.clear(); midi_output_events.clear();
if !cb( if !cb(
&mut buffer, &mut buffer,
// FIXME: Use zero filled buffers with the correct size instead
&mut AuxiliaryBuffers {
inputs: &mut [],
outputs: &mut [],
},
transport, transport,
&midi_input_events, &midi_input_events,
&mut midi_output_events, &mut midi_output_events,

View file

@ -3,7 +3,7 @@ use std::time::{Duration, Instant};
use super::super::config::WrapperConfig; use super::super::config::WrapperConfig;
use super::Backend; use super::Backend;
use crate::audio_setup::AudioIOLayout; use crate::audio_setup::{AudioIOLayout, AuxiliaryBuffers};
use crate::buffer::Buffer; use crate::buffer::Buffer;
use crate::context::process::Transport; use crate::context::process::Transport;
use crate::midi::PluginNoteEvent; use crate::midi::PluginNoteEvent;
@ -22,6 +22,7 @@ impl<P: Plugin> Backend<P> for Dummy {
&mut self, &mut self,
mut cb: impl FnMut( mut cb: impl FnMut(
&mut Buffer, &mut Buffer,
&mut AuxiliaryBuffers,
Transport, Transport,
&[PluginNoteEvent<P>], &[PluginNoteEvent<P>],
&mut Vec<PluginNoteEvent<P>>, &mut Vec<PluginNoteEvent<P>>,
@ -52,6 +53,50 @@ impl<P: Plugin> Backend<P> 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<Vec<f32>>> = Vec::new();
let mut aux_input_buffers: Vec<Buffer> = 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<Vec<f32>>> = Vec::new();
let mut aux_output_buffers: Vec<Buffer> = 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 // This queue will never actually be used
let mut midi_output_events = Vec::with_capacity(1024); let mut midi_output_events = Vec::with_capacity(1024);
let mut num_processed_samples = 0; let mut num_processed_samples = 0;
@ -68,9 +113,35 @@ impl<P: Plugin> Backend<P> for Dummy {
for channel in buffer.as_slice() { for channel in buffer.as_slice() {
channel.fill(0.0); 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(); 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; break;
} }

View file

@ -12,6 +12,7 @@ use parking_lot::Mutex;
use super::super::config::WrapperConfig; use super::super::config::WrapperConfig;
use super::Backend; use super::Backend;
use crate::audio_setup::AuxiliaryBuffers;
use crate::buffer::Buffer; use crate::buffer::Buffer;
use crate::context::process::Transport; use crate::context::process::Transport;
use crate::midi::{MidiConfig, MidiResult, NoteEvent, PluginNoteEvent}; use crate::midi::{MidiConfig, MidiResult, NoteEvent, PluginNoteEvent};
@ -39,6 +40,7 @@ impl<P: Plugin> Backend<P> for Jack {
&mut self, &mut self,
mut cb: impl FnMut( mut cb: impl FnMut(
&mut Buffer, &mut Buffer,
&mut AuxiliaryBuffers,
Transport, Transport,
&[PluginNoteEvent<P>], &[PluginNoteEvent<P>],
&mut Vec<PluginNoteEvent<P>>, &mut Vec<PluginNoteEvent<P>>,
@ -134,7 +136,17 @@ impl<P: Plugin> Backend<P> for Jack {
} }
output_events.clear(); 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 { if let Some(midi_output) = &midi_output {
let mut midi_output = midi_output.lock(); let mut midi_output = midi_output.lock();
let mut midi_writer = midi_output.writer(ps); let mut midi_writer = midi_output.writer(ps);

View file

@ -13,7 +13,6 @@ use std::thread;
use super::backend::Backend; use super::backend::Backend;
use super::config::WrapperConfig; use super::config::WrapperConfig;
use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext}; use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext};
use crate::audio_setup::AuxiliaryBuffers;
use crate::audio_setup::{AudioIOLayout, BufferConfig, ProcessMode}; use crate::audio_setup::{AudioIOLayout, BufferConfig, ProcessMode};
use crate::context::gui::AsyncExecutor; use crate::context::gui::AsyncExecutor;
use crate::context::process::Transport; use crate::context::process::Transport;
@ -466,7 +465,7 @@ impl<P: Plugin, B: Backend<P>> Wrapper<P, B> {
gui_task_sender: channel::Sender<GuiTask>, gui_task_sender: channel::Sender<GuiTask>,
) { ) {
self.clone().backend.borrow_mut().run( 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 // 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 // should also not allocate in their audio callbacks), but that's a bit more
// error prone // error prone
@ -479,11 +478,7 @@ impl<P: Plugin, B: Backend<P>> Wrapper<P, B> {
let mut plugin = self.plugin.lock(); let mut plugin = self.plugin.lock();
if let ProcessStatus::Error(err) = plugin.process( if let ProcessStatus::Error(err) = plugin.process(
buffer, buffer,
// TODO: Provide extra inputs and outputs in the JACk backend aux,
&mut AuxiliaryBuffers {
inputs: &mut [],
outputs: &mut [],
},
&mut self.make_process_context(transport, input_events, output_events), &mut self.make_process_context(transport, input_events, output_events),
) { ) {
nih_error!("The plugin returned an error while processing:"); nih_error!("The plugin returned an error while processing:");