Use new buffer management in JACK standalones
This commit is contained in:
parent
8196641d65
commit
ca4569e03a
|
@ -1,5 +1,6 @@
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
|
use std::ptr::NonNull;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
@ -17,6 +18,7 @@ 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};
|
||||||
use crate::plugin::Plugin;
|
use crate::plugin::Plugin;
|
||||||
|
use crate::wrapper::util::buffer_management::{BufferManager, ChannelPointers};
|
||||||
use crate::wrapper::util::{clamp_input_event_timing, clamp_output_event_timing};
|
use crate::wrapper::util::{clamp_input_event_timing, clamp_output_event_timing};
|
||||||
|
|
||||||
/// Uses JACK audio and MIDI.
|
/// Uses JACK audio and MIDI.
|
||||||
|
@ -35,6 +37,21 @@ pub struct Jack {
|
||||||
midi_output: Option<Arc<Mutex<Port<MidiOut>>>>,
|
midi_output: Option<Arc<Mutex<Port<MidiOut>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send+Sync wrapper for `Vec<*mut f32>` so we can preallocate channel pointer vectors for use with
|
||||||
|
/// the `BufferManager` API.
|
||||||
|
struct ChannelPointerVec(Vec<*mut f32>);
|
||||||
|
|
||||||
|
unsafe impl Send for ChannelPointerVec {}
|
||||||
|
unsafe impl Sync for ChannelPointerVec {}
|
||||||
|
|
||||||
|
impl ChannelPointerVec {
|
||||||
|
// If you directly access the `.0` field then it will try to move it out of the struct which
|
||||||
|
// undoes the Send+Sync impl.
|
||||||
|
pub fn get(&mut self) -> &mut Vec<*mut f32> {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<P: Plugin> Backend<P> for Jack {
|
impl<P: Plugin> Backend<P> for Jack {
|
||||||
fn run(
|
fn run(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -52,43 +69,36 @@ impl<P: Plugin> Backend<P> for Jack {
|
||||||
let buffer_size = client.buffer_size();
|
let buffer_size = client.buffer_size();
|
||||||
|
|
||||||
// We'll preallocate the buffers here, and then assign them to the slices belonging to the
|
// We'll preallocate the buffers here, and then assign them to the slices belonging to the
|
||||||
// JACK ports later
|
// JACK ports later. For consistency with the other backends we'll reuse the
|
||||||
let mut buffer = Buffer::default();
|
// `BufferManager`, which means we'll need to collect pointers to individual channel slices
|
||||||
unsafe {
|
// into vectors so we can provide the needed `*mut *mut f32` pointers.
|
||||||
buffer.set_slices(0, |output_slices| {
|
let mut buffer_manager =
|
||||||
output_slices.resize_with(self.main_outputs.lock().len(), || &mut []);
|
BufferManager::for_audio_io_layout(buffer_size as usize, self.audio_io_layout);
|
||||||
})
|
let mut main_output_channel_pointers = ChannelPointerVec(Vec::with_capacity(
|
||||||
}
|
self.audio_io_layout
|
||||||
|
.main_output_channels
|
||||||
// For the inputs we'll need to allocate storage because the NIH-plug API expects all
|
.map(NonZeroU32::get)
|
||||||
// buffers to be mutable, and the jack crate doesn't give us mutable slices on audio input
|
.unwrap_or(0) as usize,
|
||||||
// ports
|
));
|
||||||
let mut aux_input_storage: Vec<Vec<Vec<f32>>> = Vec::new();
|
let mut main_input_channel_pointers = ChannelPointerVec(Vec::with_capacity(
|
||||||
let mut aux_input_buffers: Vec<Buffer> = Vec::new();
|
self.audio_io_layout
|
||||||
|
.main_input_channels
|
||||||
|
.map(NonZeroU32::get)
|
||||||
|
.unwrap_or(0) as usize,
|
||||||
|
));
|
||||||
|
let mut aux_input_channel_pointers =
|
||||||
|
Vec::with_capacity(self.audio_io_layout.aux_input_ports.len());
|
||||||
for channel_count in self.audio_io_layout.aux_input_ports {
|
for channel_count in self.audio_io_layout.aux_input_ports {
|
||||||
aux_input_storage.push(vec![
|
aux_input_channel_pointers.push(ChannelPointerVec(Vec::with_capacity(
|
||||||
vec![0.0f32; self.config.period_size as usize];
|
channel_count.get() as usize,
|
||||||
channel_count.get() as usize
|
)));
|
||||||
]);
|
|
||||||
|
|
||||||
let mut aux_buffer = Buffer::default();
|
|
||||||
unsafe {
|
|
||||||
aux_buffer.set_slices(0, |output_slices| {
|
|
||||||
output_slices.resize_with(channel_count.get() as usize, || &mut []);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
aux_input_buffers.push(aux_buffer);
|
|
||||||
}
|
}
|
||||||
|
let mut aux_output_channel_pointers =
|
||||||
let mut aux_output_buffers: Vec<Buffer> = Vec::new();
|
Vec::with_capacity(self.audio_io_layout.aux_output_ports.len());
|
||||||
for channel_count in self.audio_io_layout.aux_output_ports {
|
for channel_count in self.audio_io_layout.aux_output_ports {
|
||||||
let mut aux_buffer = Buffer::default();
|
aux_output_channel_pointers.push(ChannelPointerVec(Vec::with_capacity(
|
||||||
unsafe {
|
channel_count.get() as usize,
|
||||||
aux_buffer.set_slices(0, |output_slices| {
|
)));
|
||||||
output_slices.resize_with(channel_count.get() as usize, || &mut []);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
aux_output_buffers.push(aux_buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut input_events: Vec<PluginNoteEvent<P>> = Vec::with_capacity(2048);
|
let mut input_events: Vec<PluginNoteEvent<P>> = Vec::with_capacity(2048);
|
||||||
|
@ -142,71 +152,93 @@ impl<P: Plugin> Backend<P> for Jack {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just like all of the plugin backends, we need to grab the output slices and copy the
|
// Just like all of the plugin backends, we need to grab the output slices and copy the
|
||||||
// inputs to the outputs
|
// inputs to the outputs. To do that we need to first create the same kind of `*mut *mut
|
||||||
|
// f32` pointers we would receive from a plugin API.
|
||||||
let mut main_outputs = main_outputs.lock();
|
let mut main_outputs = main_outputs.lock();
|
||||||
for (input, output) in main_inputs.iter().zip(main_outputs.iter_mut()) {
|
main_output_channel_pointers.get().clear();
|
||||||
// XXX: Since the JACK bindings let us do this, presumably these two can't alias,
|
for port in main_outputs.iter_mut() {
|
||||||
// right?
|
let slice = port.as_mut_slice(ps);
|
||||||
output.as_mut_slice(ps).copy_from_slice(input.as_slice(ps));
|
assert!(slice.len() == num_frames as usize);
|
||||||
|
|
||||||
|
main_output_channel_pointers.get().push(slice.as_mut_ptr());
|
||||||
}
|
}
|
||||||
|
|
||||||
// And the buffer's slices need to point to the JACK output ports
|
main_input_channel_pointers.get().clear();
|
||||||
unsafe {
|
for port in main_inputs.iter() {
|
||||||
buffer.set_slices(num_frames as usize, |output_slices| {
|
let slice = port.as_slice(ps);
|
||||||
for (output_slice, output) in
|
assert!(slice.len() == num_frames as usize);
|
||||||
output_slices.iter_mut().zip(main_outputs.iter_mut())
|
|
||||||
{
|
main_input_channel_pointers
|
||||||
// SAFETY: This buffer is only read from after in this callback, and the
|
.get()
|
||||||
// reference passed to `cb` cannot outlive that function call
|
.push(slice.as_ptr() as *mut f32);
|
||||||
*output_slice = &mut *(output.as_mut_slice(ps) as *mut _);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For auxiliary input ports we first need to copy the port data to our input storage
|
let aux_input_ports = aux_input_ports.lock();
|
||||||
// because the NIH-plug API expects every buffer to be mutable
|
for (input_channel_pointers, input_ports) in aux_input_channel_pointers
|
||||||
let mut aux_input_ports = aux_input_ports.lock();
|
|
||||||
for (aux_inputs, (aux_storage, aux_buffer)) in aux_input_ports.iter_mut().zip(
|
|
||||||
aux_input_storage
|
|
||||||
.iter_mut()
|
|
||||||
.zip(aux_input_buffers.iter_mut()),
|
|
||||||
) {
|
|
||||||
for (aux_input, channel) in aux_inputs.iter_mut().zip(aux_storage.iter_mut()) {
|
|
||||||
channel.copy_from_slice(aux_input.as_slice(ps));
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
aux_buffer.set_slices(num_frames as usize, |input_slices| {
|
|
||||||
for (input_slice, channel) in
|
|
||||||
input_slices.iter_mut().zip(aux_storage.iter_mut())
|
|
||||||
{
|
|
||||||
// SAFETY: This buffer is only read from after in this callback, and the
|
|
||||||
// reference passed to `cb` cannot outlive that function call
|
|
||||||
*input_slice = &mut *(channel.as_mut_slice() as *mut _);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can point the buffers for the auxiliary output pots directly at the ports
|
|
||||||
let mut aux_output_ports = aux_output_ports.lock();
|
|
||||||
for (aux_outputs, aux_buffer) in aux_output_ports
|
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.zip(aux_output_buffers.iter_mut())
|
.zip(aux_input_ports.iter())
|
||||||
{
|
{
|
||||||
unsafe {
|
input_channel_pointers.get().clear();
|
||||||
aux_buffer.set_slices(num_frames as usize, |output_slices| {
|
for port in input_ports.iter() {
|
||||||
for (output_slice, channel) in
|
let slice = port.as_slice(ps);
|
||||||
output_slices.iter_mut().zip(aux_outputs.iter_mut())
|
assert!(slice.len() == num_frames as usize);
|
||||||
{
|
|
||||||
// SAFETY: This buffer is only read from after in this callback, and the
|
input_channel_pointers
|
||||||
// reference passed to `cb` cannot outlive that function call
|
.get()
|
||||||
*output_slice = &mut *(channel.as_mut_slice(ps) as *mut _);
|
.push(slice.as_ptr() as *mut f32);
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut aux_output_ports = aux_output_ports.lock();
|
||||||
|
for (output_channel_pointers, output_ports) in aux_output_channel_pointers
|
||||||
|
.iter_mut()
|
||||||
|
.zip(aux_output_ports.iter_mut())
|
||||||
|
{
|
||||||
|
output_channel_pointers.get().clear();
|
||||||
|
for port in output_ports.iter_mut() {
|
||||||
|
let slice = port.as_mut_slice(ps);
|
||||||
|
assert!(slice.len() == num_frames as usize);
|
||||||
|
|
||||||
|
output_channel_pointers.get().push(slice.as_mut_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffers = unsafe {
|
||||||
|
buffer_manager.create_buffers(num_frames as usize, |buffer_sources| {
|
||||||
|
*buffer_sources.main_output_channel_pointers = Some(ChannelPointers {
|
||||||
|
ptrs: NonNull::new(main_output_channel_pointers.get().as_mut_ptr())
|
||||||
|
.unwrap(),
|
||||||
|
num_channels: main_output_channel_pointers.get().len(),
|
||||||
|
});
|
||||||
|
*buffer_sources.main_input_channel_pointers = Some(ChannelPointers {
|
||||||
|
ptrs: NonNull::new(main_input_channel_pointers.get().as_mut_ptr()).unwrap(),
|
||||||
|
num_channels: main_input_channel_pointers.get().len(),
|
||||||
|
});
|
||||||
|
|
||||||
|
for (input_source_channel_pointers, input_channel_pointers) in buffer_sources
|
||||||
|
.aux_input_channel_pointers
|
||||||
|
.iter_mut()
|
||||||
|
.zip(aux_input_channel_pointers.iter_mut())
|
||||||
|
{
|
||||||
|
*input_source_channel_pointers = Some(ChannelPointers {
|
||||||
|
ptrs: NonNull::new(input_channel_pointers.get().as_mut_ptr()).unwrap(),
|
||||||
|
num_channels: input_channel_pointers.get().len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (output_source_channel_pointers, output_channel_pointers) in buffer_sources
|
||||||
|
.aux_output_channel_pointers
|
||||||
|
.iter_mut()
|
||||||
|
.zip(aux_output_channel_pointers.iter_mut())
|
||||||
|
{
|
||||||
|
*output_source_channel_pointers = Some(ChannelPointers {
|
||||||
|
ptrs: NonNull::new(output_channel_pointers.get().as_mut_ptr()).unwrap(),
|
||||||
|
num_channels: output_channel_pointers.get().len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
input_events.clear();
|
input_events.clear();
|
||||||
if let Some(midi_input) = &midi_input {
|
if let Some(midi_input) = &midi_input {
|
||||||
input_events.extend(midi_input.iter(ps).filter_map(|midi| {
|
input_events.extend(midi_input.iter(ps).filter_map(|midi| {
|
||||||
|
@ -216,19 +248,13 @@ impl<P: Plugin> Backend<P> for Jack {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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]),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
output_events.clear();
|
output_events.clear();
|
||||||
|
let mut aux = AuxiliaryBuffers {
|
||||||
|
inputs: buffers.aux_inputs,
|
||||||
|
outputs: buffers.aux_outputs,
|
||||||
|
};
|
||||||
if cb(
|
if cb(
|
||||||
&mut buffer,
|
buffers.main_buffer,
|
||||||
&mut aux,
|
&mut aux,
|
||||||
transport,
|
transport,
|
||||||
&input_events,
|
&input_events,
|
||||||
|
|
Loading…
Reference in a new issue