1
0
Fork 0

Add stubs for a CLAP ProcessContext implementation

And the `clap_plugin::active()` function.
This commit is contained in:
Robbert van der Helm 2022-02-28 19:45:41 +01:00
parent 41663a404a
commit 4ad972ea23
4 changed files with 112 additions and 26 deletions

View file

@ -1,3 +1,4 @@
mod context;
mod descriptor;
mod factory;
mod plugin;

View file

@ -0,0 +1,33 @@
use parking_lot::RwLockWriteGuard;
use std::collections::VecDeque;
use std::sync::atomic::Ordering;
use super::plugin::{Plugin, Task};
use crate::context::ProcessContext;
use crate::event_loop::EventLoop;
use crate::plugin::{ClapPlugin, NoteEvent};
/// A [ProcessContext] implementation for the wrapper. This is a separate object so it can hold on
/// to lock guards for event queues. Otherwise reading these events would require constant
/// unnecessary atomic operations to lock the uncontested RwLocks.
pub(crate) struct WrapperProcessContext<'a, P: ClapPlugin> {
pub(super) plugin: &'a Plugin<P>,
pub(super) input_events_guard: RwLockWriteGuard<'a, VecDeque<NoteEvent>>,
}
impl<P: ClapPlugin> ProcessContext for WrapperProcessContext<'_, P> {
fn set_latency_samples(&self, samples: u32) {
// Only make a callback if it's actually needed
// XXX: For CLAP we could move this handling to the Plugin struct, but it may be worthwhile
// to keep doing it this way to stay consistent with VST3.
let old_latency = self.plugin.current_latency.swap(samples, Ordering::SeqCst);
if old_latency != samples {
let task_posted = self.plugin.do_maybe_async(Task::LatencyChanged);
nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
}
}
fn next_midi_event(&mut self) -> Option<NoteEvent> {
self.input_events_guard.pop_front()
}
}

View file

@ -3,12 +3,17 @@ use clap_sys::plugin::clap_plugin;
use clap_sys::process::{clap_process, clap_process_status};
use crossbeam::atomic::AtomicCell;
use parking_lot::RwLock;
use std::collections::VecDeque;
use std::ffi::c_void;
use std::os::raw::c_char;
use std::ptr;
use std::sync::atomic::AtomicU32;
use super::context::WrapperProcessContext;
use super::descriptor::PluginDescriptor;
use crate::event_loop::{EventLoop, MainThreadExecutor};
use crate::plugin::{BufferConfig, BusConfig, ClapPlugin};
use crate::NoteEvent;
#[repr(C)]
pub struct Plugin<P: ClapPlugin> {
@ -23,14 +28,62 @@ pub struct Plugin<P: ClapPlugin> {
current_bus_config: AtomicCell<BusConfig>,
/// The current buffer configuration, containing the sample rate and the maximum block size.
/// Will be set in `clap_plugin::activate()`.
pub current_buffer_config: AtomicCell<Option<BufferConfig>>,
current_buffer_config: AtomicCell<Option<BufferConfig>>,
/// The incoming events for the plugin, if `P::ACCEPTS_MIDI` is set.
///
/// TODO: Maybe load these lazily at some point instead of needing to spool them all to this
/// queue first
/// TODO: Read these in the process call.
input_events: RwLock<VecDeque<NoteEvent>>,
/// The current latency in samples, as set by the plugin through the [ProcessContext]. uses the
/// latency extnesion
///
/// TODO: Implement the latency extension.
pub current_latency: AtomicU32,
host_callback: *const clap_host,
host_callback: HostCallback,
/// Needs to be boxed because the plugin object is supposed to contain a static reference to
/// this.
plugin_descriptor: Box<PluginDescriptor<P>>,
}
/// Send+Sync wrapper around clap_host.
struct HostCallback(*const clap_host);
/// Tasks that can be sent from the plugin to be executed on the main thread in a non-blocking
/// realtime safe way. Instead of using a random thread or the OS' event loop like in the Linux
/// implementation, this uses [clap_host::request_callback()] instead.
#[derive(Debug, Clone)]
pub enum Task {
/// Inform the host that the latency has changed.
LatencyChanged,
}
/// 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, Plugin<P>> for Plugin<P> {
fn new_and_spawn(executor: std::sync::Weak<Self>) -> Self {
panic!("What are you doing");
}
fn do_maybe_async(&self, task: Task) -> bool {
todo!()
}
fn is_main_thread(&self) -> bool {
todo!()
}
}
impl<P: ClapPlugin> MainThreadExecutor<Task> for Plugin<P> {
unsafe fn execute(&self, task: Task) {
todo!()
}
}
unsafe impl Send for HostCallback {}
unsafe impl Sync for HostCallback {}
impl<P: ClapPlugin> Plugin<P> {
pub fn new(host_callback: *const clap_host) -> Self {
let plugin_descriptor = Box::new(PluginDescriptor::default());
@ -62,20 +115,20 @@ impl<P: ClapPlugin> Plugin<P> {
num_output_channels: P::DEFAULT_NUM_OUTPUTS,
}),
current_buffer_config: AtomicCell::new(None),
input_events: RwLock::new(VecDeque::with_capacity(512)),
current_latency: AtomicU32::new(0),
host_callback,
host_callback: HostCallback(host_callback),
plugin_descriptor,
}
}
}
impl<P: ClapPlugin> Plugin<P> {
// pub fn make_process_context(&self) -> WrapperProcessContext<'_, P> {
// WrapperProcessContext {
// plugin: self,
// input_events_guard: self.input_events.write(),
// }
// }
fn make_process_context(&self) -> WrapperProcessContext<'_, P> {
WrapperProcessContext {
plugin: self,
input_events_guard: self.input_events.write(),
}
}
unsafe extern "C" fn init(_plugin: *const clap_plugin) -> bool {
// We don't need any special initialization
@ -102,21 +155,20 @@ impl<P: ClapPlugin> Plugin<P> {
// TODO: Reset smoothers
todo!();
// if plugin.plugin.write().initialize(
// &bus_config,
// &buffer_config,
// plugin.make_process_context(),
// ) {
// // TODO: Allocate buffer slices
if plugin.plugin.write().initialize(
&bus_config,
&buffer_config,
&mut plugin.make_process_context(),
) {
// TODO: Allocate buffer slices
// // Also store this for later, so we can reinitialize the plugin after restoring state
// plugin.current_buffer_config.store(Some(buffer_config));
// Also store this for later, so we can reinitialize the plugin after restoring state
plugin.current_buffer_config.store(Some(buffer_config));
// true
// } else {
// false
// }
true
} else {
false
}
}
unsafe extern "C" fn deactivate(_plugin: *const clap_plugin) {

View file

@ -11,8 +11,8 @@ use crate::plugin::{NoteEvent, Vst3Plugin};
/// to lock guards for event queues. Otherwise reading these events would require constant
/// unnecessary atomic operations to lock the uncontested RwLocks.
pub(crate) struct WrapperProcessContext<'a, P: Vst3Plugin> {
pub inner: &'a WrapperInner<P>,
pub input_events_guard: RwLockWriteGuard<'a, VecDeque<NoteEvent>>,
pub(super) inner: &'a WrapperInner<P>,
pub(super) input_events_guard: RwLockWriteGuard<'a, VecDeque<NoteEvent>>,
}
impl<P: Vst3Plugin> ProcessContext for WrapperProcessContext<'_, P> {