Add parameter change outputs for CLAP
But without any way to send them, at least for now.
This commit is contained in:
parent
8f92669a47
commit
a4930dc887
|
@ -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(
|
||||
|
|
Loading…
Reference in a new issue