1
0
Fork 0

Implement automation gestures for CLAP

This commit is contained in:
Robbert van der Helm 2022-03-11 00:14:39 +01:00
parent 5d3527c5c2
commit 246c319bbf
2 changed files with 65 additions and 12 deletions

View file

@ -8,6 +8,7 @@ use crate::context::{GuiContext, ProcessContext, Transport};
use crate::event_loop::EventLoop;
use crate::param::internals::ParamPtr;
use crate::plugin::{ClapPlugin, NoteEvent};
use crate::wrapper::clap::wrapper::OutputParamChangeType;
/// A [`GuiContext`] implementation for the wrapper. This is passed to the plugin in
/// [`Editor::spawn()`][crate::prelude::Editor::spawn()] so it can interact with the rest of the plugin and
@ -28,10 +29,23 @@ pub(crate) struct WrapperProcessContext<'a, P: ClapPlugin> {
impl<P: ClapPlugin> GuiContext for WrapperGuiContext<P> {
// All of these functions are supposed to be called from the main thread, so we'll put some
// trust in the caller and assume that this is indeed the case
unsafe fn raw_begin_set_parameter(&self, _param: ParamPtr) {
// TODO: Parameter event gestures are a bit weird in CLAP right now because they're
// implemented as flags on events, and you don't know when a gesture ends before it
// has ended. Implement this once that's a bit clearer.
unsafe fn raw_begin_set_parameter(&self, param: ParamPtr) {
match self.wrapper.param_ptr_to_hash.get(&param) {
Some(hash) => {
// Apparently you're supposed to resend old values for the automation begin and end
// gestures
let clap_plain_value =
param.normalized_value() as f64 * param.step_count().unwrap_or(1) as f64;
let success = self.wrapper.queue_parameter_change(OutputParamChange {
param_hash: *hash,
clap_plain_value,
change_type: OutputParamChangeType::BeginGesture,
});
nih_debug_assert!(success, "Parameter output queue was full, parameter change will not be sent to the host");
}
None => nih_debug_assert_failure!("Unknown parameter: {:?}", param),
}
}
unsafe fn raw_set_parameter_normalized(&self, param: ParamPtr, normalized: f32) {
@ -46,6 +60,7 @@ impl<P: ClapPlugin> GuiContext for WrapperGuiContext<P> {
let success = self.wrapper.queue_parameter_change(OutputParamChange {
param_hash: *hash,
clap_plain_value,
change_type: OutputParamChangeType::Normal,
});
nih_debug_assert!(success, "Parameter output queue was full, parameter change will not be sent to the host");
@ -54,10 +69,23 @@ impl<P: ClapPlugin> GuiContext for WrapperGuiContext<P> {
}
}
unsafe fn raw_end_set_parameter(&self, _param: ParamPtr) {
// TODO: Parameter event gestures are a bit weird in CLAP right now because they're
// implemented as flags on events, and you don't know when a gesture ends before it
// has ended. Implement this once that's a bit clearer.
unsafe fn raw_end_set_parameter(&self, param: ParamPtr) {
match self.wrapper.param_ptr_to_hash.get(&param) {
Some(hash) => {
// Apparently you're supposed to resend old values for the automation begin and end
// gestures
let clap_plain_value =
param.normalized_value() as f64 * param.step_count().unwrap_or(1) as f64;
let success = self.wrapper.queue_parameter_change(OutputParamChange {
param_hash: *hash,
clap_plain_value,
change_type: OutputParamChangeType::EndGesture,
});
nih_debug_assert!(success, "Parameter output queue was full, parameter change will not be sent to the host");
}
None => nih_debug_assert_failure!("Unknown parameter: {:?}", param),
}
}
unsafe fn raw_default_normalized_param_value(&self, param: ParamPtr) -> f32 {

View file

@ -6,9 +6,10 @@ use atomic_float::AtomicF32;
use atomic_refcell::{AtomicRefCell, AtomicRefMut};
use clap_sys::events::{
clap_event_header, clap_event_note, clap_event_param_mod, clap_event_param_value,
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_PARAM_MOD,
CLAP_EVENT_PARAM_VALUE, CLAP_EVENT_SHOULD_RECORD, CLAP_TRANSPORT_HAS_BEATS_TIMELINE,
clap_input_events, clap_output_events, CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_BEGIN_ADJUST,
CLAP_EVENT_END_ADJUST, CLAP_EVENT_IS_LIVE, CLAP_EVENT_MIDI, CLAP_EVENT_NOTE_EXPRESSION,
CLAP_EVENT_NOTE_OFF, CLAP_EVENT_NOTE_ON, CLAP_EVENT_PARAM_MOD, CLAP_EVENT_PARAM_VALUE,
CLAP_EVENT_SHOULD_RECORD, CLAP_TRANSPORT_HAS_BEATS_TIMELINE,
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_IS_RECORDING, CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL,
@ -226,6 +227,19 @@ pub struct OutputParamChange {
/// The 'plain' value as reported to CLAP. This is the normalized value multiplied by
/// [`Param::step_size()`][crate::Param::step_size()].
pub clap_plain_value: f64,
/// The kind of parameter change event, since CLAP wants you to resend an old value to handle
/// automation start and end gestures.
pub change_type: OutputParamChangeType,
}
/// The type of an [`OutputParamChange`].
pub enum OutputParamChangeType {
/// A regular parameter change, sending a new value for a parameter.
Normal,
/// Tell the host that the automation gesture begins. The event may resend an old value.
BeginGesture,
/// Tell the host that the automation gesture has ended. This event may resend an old value.
EndGesture,
}
/// Because CLAP has this [`clap_host::request_host_callback()`] function, we don't need to use
@ -644,7 +658,18 @@ impl<P: ClapPlugin> Wrapper<P> {
time: current_sample_idx as u32,
space_id: CLAP_CORE_EVENT_SPACE_ID,
type_: CLAP_EVENT_PARAM_VALUE,
flags: CLAP_EVENT_SHOULD_RECORD,
flags: match change.change_type {
OutputParamChangeType::Normal => {
CLAP_EVENT_IS_LIVE | CLAP_EVENT_SHOULD_RECORD
}
// TODO: Should this include `CLAP_EVENT_SHOULD_RECORD`?
OutputParamChangeType::BeginGesture => {
CLAP_EVENT_IS_LIVE | CLAP_EVENT_BEGIN_ADJUST
}
OutputParamChangeType::EndGesture => {
CLAP_EVENT_IS_LIVE | CLAP_EVENT_END_ADJUST
}
},
},
param_id: change.param_hash,
cookie: ptr::null_mut(),