diff --git a/src/wrapper/clap.rs b/src/wrapper/clap.rs
index cc991518..8edfefa3 100644
--- a/src/wrapper/clap.rs
+++ b/src/wrapper/clap.rs
@@ -1,3 +1,4 @@
+mod context;
mod descriptor;
mod factory;
mod plugin;
diff --git a/src/wrapper/clap/context.rs b/src/wrapper/clap/context.rs
new file mode 100644
index 00000000..f9f863d2
--- /dev/null
+++ b/src/wrapper/clap/context.rs
@@ -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
,
+ pub(super) input_events_guard: RwLockWriteGuard<'a, VecDeque>,
+}
+
+impl 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 {
+ self.input_events_guard.pop_front()
+ }
+}
diff --git a/src/wrapper/clap/plugin.rs b/src/wrapper/clap/plugin.rs
index 40c13bb2..26d7ebe8 100644
--- a/src/wrapper/clap/plugin.rs
+++ b/src/wrapper/clap/plugin.rs
@@ -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 {
@@ -23,14 +28,62 @@ pub struct Plugin {
current_bus_config: AtomicCell,
/// 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