clack_host/host.rs
1//! Core types and traits to implement CLAP host callback handlers.
2//!
3//! The [`HostHandlers`] trait is the main one required to be implemented for a Clack host. It provides
4//! the host's supported extensions and is associated to a main type implementing [`SharedHandler`] ,
5//! as well as two more optional types implementing [`MainThreadHandler`] and [`AudioProcessorHandler`] (the
6//! unit type `()` can be used as default implementations that do nothing).
7//!
8//! These three types must implement host interfaces to allow them to respond to various requests
9//! from the plugin, following the CLAP thread model described below.
10//!
11//! # Thread specifications
12//!
13//! CLAP's thread model for plugins is split into three specifications of operations: those
14//! happening in the main thread, those happening in an audio processing thread,
15//! and thread-safe operations:
16//!
17//! * The *main thread* (`[main-thread]` in the CLAP specification): this is represented by a type
18//! implementing the [`MainThreadHandler`] trait, which is neither [`Send`] nor [`Sync`], and lives
19//! encapsulated in the [`PluginInstance`](crate::plugin::PluginInstance) struct.
20//!
21//! This type can handle all non-realtime-safe operations, such as those related to buffer
22//! allocations or GUI handling, and extensions can extend its use to more kinds of operations.
23//!
24//! This type is intended to stay on the thread it was created in (i.e. the "main" thread):
25//! implementations cannot [`Send`] this type to any other threads.
26//!
27//! * The *audio thread* (`[audio-thread]` in the CLAP specification): this is represented by a type
28//! implementing the [`AudioProcessorHandler`] trait, which is [`Send`] but
29//! [`!Sync`](Sync), and lives encapsulated in the
30//! [`PluginAudioProcessor`](crate::process::PluginAudioProcessor) struct. It is only
31//! instantiated when the [`activate`](crate::plugin::PluginInstance::activate) method is called,
32//! and is dropped on [`deactivate`](crate::plugin::PluginInstance::deactivate). If it isn't
33//! needed , `()` can be used instead.
34//!
35//! This type is designed to handle all DSP-related requests from the plugin and lives in one of
36//! the host's audio threads, of which there may be multiple, if the host uses a thread pool for
37//! example.
38//!
39//! The host is free to [`Send`] the [`AudioProcessorHandler`] type between any of its audio threads,
40//! but any operation of this class is guaranteed to be called by only a single thread
41//! simultaneously ([`!Sync`](Sync)).
42//!
43//! One exception is for CLAP plugins' activation and deactivation (represented in Clack by the
44//! plugin type's construction and destruction), which is guaranteed to happen in the Main Thread
45//! instead. This allows an [`AudioProcessorHandler`] implementation to receive temporary exclusive
46//! references to the [`MainThreadHandler`] type during its construction and destruction, to take
47//! and release ownership of extra buffers for instance.
48//!
49//! * *Thread-safe operations* (`[thread-safe]` in the CLAP specification) are represented by a type
50//! implementing the [`SharedHandler`] trait, which is both [`Send`] and [`Sync`], and will be
51//! shared between the main thread and the audio thread.
52//!
53//! It can be used to hold read-only data (such as all the detected plugin extensions), or to
54//! hold any other kind of state that is to be synchronized between multiple threads.
55//!
56//! However, it should be noted that this type *can* be used by the plugin simultaneously from
57//! threads that are neither the main thread nor the audio thread.
58//!
59//! # Example
60//!
61//! This example implements a basic host which is able to process callback requests from the plugin,
62//! along with two extensions: `latency` and `log`.
63//!
64//! This is done by implementing the [`HostHandlers`] trait and specifying its associated types:
65//! [`SharedHandler`], [`MainThreadHandler`], and [`AudioProcessorHandler`].
66//!
67//! Because our host supports some extensions, we also implement the [`HostHandlers::declare_extensions`]
68//! method to declare them to the plugin, which requires us to implement the associated traits on
69//! the appropriate [`HostHandlers`] associated types to handle the host-side.
70//!
71//! For more information about which extension traits needs to be implemented on which time, refer
72//! to that extension's documentation.
73//!
74//! For more information about how to work with extensions, see the
75//! [`extensions`](crate::extensions) module documentation.
76//!
77//! ```
78//! use clack_host::events::event_types::*;
79//! use clack_host::prelude::*;
80//!
81//! use clack_extensions::latency::*;
82//! use clack_extensions::log::*;
83//!
84//! use std::sync::atomic::{AtomicBool, Ordering};
85//! use std::sync::OnceLock;
86//! use std::ffi::CStr;
87//!
88//! #[derive(Default)]
89//! struct MyHostShared {
90//! // A real-world implementation may use a fancier notification system.
91//! // For this example, we are simply checking a handful of atomics from time to time.
92//! restart_requested: AtomicBool,
93//! process_requested: AtomicBool,
94//! callback_requested: AtomicBool,
95//!
96//! // Queried extensions
97//! // This may be none if the extension isn't supported by the plugin.
98//! latency_extension: OnceLock<Option<PluginLatency>>
99//! }
100//!
101//! impl<'a> SharedHandler<'a> for MyHostShared {
102//! // Once the plugin is initializing, we can query its extensions
103//! fn initializing(&self, instance: InitializingPluginHandle<'a>) {
104//! let _ = self.latency_extension.set(instance.get_extension());
105//! }
106//!
107//! fn request_restart(&self) { self.restart_requested.store(true, Ordering::SeqCst) }
108//! fn request_process(&self) { self.process_requested.store(true, Ordering::SeqCst) }
109//! fn request_callback(&self) { self.callback_requested.store(true, Ordering::SeqCst) }
110//! }
111//!
112//! impl HostLogImpl for MyHostShared {
113//! fn log(&self, severity: LogSeverity, message: &str) {
114//! // A real-world implementation would make sure this is wait-free.
115//! // But for this example, println! is good enough.
116//! println!("[{severity}] [Plugin] {message}")
117//! }
118//! }
119//!
120//! struct MyHostMainThread<'a> {
121//! shared: &'a MyHostShared,
122//! instance: Option<InitializedPluginHandle<'a>>,
123//!
124//! latency_changed: bool
125//! }
126//!
127//! impl<'a> MainThreadHandler<'a> for MyHostMainThread<'a> {
128//! fn initialized(&mut self, instance: InitializedPluginHandle<'a>) {
129//! self.instance = Some(instance);
130//! }
131//! }
132//!
133//! impl<'a> HostLatencyImpl for MyHostMainThread<'a> {
134//! fn changed(&mut self) {
135//! if let Some(Some(_latency)) = self.shared.latency_extension.get() {
136//! self.latency_changed = true
137//! }
138//! }
139//! }
140//!
141//! struct MyHost;
142//! impl HostHandlers for MyHost {
143//! type Shared<'a> = MyHostShared;
144//!
145//! type MainThread<'a> = MyHostMainThread<'a>;
146//! type AudioProcessor<'a> = ();
147//!
148//! fn declare_extensions(builder: &mut HostExtensions<Self>, shared: &Self::Shared<'_>) {
149//! builder
150//! .register::<HostLog>()
151//! .register::<HostLatency>();
152//! }
153//! }
154//!
155//! # pub fn main() -> Result<(), Box<dyn std::error::Error>> {
156//! // Information about our totally legit host.
157//! let host_info = HostInfo::new("Legit Studio", "Legit Ltd.", "https://example.com", "4.3.2")?;
158//!
159//! # mod diva { include!("entry/diva_stub.rs"); }
160//! # let entry = PluginEntry::load_from_clack::<diva::Entry>(c"/home/user/.clap/u-he/libdiva.so")?;
161//! # /*
162//! let entry = PluginEntry::load("/home/user/.clap/u-he/libdiva.so")?;
163//! # */
164//!
165//! let mut plugin_instance = PluginInstance::<MyHost>::new(
166//! |_| MyHostShared::default(),
167//! |shared| MyHostMainThread { shared, instance: None, latency_changed: false },
168//! &entry,
169//! // We're hard-coding a specific plugin to load for this example
170//! c"com.u-he.diva",
171//! &host_info
172//! )?;
173//!
174//! // Assume we've activated the plugin and just done some processing.
175//! /* ... */
176//!
177//! // Let's check if the plugin requested a callback, by accessing our shared host data.
178//! let callback_requested: &AtomicBool = plugin_instance.access_shared_handler(|h| &h.callback_requested);
179//!
180//! // This fetches the previous value and sets it to false in a single atomic operation.
181//! if callback_requested.fetch_and(false, Ordering::SeqCst) {
182//! plugin_instance.call_on_main_thread_callback();
183//! }
184//!
185//! // Do the same for the restart and process requests
186//! /* ... */
187//!
188//! # Ok(()) }
189//! ```
190
191mod error;
192mod extensions;
193mod info;
194
195pub use error::HostError;
196pub use extensions::HostExtensions;
197pub use info::HostInfo;
198
199use crate::plugin::{InitializedPluginHandle, InitializingPluginHandle};
200
201/// Host data and callbacks that are tied to `[main-thread]` operations.
202///
203/// This trait requires neither [`Send`] nor [`Sync`], as types implementing it are intended to
204/// contain host data staying on the main thread.
205///
206/// This trait is bound to a lifetime (`'a`) that lives at least as long as the plugin instance
207/// does. Since the plugin can call host callbacks during its destructor, this is actually kept alive
208/// slightly longer.
209///
210/// See the [module docs](self) for more information, and for an example implementation.
211pub trait MainThreadHandler<'a>: 'a {
212 /// Called when the plugin has been successfully instantiated.
213 ///
214 /// This is given a handle to the plugin's own main thread data ([`InitializedPluginHandle`]),
215 /// which can be used to call plugin callbacks, and can be kept for the remainder of this
216 /// handler's lifetime.
217 #[inline]
218 #[allow(unused)]
219 fn initialized(&mut self, instance: InitializedPluginHandle<'a>) {}
220}
221
222/// Host data and callbacks that are tied to `[audio-thread]` operations.
223///
224/// This trait requires [`Send`], as a plugin's audio processor can be sent between multiple
225/// threads, but can only be accessed by one thread at a time.
226///
227/// This trait is bound to the plugin audio processor's lifetime (`'a`).
228///
229/// This trait doesn't have any inherent methods, and serves only to restrict the lifetime and
230/// [`Send`] bounds. However, extensions can add more callbacks to the [`HostHandlers::AudioProcessor`]
231/// type.
232///
233/// See the [module docs](self) for more information, and for an example implementation.
234pub trait AudioProcessorHandler<'a>: Send + 'a {}
235
236/// Host data and callbacks that are tied to `[thread-safe]` operations.
237///
238/// This trait requires both [`Send`] *and* [`Sync`], as those callbacks and data can be used from
239/// multiple different threads at the same time.
240///
241/// This trait is bound to a lifetime (`'a`) that lives at least as long as the plugin instance
242/// does. Since the plugin can call host callbacks during its destructor, this is actually kept alive
243/// slightly longer.
244///
245/// See the [module docs](self) for more information, and for an example implementation.
246pub trait SharedHandler<'a>: Send + Sync {
247 /// Called either during the plugin's initialization, or right after it has completed.
248 ///
249 /// More specifically, this is called if the plugin calls any of the host's provided callbacks
250 /// during its initialization process (i.e. during the call of its provided `init()` function).
251 /// If it hasn't called any by the time `init()` finishes, the [`initializing`](Self::initializing) method is
252 /// then called right afterward.
253 ///
254 /// During this time, the host is only allowed to query the plugin's extensions but nothing else.
255 /// The given [`InitializingPluginHandle`] therefore only allows that operation.
256 ///
257 /// The full capability of the [`PluginSharedHandle`](crate::plugin::PluginSharedHandle) is only
258 /// given once [`MainThreadHandler::initialized`] is called, and can be obtained through its given
259 /// [`InitializedPluginHandle`].
260 ///
261 /// This function may be called multiple times concurrently while the plugin is initializing, if
262 /// it performs multithreaded accesses during that time.
263 ///
264 /// It is also possible for other host callbacks to be called *before* this, if the plugin's
265 /// `get_extension()` method calls host callbacks re-entrantly for some reason.
266 ///
267 /// This [`SharedHandler`] implementation should handle both of these cases as gracefully as
268 /// possible.
269 #[inline]
270 #[allow(unused)]
271 fn initializing(&self, instance: InitializingPluginHandle<'a>) {}
272
273 /// Called by the plugin when it requests to be deactivated and then restarted by the host.
274 ///
275 /// This operation may be delayed by the host.
276 fn request_restart(&self);
277
278 /// Called by the plugin when it requests to be activated and/or to start processing.
279 fn request_process(&self);
280
281 /// Called by the plugin when it requests a call to the
282 /// [`on_main_thread` callback](crate::plugin::PluginInstance::call_on_main_thread_callback)
283 /// to be scheduled on the main thread.
284 fn request_callback(&self);
285}
286
287/// A set of host callback handlers.
288///
289/// This type does not hold any data nor is used at runtime. It exists only to tie
290/// the [`Shared`](HostHandlers::Shared), [`MainThread`](HostHandlers::MainThread),
291/// and [`AudioProcessor`](HostHandlers::AudioProcessor)
292/// associated types together with the declared extensions.
293///
294/// See the [module docs](self) for more information, and for an example implementation.
295pub trait HostHandlers: 'static {
296 /// The type that handles host callbacks tied to `[thread-safe]` operations.
297 /// See the [`SharedHandler`] docs and the [module docs](self) for more information.
298 type Shared<'a>: SharedHandler<'a> + 'a;
299
300 /// The type that handles host callbacks tied to `[main-thread]` operations.
301 /// See the [`MainThreadHandler`] docs and the [module docs](self) for more information.
302 type MainThread<'a>: MainThreadHandler<'a> + 'a;
303
304 /// The type that handles host callbacks tied to `[audio-thread]` operations.
305 /// See the [`AudioProcessorHandler`] docs and the [module docs](self) for more information.
306 type AudioProcessor<'a>: AudioProcessorHandler<'a> + 'a;
307
308 /// Declares all the extensions supported by this host.
309 ///
310 /// Extension declaration is done using the [`HostExtensions::register`] method.
311 ///
312 /// A temporary reference to this host's [`Shared`](HostHandlers::Shared) type is also given, in case
313 /// extensions need to be dynamically declared.
314 #[inline]
315 #[allow(unused)]
316 fn declare_extensions(builder: &mut HostExtensions<Self>, shared: &Self::Shared<'_>) {}
317}
318
319// QoL implementations
320
321impl AudioProcessorHandler<'_> for () {}
322impl MainThreadHandler<'_> for () {}
323impl SharedHandler<'_> for () {
324 fn request_restart(&self) {}
325 fn request_process(&self) {}
326 fn request_callback(&self) {}
327}
328impl HostHandlers for () {
329 type Shared<'a> = ();
330 type MainThread<'a> = ();
331 type AudioProcessor<'a> = ();
332}