Add stubs for a CLAP ProcessContext implementation
And the `clap_plugin::active()` function.
This commit is contained in:
parent
41663a404a
commit
4ad972ea23
4 changed files with 112 additions and 26 deletions
|
@ -1,3 +1,4 @@
|
|||
mod context;
|
||||
mod descriptor;
|
||||
mod factory;
|
||||
mod plugin;
|
||||
|
|
33
src/wrapper/clap/context.rs
Normal file
33
src/wrapper/clap/context.rs
Normal 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()
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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> {
|
||||
|
|
Loading…
Add table
Reference in a new issue