1
0
Fork 0

Add parameter change outputs for CLAP

But without any way to send them, at least for now.
This commit is contained in:
Robbert van der Helm 2022-03-03 17:21:32 +01:00
parent 8f92669a47
commit a4930dc887

View file

@ -6,7 +6,7 @@ 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_PARAM_VALUE, CLAP_EVENT_SHOULD_RECORD,
};
use clap_sys::ext::audio_ports::{
clap_audio_port_info, clap_plugin_audio_ports, CLAP_AUDIO_PORT_IS_MAIN, CLAP_EXT_AUDIO_PORTS,
@ -39,6 +39,7 @@ use std::any::Any;
use std::cmp;
use std::collections::{HashMap, VecDeque};
use std::ffi::{c_void, CStr};
use std::mem;
use std::os::raw::c_char;
use std::ptr;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
@ -63,6 +64,10 @@ lazy_static! {
pub static ref BYPASS_PARAM_HASH: u32 = hash_param_id(BYPASS_PARAM_ID);
}
/// How many output parameter changes we can store in our output parameter change queue. Storing
/// more than this many parmaeters at a time will cause changes to get lost.
const OUTPUT_EVENT_QUEUE_CAPACITY: usize = 2048;
#[repr(C)]
pub struct Wrapper<P: ClapPlugin> {
// Keep the vtable as the first field so we can do a simple pointer cast
@ -149,6 +154,13 @@ pub struct Wrapper<P: ClapPlugin> {
/// ergonomic parameter setting API that uses references to the parameters instead of having to
/// add a setter function to the parameter (or even worse, have it be completely untyped).
param_ptr_to_hash: HashMap<ParamPtr, u32>,
/// A queue of parameter changes that should be output in either the next process call or in the
/// next parameter flush.
///
/// XXX: There's no guarentee that a single parmaeter doesn't occur twice in this queue, but
/// even if it does then that should still not be a problem because the host also reads it
/// in the same order, right?
output_parameter_changes: ArrayQueue<OutputParamChange>,
host_thread_check: Option<ClapPtr<clap_host_thread_check>>,
@ -184,6 +196,16 @@ pub enum ClapParamUpdate {
PlainValueMod(f64),
}
/// A parameter change that should be output by the plugin, stored in a queue on the wrapper and
/// written to the host either at the end of the process function or during a flush.
struct OutputParamChange {
/// The internal hash for the parameter.
param_hash: u32,
/// The 'plain' value as reported to CLAP. This is the normalized value multiplied by
/// [crate::Param::step_size].
clap_plain_value: f64,
}
/// Because CLAP has this [clap_host::request_host_callback()] function, we don't need to use
/// `OsEventLoop` and can instead just request a main thread callback directly.
impl<P: ClapPlugin> EventLoop<Task, Wrapper<P>> for Wrapper<P> {
@ -328,6 +350,7 @@ impl<P: ClapPlugin> Wrapper<P> {
param_defaults_normalized: HashMap::new(),
param_id_to_hash: HashMap::new(),
param_ptr_to_hash: HashMap::new(),
output_parameter_changes: ArrayQueue::new(OUTPUT_EVENT_QUEUE_CAPACITY),
host_thread_check,
@ -491,6 +514,31 @@ impl<P: ClapPlugin> Wrapper<P> {
}
}
/// Write the unflushed parameter changes to the host's output event queue.
pub unsafe fn handle_out_events(&self, out: &clap_output_events) {
// We'll always write these events to the first sample, so even when we add note output we
// shouldn't have to think about interleaving events here
while let Some(change) = self.output_parameter_changes.pop() {
let event = clap_event_param_value {
header: clap_event_header {
size: mem::size_of::<clap_event_param_value>() as u32,
time: 0,
space_id: CLAP_CORE_EVENT_SPACE_ID,
type_: CLAP_EVENT_PARAM_VALUE,
flags: CLAP_EVENT_SHOULD_RECORD,
},
param_id: change.param_hash,
cookie: ptr::null_mut(),
port_index: -1,
key: -1,
channel: -1,
value: change.clap_plain_value,
};
(out.push_back)(out, &event.header);
}
}
/// Handle an incoming CLAP event. You must clear [Self::input_events] first before calling this
/// from the process function.
///
@ -727,7 +775,7 @@ impl<P: ClapPlugin> Wrapper<P> {
let mut plugin = wrapper.plugin.write();
let mut context = wrapper.make_process_context();
match plugin.process(&mut output_buffer, &mut context) {
let result = match plugin.process(&mut output_buffer, &mut context) {
ProcessStatus::Error(err) => {
nih_debug_assert_failure!("Process error: {}", err);
@ -736,9 +784,14 @@ impl<P: ClapPlugin> Wrapper<P> {
ProcessStatus::Normal => CLAP_PROCESS_CONTINUE_IF_NOT_QUIET,
ProcessStatus::Tail(_) => CLAP_PROCESS_CONTINUE,
ProcessStatus::KeepAlive => CLAP_PROCESS_CONTINUE,
};
// After processing audio, send all spooled events to the host
if !process.out_events.is_null() {
wrapper.handle_out_events(&*process.out_events);
}
// TODO: Handle parameter outputs/automation
result
})
}
@ -1234,7 +1287,9 @@ impl<P: ClapPlugin> Wrapper<P> {
wrapper.handle_in_events(&*in_);
}
// TODO: Handle automation/outputs
if !out.is_null() {
wrapper.handle_out_events(&*out);
}
}
unsafe extern "C" fn ext_state_save(