Support CLAP transport information events
This will cause the buffer to be split when the transport information changes.
This commit is contained in:
parent
7cb671319e
commit
a74d8264fb
1 changed files with 92 additions and 48 deletions
|
@ -6,13 +6,14 @@ use atomic_float::AtomicF32;
|
||||||
use atomic_refcell::{AtomicRefCell, AtomicRefMut};
|
use atomic_refcell::{AtomicRefCell, AtomicRefMut};
|
||||||
use clap_sys::events::{
|
use clap_sys::events::{
|
||||||
clap_event_header, clap_event_midi, clap_event_note, clap_event_note_expression,
|
clap_event_header, clap_event_midi, clap_event_note, clap_event_note_expression,
|
||||||
clap_event_param_gesture, clap_event_param_mod, clap_event_param_value, clap_event_type,
|
clap_event_param_gesture, clap_event_param_mod, clap_event_param_value, clap_event_transport,
|
||||||
clap_input_events, clap_output_events, CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_IS_LIVE,
|
clap_event_type, clap_input_events, clap_output_events, CLAP_CORE_EVENT_SPACE_ID,
|
||||||
CLAP_EVENT_MIDI, CLAP_EVENT_NOTE_EXPRESSION, CLAP_EVENT_NOTE_OFF, CLAP_EVENT_NOTE_ON,
|
CLAP_EVENT_IS_LIVE, CLAP_EVENT_MIDI, CLAP_EVENT_NOTE_EXPRESSION, CLAP_EVENT_NOTE_OFF,
|
||||||
CLAP_EVENT_PARAM_GESTURE_BEGIN, CLAP_EVENT_PARAM_GESTURE_END, CLAP_EVENT_PARAM_MOD,
|
CLAP_EVENT_NOTE_ON, CLAP_EVENT_PARAM_GESTURE_BEGIN, CLAP_EVENT_PARAM_GESTURE_END,
|
||||||
CLAP_EVENT_PARAM_VALUE, CLAP_NOTE_EXPRESSION_BRIGHTNESS, CLAP_NOTE_EXPRESSION_EXPRESSION,
|
CLAP_EVENT_PARAM_MOD, CLAP_EVENT_PARAM_VALUE, CLAP_EVENT_TRANSPORT,
|
||||||
CLAP_NOTE_EXPRESSION_PAN, CLAP_NOTE_EXPRESSION_PRESSURE, CLAP_NOTE_EXPRESSION_TUNING,
|
CLAP_NOTE_EXPRESSION_BRIGHTNESS, CLAP_NOTE_EXPRESSION_EXPRESSION, CLAP_NOTE_EXPRESSION_PAN,
|
||||||
CLAP_NOTE_EXPRESSION_VIBRATO, CLAP_NOTE_EXPRESSION_VOLUME, CLAP_TRANSPORT_HAS_BEATS_TIMELINE,
|
CLAP_NOTE_EXPRESSION_PRESSURE, CLAP_NOTE_EXPRESSION_TUNING, CLAP_NOTE_EXPRESSION_VIBRATO,
|
||||||
|
CLAP_NOTE_EXPRESSION_VOLUME, CLAP_TRANSPORT_HAS_BEATS_TIMELINE,
|
||||||
CLAP_TRANSPORT_HAS_SECONDS_TIMELINE, CLAP_TRANSPORT_HAS_TEMPO,
|
CLAP_TRANSPORT_HAS_SECONDS_TIMELINE, CLAP_TRANSPORT_HAS_TEMPO,
|
||||||
CLAP_TRANSPORT_HAS_TIME_SIGNATURE, CLAP_TRANSPORT_IS_LOOP_ACTIVE, CLAP_TRANSPORT_IS_PLAYING,
|
CLAP_TRANSPORT_HAS_TIME_SIGNATURE, CLAP_TRANSPORT_IS_LOOP_ACTIVE, CLAP_TRANSPORT_IS_PLAYING,
|
||||||
CLAP_TRANSPORT_IS_RECORDING, CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL,
|
CLAP_TRANSPORT_IS_RECORDING, CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL,
|
||||||
|
@ -706,7 +707,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
for event_idx in 0..num_events {
|
for event_idx in 0..num_events {
|
||||||
let event = ((*in_).get)(&*in_, event_idx);
|
let event = ((*in_).get)(&*in_, event_idx);
|
||||||
parameter_values_changed |=
|
parameter_values_changed |=
|
||||||
self.handle_in_event(event, &mut input_events, current_sample_idx);
|
self.handle_in_event(event, &mut input_events, None, current_sample_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow the GUI to react to any parameter values that might have been changed
|
// Allow the GUI to react to any parameter values that might have been changed
|
||||||
|
@ -715,17 +716,22 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Similar to [`handle_in_events()`][Self::handle_in_events()], but will stop just before the
|
/// Similar to [`handle_in_events()`][Self::handle_in_events()], but will stop just before an
|
||||||
/// next parameter change event with `raw_event.time > current_sample_idx` and return the
|
/// event if the preducate returns true for that events. This predicate is only called for
|
||||||
/// **absolute** (relative to the entire buffer that's being split) sample index of that event
|
/// events that occur after `current_sample_idx`. This is used to stop before a tempo or time
|
||||||
/// along with the its index in the event queue as a `(sample_idx, event_idx)` tuple. This
|
/// signature change, or before next parameter change event with `raw_event.time >
|
||||||
/// allows for splitting the audio buffer into segments with distinct sample values to enable
|
/// current_sample_idx` and return the **absolute** (relative to the entire buffer that's being
|
||||||
/// sample accurate automation without modifcations to the wrapped plugin.
|
/// split) sample index of that event along with the its index in the event queue as a
|
||||||
pub unsafe fn handle_in_events_until_next_param_change(
|
/// `(sample_idx, event_idx)` tuple. This allows for splitting the audio buffer into segments
|
||||||
|
/// with distinct sample values to enable sample accurate automation without modifcations to the
|
||||||
|
/// wrapped plugin.
|
||||||
|
pub unsafe fn handle_in_events_until(
|
||||||
&self,
|
&self,
|
||||||
in_: &clap_input_events,
|
in_: &clap_input_events,
|
||||||
|
transport_info: &mut *const clap_event_transport,
|
||||||
current_sample_idx: usize,
|
current_sample_idx: usize,
|
||||||
resume_from_event_idx: usize,
|
resume_from_event_idx: usize,
|
||||||
|
stop_predicate: impl Fn(*const clap_event_header) -> bool,
|
||||||
) -> Option<(usize, usize)> {
|
) -> Option<(usize, usize)> {
|
||||||
let mut input_events = self.input_events.borrow_mut();
|
let mut input_events = self.input_events.borrow_mut();
|
||||||
input_events.clear();
|
input_events.clear();
|
||||||
|
@ -740,27 +746,30 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
let mut event: *const clap_event_header = ((*in_).get)(&*in_, start_idx);
|
let mut event: *const clap_event_header = ((*in_).get)(&*in_, start_idx);
|
||||||
let mut parameter_values_changed = false;
|
let mut parameter_values_changed = false;
|
||||||
for next_event_idx in (start_idx + 1)..num_events {
|
for next_event_idx in (start_idx + 1)..num_events {
|
||||||
parameter_values_changed |=
|
parameter_values_changed |= self.handle_in_event(
|
||||||
self.handle_in_event(event, &mut input_events, current_sample_idx);
|
event,
|
||||||
|
&mut input_events,
|
||||||
|
Some(transport_info),
|
||||||
|
current_sample_idx,
|
||||||
|
);
|
||||||
|
|
||||||
// Stop just before the next parameter change event at a sample after the current sample
|
// Stop just before the next parameter change or transport information event at a sample
|
||||||
|
// after the current sample
|
||||||
let next_event: *const clap_event_header = ((*in_).get)(&*in_, next_event_idx);
|
let next_event: *const clap_event_header = ((*in_).get)(&*in_, next_event_idx);
|
||||||
match ((*next_event).space_id, (*next_event).type_) {
|
if (*next_event).time > current_sample_idx as u32 && stop_predicate(next_event) {
|
||||||
(CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_VALUE)
|
return Some(((*next_event).time as usize, next_event_idx as usize));
|
||||||
| (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_MOD)
|
|
||||||
if (*next_event).time > current_sample_idx as u32 =>
|
|
||||||
{
|
|
||||||
return Some(((*next_event).time as usize, next_event_idx as usize));
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
event = next_event;
|
event = next_event;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't forget about the last event
|
// Don't forget about the last event
|
||||||
parameter_values_changed |=
|
parameter_values_changed |= self.handle_in_event(
|
||||||
self.handle_in_event(event, &mut input_events, current_sample_idx);
|
event,
|
||||||
|
&mut input_events,
|
||||||
|
Some(transport_info),
|
||||||
|
current_sample_idx,
|
||||||
|
);
|
||||||
|
|
||||||
// NOTE: We explicitly did not do this on a block split because that seems a bit excessive.
|
// NOTE: We explicitly did not do this on a block split because that seems a bit excessive.
|
||||||
// When we're performing a block split we're guarenteed that there's still at least one more
|
// When we're performing a block split we're guarenteed that there's still at least one more
|
||||||
|
@ -1154,6 +1163,9 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
/// To save on mutex operations when handing MIDI events, the lock guard for the input events
|
/// To save on mutex operations when handing MIDI events, the lock guard for the input events
|
||||||
/// need to be passed into this function.
|
/// need to be passed into this function.
|
||||||
///
|
///
|
||||||
|
/// If the event was a transport event and the `transport_info` argument is not `None`, then the
|
||||||
|
/// pointer will be changed to point to the transport information from this event.
|
||||||
|
///
|
||||||
/// The return value indicates whether this was a parameter event. If it is a parameter event,
|
/// The return value indicates whether this was a parameter event. If it is a parameter event,
|
||||||
/// then [`notify_param_values_changed()`][Self::notify_param_values_changed()] should be called
|
/// then [`notify_param_values_changed()`][Self::notify_param_values_changed()] should be called
|
||||||
/// once all of these events have been processed.
|
/// once all of these events have been processed.
|
||||||
|
@ -1162,6 +1174,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
&self,
|
&self,
|
||||||
event: *const clap_event_header,
|
event: *const clap_event_header,
|
||||||
input_events: &mut AtomicRefMut<VecDeque<NoteEvent>>,
|
input_events: &mut AtomicRefMut<VecDeque<NoteEvent>>,
|
||||||
|
transport_info: Option<&mut *const clap_event_transport>,
|
||||||
current_sample_idx: usize,
|
current_sample_idx: usize,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let raw_event = &*event;
|
let raw_event = &*event;
|
||||||
|
@ -1186,6 +1199,14 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
(CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_TRANSPORT) => {
|
||||||
|
let event = &*(event as *const clap_event_transport);
|
||||||
|
if let Some(transport_info) = transport_info {
|
||||||
|
*transport_info = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
(CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_ON) => {
|
(CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_ON) => {
|
||||||
if P::MIDI_INPUT >= MidiConfig::Basic {
|
if P::MIDI_INPUT >= MidiConfig::Basic {
|
||||||
let event = &*(event as *const clap_event_note);
|
let event = &*(event as *const clap_event_note);
|
||||||
|
@ -1567,27 +1588,49 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
let mut block_start = 0;
|
let mut block_start = 0;
|
||||||
let mut block_end = process.frames_count as usize;
|
let mut block_end = process.frames_count as usize;
|
||||||
let mut event_start_idx = 0;
|
let mut event_start_idx = 0;
|
||||||
|
|
||||||
|
// The host may send new transport information as an event. In that case we'll also
|
||||||
|
// split the buffer.
|
||||||
|
let mut transport_info = process.transport;
|
||||||
|
|
||||||
let result = loop {
|
let result = loop {
|
||||||
if !process.in_events.is_null() {
|
if !process.in_events.is_null() {
|
||||||
if P::SAMPLE_ACCURATE_AUTOMATION {
|
let split_result = wrapper.handle_in_events_until(
|
||||||
let split_result = wrapper.handle_in_events_until_next_param_change(
|
&*process.in_events,
|
||||||
&*process.in_events,
|
&mut transport_info,
|
||||||
block_start,
|
block_start,
|
||||||
event_start_idx,
|
event_start_idx,
|
||||||
);
|
|next_event| {
|
||||||
|
// Always split the buffer on transport information changes (tempo, time
|
||||||
// If there are any parameter changes after `block_start`, then we'll do a
|
// signature, or position changes), and also split on parameter value
|
||||||
// new block just after that. Otherwise we can process all audio until the
|
// changes after the current sample if sample accurate automation is
|
||||||
// end of the buffer.
|
// enabled
|
||||||
match split_result {
|
if P::SAMPLE_ACCURATE_AUTOMATION {
|
||||||
Some((next_param_change_sample_idx, next_param_change_event_idx)) => {
|
matches!(
|
||||||
block_end = next_param_change_sample_idx as usize;
|
((*next_event).space_id, (*next_event).type_,),
|
||||||
event_start_idx = next_param_change_event_idx as usize;
|
(CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_VALUE)
|
||||||
|
| (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_MOD)
|
||||||
|
| (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_TRANSPORT)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
matches!(
|
||||||
|
((*next_event).space_id, (*next_event).type_,),
|
||||||
|
(CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_TRANSPORT)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
None => block_end = process.frames_count as usize,
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// If there are any parameter changes after `block_start` and sample
|
||||||
|
// accurate automatoin is enabled or the host sends new transport
|
||||||
|
// inforamtion, then we'll process a new block just after that. Otherwise we can
|
||||||
|
// process all audio until the end of the buffer.
|
||||||
|
match split_result {
|
||||||
|
Some((next_param_change_sample_idx, next_param_change_event_idx)) => {
|
||||||
|
block_end = next_param_change_sample_idx as usize;
|
||||||
|
event_start_idx = next_param_change_event_idx as usize;
|
||||||
}
|
}
|
||||||
} else {
|
None => block_end = process.frames_count as usize,
|
||||||
wrapper.handle_in_events(&*process.in_events, block_start);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1686,8 +1729,8 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
.expect("Process call without prior initialization call")
|
.expect("Process call without prior initialization call")
|
||||||
.sample_rate;
|
.sample_rate;
|
||||||
let mut transport = Transport::new(sample_rate);
|
let mut transport = Transport::new(sample_rate);
|
||||||
if !process.transport.is_null() {
|
if !transport_info.is_null() {
|
||||||
let context = &*process.transport;
|
let context = &*transport_info;
|
||||||
|
|
||||||
transport.playing = context.flags & CLAP_TRANSPORT_IS_PLAYING != 0;
|
transport.playing = context.flags & CLAP_TRANSPORT_IS_PLAYING != 0;
|
||||||
transport.recording = context.flags & CLAP_TRANSPORT_IS_RECORDING != 0;
|
transport.recording = context.flags & CLAP_TRANSPORT_IS_RECORDING != 0;
|
||||||
|
@ -2083,7 +2126,8 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match (space_id, event_type) {
|
match (space_id, event_type) {
|
||||||
(CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_VALUE)
|
(CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_VALUE)
|
||||||
| (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_MOD) => true,
|
| (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_MOD)
|
||||||
|
| (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_TRANSPORT) => true,
|
||||||
(CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_ON)
|
(CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_ON)
|
||||||
| (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_OFF)
|
| (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_OFF)
|
||||||
| (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_EXPRESSION)
|
| (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_EXPRESSION)
|
||||||
|
|
Loading…
Add table
Reference in a new issue