Skip to content

Commit

Permalink
introduce Processor::flush method
Browse files Browse the repository at this point in the history
Introduce a `Processor::flush` method for handling events without a
corresponding set of audio buffers. Update the VST3 and CLAP backends to
call `flush` wherever they currently call `process` with a zero-length
buffer.
  • Loading branch information
micahrj committed Sep 7, 2024
1 parent 7c7acfd commit 852eb44
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 37 deletions.
18 changes: 15 additions & 3 deletions examples/gain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,16 +113,28 @@ pub struct GainProcessor {
params: GainParams,
}

impl GainProcessor {
fn handle_event(&mut self, event: &Event) {
if let Data::ParamChange { id, value } = event.data {
self.params.set_param(id, value);
}
}
}

impl Processor for GainProcessor {
fn reset(&mut self) {}

fn flush(&mut self, events: Events) {
for event in events {
self.handle_event(event);
}
}

fn process(&mut self, buffers: Buffers, events: Events) {
let mut buffers: (BufferMut,) = buffers.try_into().unwrap();
for (mut buffer, events) in buffers.0.split_at_events(events) {
for event in events {
if let Data::ParamChange { id, value } = event.data {
self.params.set_param(id, value);
}
self.handle_event(event);
}

for sample in buffer.samples() {
Expand Down
23 changes: 2 additions & 21 deletions src/format/clap/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,16 +394,7 @@ impl<P: Plugin> Instance<P> {
instance.sync_processor(&mut process_state.events);

if !process_state.events.is_empty() {
process_state.buffer_ptrs.fill(NonNull::dangling().as_ptr());
processor.process(
Buffers::from_raw_parts(
&process_state.buffer_data,
&process_state.buffer_ptrs,
0,
0,
),
Events::new(&process_state.events),
);
processor.flush(Events::new(&process_state.events));
}

processor.reset();
Expand Down Expand Up @@ -810,8 +801,6 @@ impl<P: Plugin> Instance<P> {

// If we are in the active state, flush will be called on the audio thread.
if let Some(processor) = &mut process_state.processor {
process_state.buffer_ptrs.fill(NonNull::dangling().as_ptr());

process_state.events.clear();
instance.sync_processor(&mut process_state.events);
instance.process_param_events(in_, &mut process_state.events);
Expand All @@ -822,15 +811,7 @@ impl<P: Plugin> Instance<P> {
0,
);

processor.process(
Buffers::from_raw_parts(
&process_state.buffer_data,
&process_state.buffer_ptrs,
0,
0,
),
Events::new(&process_state.events),
);
processor.flush(Events::new(&process_state.events));
}
// Otherwise, flush will be called on the main thread.
else {
Expand Down
1 change: 1 addition & 0 deletions src/format/clap/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ struct TestProcessor;

impl Processor for TestProcessor {
fn reset(&mut self) {}
fn flush(&mut self, _events: Events) {}
fn process(&mut self, _buffers: Buffers, _events: Events) {}
}

Expand Down
25 changes: 17 additions & 8 deletions src/format/vst3/buffers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,26 @@ impl ScratchBuffers {
self.moves.reserve(in_out_channels);
}

/// Set up buffer pointers for the processor given a VST3 `ProcessData` struct.
///
/// This method is responsible for detecting if any of the buffers for an input bus are aliased
/// by a output buffer and, if so, copying those inputs to scratch buffers. It is also
/// responsible for detecting if separate input and output buffers have been passed for an
/// in-out bus and copying those inputs to the corresponding outputs.
///
/// This method will return `Err` if the channel counts do not match the current layout or if
/// the buffer's length exceeds the maximum buffer size. It will return `Ok(None)` if the
/// buffer's length is 0, as hosts are not guaranteed to provide the correct number of inputs
/// and outputs in that case, and we don't need to construct a `Buffers` object as we will be
/// calling `Processor::flush` instead of `Processor::process`.
pub unsafe fn get_buffers(
&mut self,
buses: &[BusInfo],
input_bus_map: &[usize],
output_bus_map: &[usize],
config: &Config,
data: &ProcessData,
) -> Result<Buffers, ()> {
) -> Result<Option<Buffers>, ()> {
let len = data.numSamples as usize;
if len > config.max_buffer_size {
return Err(());
Expand All @@ -103,7 +115,7 @@ impl ScratchBuffers {
let mut scratch = &mut self.buffers[..];

if len == 0 {
return Ok(self.get_empty_buffers());
return Ok(None);
}

let input_count = data.numInputs as usize;
Expand Down Expand Up @@ -242,11 +254,8 @@ impl ScratchBuffers {

self.output_ptrs.clear();

Ok(Buffers::from_raw_parts(&self.data, &self.ptrs, 0, len))
}

pub fn get_empty_buffers(&mut self) -> Buffers {
self.ptrs.fill(NonNull::dangling().as_ptr());
unsafe { Buffers::from_raw_parts(&self.data, &self.ptrs, 0, 0) }
Ok(Some(Buffers::from_raw_parts(
&self.data, &self.ptrs, 0, len,
)))
}
}
12 changes: 7 additions & 5 deletions src/format/vst3/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,10 +487,7 @@ impl<P: Plugin> IAudioProcessorTrait for Component<P> {
}

if !process_state.events.is_empty() {
processor.process(
process_state.scratch_buffers.get_empty_buffers(),
Events::new(&process_state.events),
);
processor.flush(Events::new(&process_state.events));
}

processor.reset();
Expand Down Expand Up @@ -563,7 +560,12 @@ impl<P: Plugin> IAudioProcessorTrait for Component<P> {
}
}

processor.process(buffers, Events::new(&process_state.events));
let events = Events::new(&process_state.events);
if let Some(buffers) = buffers {
processor.process(buffers, events);
} else {
processor.flush(events);
}

kResultOk
}
Expand Down
1 change: 1 addition & 0 deletions src/format/vst3/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ struct TestProcessor;

impl Processor for TestProcessor {
fn reset(&mut self) {}
fn flush(&mut self, _events: Events) {}
fn process(&mut self, _buffers: Buffers, _events: Events) {}
}

Expand Down
1 change: 1 addition & 0 deletions src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ pub struct Config {

pub trait Processor: Send + Sized + 'static {
fn reset(&mut self);
fn flush(&mut self, events: Events);
fn process(&mut self, buffers: Buffers, events: Events);
}

0 comments on commit 852eb44

Please sign in to comment.