From 246c319bbf1e8a93b1dc235e55aa0e2417bb1b51 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Fri, 11 Mar 2022 00:14:39 +0100 Subject: [PATCH] Implement automation gestures for CLAP --- src/wrapper/clap/context.rs | 44 ++++++++++++++++++++++++++++++------- src/wrapper/clap/wrapper.rs | 33 ++++++++++++++++++++++++---- 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/wrapper/clap/context.rs b/src/wrapper/clap/context.rs index 2f48c8da..df85747d 100644 --- a/src/wrapper/clap/context.rs +++ b/src/wrapper/clap/context.rs @@ -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 GuiContext for WrapperGuiContext

{ // 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(¶m) { + 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 GuiContext for WrapperGuiContext

{ 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 GuiContext for WrapperGuiContext

{ } } - 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(¶m) { + 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 { diff --git a/src/wrapper/clap/wrapper.rs b/src/wrapper/clap/wrapper.rs index 31c45c65..9be414aa 100644 --- a/src/wrapper/clap/wrapper.rs +++ b/src/wrapper/clap/wrapper.rs @@ -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 Wrapper

{ 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(),