Use new buffer management for the dummy backend
This commit is contained in:
parent
ca4569e03a
commit
9d45cbf1d9
|
@ -1,4 +1,5 @@
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
|
use std::ptr::NonNull;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use super::super::config::WrapperConfig;
|
use super::super::config::WrapperConfig;
|
||||||
|
@ -8,6 +9,7 @@ use crate::buffer::Buffer;
|
||||||
use crate::context::process::Transport;
|
use crate::context::process::Transport;
|
||||||
use crate::midi::PluginNoteEvent;
|
use crate::midi::PluginNoteEvent;
|
||||||
use crate::plugin::Plugin;
|
use crate::plugin::Plugin;
|
||||||
|
use crate::wrapper::util::buffer_management::{BufferManager, ChannelPointers};
|
||||||
|
|
||||||
/// This backend doesn't input or output any audio or MIDI. It only exists so the standalone
|
/// 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
|
/// application can continue to run even when there is no audio backend available. This can be
|
||||||
|
@ -31,114 +33,139 @@ impl<P: Plugin> Backend<P> for Dummy {
|
||||||
+ Send,
|
+ Send,
|
||||||
) {
|
) {
|
||||||
// We can't really do anything meaningful here, so we'll simply periodically call the
|
// We can't really do anything meaningful here, so we'll simply periodically call the
|
||||||
// callback with empty buffers.
|
// callback with empty buffers
|
||||||
let interval =
|
let interval =
|
||||||
Duration::from_secs_f32(self.config.period_size as f32 / self.config.sample_rate);
|
Duration::from_secs_f32(self.config.period_size as f32 / self.config.sample_rate);
|
||||||
|
|
||||||
|
let num_samples = self.config.period_size as usize;
|
||||||
let num_output_channels = self
|
let num_output_channels = self
|
||||||
.audio_io_layout
|
.audio_io_layout
|
||||||
.main_output_channels
|
.main_output_channels
|
||||||
.map(NonZeroU32::get)
|
.map(NonZeroU32::get)
|
||||||
.unwrap_or_default() as usize;
|
.unwrap_or_default() as usize;
|
||||||
let mut channels =
|
let num_input_channels = self
|
||||||
vec![vec![0.0f32; self.config.period_size as usize]; num_output_channels];
|
.audio_io_layout
|
||||||
let mut buffer = Buffer::default();
|
.main_input_channels
|
||||||
unsafe {
|
.map(NonZeroU32::get)
|
||||||
buffer.set_slices(self.config.period_size as usize, |output_slices| {
|
.unwrap_or_default() as usize;
|
||||||
// SAFETY: `channels` is no longer used directly after this
|
let mut main_io_storage = vec![vec![0.0f32; num_samples]; num_output_channels];
|
||||||
*output_slices = channels
|
|
||||||
.iter_mut()
|
|
||||||
.map(|channel| &mut *(channel.as_mut_slice() as *mut [f32]))
|
|
||||||
.collect();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// We'll do the same thing for auxiliary inputs and outputs, so the plugin always gets the
|
// We'll do the same thing for auxiliary inputs and outputs, so the plugin always gets the
|
||||||
// buffers it expects
|
// buffers it expects
|
||||||
let mut aux_input_storage: Vec<Vec<Vec<f32>>> = Vec::new();
|
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 {
|
for channel_count in self.audio_io_layout.aux_input_ports {
|
||||||
aux_input_storage.push(vec![
|
aux_input_storage.push(vec![
|
||||||
vec![0.0f32; self.config.period_size as usize];
|
vec![0.0f32; num_samples];
|
||||||
channel_count.get() 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();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
aux_input_buffers.push(aux_buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut aux_output_storage: Vec<Vec<Vec<f32>>> = Vec::new();
|
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 {
|
for channel_count in self.audio_io_layout.aux_output_ports {
|
||||||
aux_output_storage.push(vec![
|
aux_output_storage.push(vec![
|
||||||
vec![0.0f32; self.config.period_size as usize];
|
vec![0.0f32; num_samples];
|
||||||
channel_count.get() 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();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
aux_output_buffers.push(aux_buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We need pointers to this storage to emulate the API used by plugins
|
||||||
|
let mut main_io_channel_pointers: Vec<*mut f32> = main_io_storage
|
||||||
|
.iter_mut()
|
||||||
|
.map(|channel_slice| channel_slice.as_mut_ptr())
|
||||||
|
.collect();
|
||||||
|
let mut aux_input_channel_pointers: Vec<Vec<*mut f32>> = aux_input_storage
|
||||||
|
.iter_mut()
|
||||||
|
.map(|aux_input_storage| {
|
||||||
|
aux_input_storage
|
||||||
|
.iter_mut()
|
||||||
|
.map(|channel_slice| channel_slice.as_mut_ptr())
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let mut aux_output_channel_pointers: Vec<Vec<*mut f32>> = aux_output_storage
|
||||||
|
.iter_mut()
|
||||||
|
.map(|aux_output_storage| {
|
||||||
|
aux_output_storage
|
||||||
|
.iter_mut()
|
||||||
|
.map(|channel_slice| channel_slice.as_mut_ptr())
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// The `BufferManager` can then manage buffers using this storage just like in every other
|
||||||
|
// backend
|
||||||
|
let mut buffer_manager =
|
||||||
|
BufferManager::for_audio_io_layout(num_samples, self.audio_io_layout);
|
||||||
|
|
||||||
// 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 = 0usize;
|
||||||
loop {
|
loop {
|
||||||
let period_start = Instant::now();
|
let period_start = Instant::now();
|
||||||
|
|
||||||
let mut transport = Transport::new(self.config.sample_rate);
|
let mut transport = Transport::new(self.config.sample_rate);
|
||||||
transport.pos_samples = Some(num_processed_samples);
|
transport.pos_samples = Some(num_processed_samples as i64);
|
||||||
transport.tempo = Some(self.config.tempo as f64);
|
transport.tempo = Some(self.config.tempo as f64);
|
||||||
transport.time_sig_numerator = Some(self.config.timesig_num as i32);
|
transport.time_sig_numerator = Some(self.config.timesig_num as i32);
|
||||||
transport.time_sig_denominator = Some(self.config.timesig_denom as i32);
|
transport.time_sig_denominator = Some(self.config.timesig_denom as i32);
|
||||||
transport.playing = true;
|
transport.playing = true;
|
||||||
|
|
||||||
for channel in buffer.as_slice() {
|
for channel in &mut main_io_storage {
|
||||||
channel.fill(0.0);
|
channel.fill(0.0);
|
||||||
}
|
}
|
||||||
for aux_buffer in &mut aux_input_buffers {
|
for aux_buffer in &mut aux_input_storage {
|
||||||
for channel in aux_buffer.as_slice() {
|
for channel in aux_buffer {
|
||||||
channel.fill(0.0);
|
channel.fill(0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for aux_buffer in &mut aux_output_buffers {
|
for aux_buffer in &mut aux_output_storage {
|
||||||
for channel in aux_buffer.as_slice() {
|
for channel in aux_buffer {
|
||||||
channel.fill(0.0);
|
channel.fill(0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: Shortening these borrows is safe as even if the plugin overwrites the
|
let buffers = unsafe {
|
||||||
// slices (which it cannot do without using unsafe code), then they
|
buffer_manager.create_buffers(num_samples, |buffer_sources| {
|
||||||
// would still be reset on the next iteration
|
*buffer_sources.main_output_channel_pointers = Some(ChannelPointers {
|
||||||
let mut aux = unsafe {
|
ptrs: NonNull::new(main_io_channel_pointers.as_mut_ptr()).unwrap(),
|
||||||
AuxiliaryBuffers {
|
num_channels: main_io_channel_pointers.len(),
|
||||||
inputs: &mut *(aux_input_buffers.as_mut_slice() as *mut [Buffer]),
|
});
|
||||||
outputs: &mut *(aux_output_buffers.as_mut_slice() as *mut [Buffer]),
|
*buffer_sources.main_input_channel_pointers = Some(ChannelPointers {
|
||||||
}
|
ptrs: NonNull::new(main_io_channel_pointers.as_mut_ptr()).unwrap(),
|
||||||
|
num_channels: num_input_channels.min(main_io_channel_pointers.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.as_mut_ptr()).unwrap(),
|
||||||
|
num_channels: input_channel_pointers.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.as_mut_ptr()).unwrap(),
|
||||||
|
num_channels: output_channel_pointers.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
midi_output_events.clear();
|
midi_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,
|
||||||
&[],
|
&[],
|
||||||
|
@ -147,7 +174,7 @@ impl<P: Plugin> Backend<P> for Dummy {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
num_processed_samples += buffer.samples() as i64;
|
num_processed_samples += num_samples;
|
||||||
|
|
||||||
let period_end = Instant::now();
|
let period_end = Instant::now();
|
||||||
std::thread::sleep((period_start + interval).saturating_duration_since(period_end));
|
std::thread::sleep((period_start + interval).saturating_duration_since(period_end));
|
||||||
|
|
Loading…
Reference in a new issue