1
0
Fork 0

Implement the event loop for CLAP

It cannot yet handle any tasks though.
This commit is contained in:
Robbert van der Helm 2022-02-28 20:05:48 +01:00
parent 4ad972ea23
commit 2750b2a6ad

View file

@ -2,16 +2,18 @@ use clap_sys::host::clap_host;
use clap_sys::plugin::clap_plugin;
use clap_sys::process::{clap_process, clap_process_status};
use crossbeam::atomic::AtomicCell;
use crossbeam::queue::ArrayQueue;
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 std::thread::{self, ThreadId};
use super::context::WrapperProcessContext;
use super::descriptor::PluginDescriptor;
use crate::event_loop::{EventLoop, MainThreadExecutor};
use crate::event_loop::{EventLoop, MainThreadExecutor, TASK_QUEUE_CAPACITY};
use crate::plugin::{BufferConfig, BusConfig, ClapPlugin};
use crate::NoteEvent;
@ -45,6 +47,18 @@ pub struct Plugin<P: ClapPlugin> {
/// Needs to be boxed because the plugin object is supposed to contain a static reference to
/// this.
plugin_descriptor: Box<PluginDescriptor<P>>,
/// A queue of tasks that still need to be performed. Because CLAP lets the plugin request a
/// host callback directly, we don't need to use the OsEventLoop we use in our other plugin
/// implementations. Instead, we'll post tasks to this queue, ask the host to call
/// [Self::on_main_thread] on the main thread, and then continue to pop tasks off this queue
/// there until it is empty.
tasks: ArrayQueue<Task>,
/// The ID of the main thread. In practice this is the ID of the thread that created this
/// object.
///
/// TODO: If the host supports the ThreadCheck extension, we should use that instead.
main_thread_id: ThreadId,
}
/// Send+Sync wrapper around clap_host.
@ -62,22 +76,35 @@ pub enum Task {
/// 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 {
fn new_and_spawn(_executor: std::sync::Weak<Self>) -> Self {
panic!("What are you doing");
}
fn do_maybe_async(&self, task: Task) -> bool {
todo!()
if self.is_main_thread() {
unsafe { self.execute(task) };
true
} else {
let success = self.tasks.push(task).is_ok();
if success {
// CLAP lets us use the host's event loop instead of having to implement our own
let host = self.host_callback.0;
unsafe { ((*host).request_callback)(host) };
}
success
}
}
fn is_main_thread(&self) -> bool {
todo!()
// TODO: Use the `thread_check::is_main_thread` extension method if that's available
thread::current().id() == self.main_thread_id
}
}
impl<P: ClapPlugin> MainThreadExecutor<Task> for Plugin<P> {
unsafe fn execute(&self, task: Task) {
todo!()
todo!("Implement latency changes for CLAP")
}
}
@ -120,6 +147,9 @@ impl<P: ClapPlugin> Plugin<P> {
host_callback: HostCallback(host_callback),
plugin_descriptor,
tasks: ArrayQueue::new(TASK_QUEUE_CAPACITY),
main_thread_id: thread::current().id(),
}
}
@ -142,7 +172,7 @@ impl<P: ClapPlugin> Plugin<P> {
unsafe extern "C" fn activate(
plugin: *const clap_plugin,
sample_rate: f64,
min_frames_count: u32,
_min_frames_count: u32,
max_frames_count: u32,
) -> bool {
let plugin = &*(plugin as *const Self);
@ -199,6 +229,12 @@ impl<P: ClapPlugin> Plugin<P> {
}
unsafe extern "C" fn on_main_thread(plugin: *const clap_plugin) {
todo!();
let plugin = &*(plugin as *const Self);
// [Self::do_maybe_async] posts a task to the queue and asks the host to call this function
// on the main thread, so once that's done we can just handle all requests here
while let Some(task) = plugin.tasks.pop() {
plugin.execute(task);
}
}
}