Skip to main content

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::cell::{Cell, OnceCell};
87//! use std::ffi::CStr;
88//!
89//! #[derive(Default)]
90//! struct MyHostShared {
91//!     // A real-world implementation may use a fancier notification system.
92//!     // For this example, we are simply checking a handful of atomics from time to time.
93//!     restart_requested: AtomicBool,
94//!     process_requested: AtomicBool,
95//!     callback_requested: AtomicBool,
96//!
97//!     // Queried extensions
98//!     // This may be none if the extension isn't supported by the plugin.
99//!     latency_extension: OnceLock<Option<PluginLatency>>
100//! }
101//!
102//! impl<'a> SharedHandler<'a> for MyHostShared {
103//!     // Once the plugin is initializing, we can query its extensions
104//!     fn initializing(&self, instance: InitializingPluginHandle<'a>) {
105//!         let _ = self.latency_extension.set(instance.get_extension());
106//!     }
107//!     
108//!     fn request_restart(&self) { self.restart_requested.store(true, Ordering::SeqCst) }
109//!     fn request_process(&self) { self.process_requested.store(true, Ordering::SeqCst) }
110//!     fn request_callback(&self) { self.callback_requested.store(true, Ordering::SeqCst) }
111//! }
112//!
113//! impl HostLogImpl for MyHostShared {
114//!     fn log(&self, severity: LogSeverity, message: &str) {
115//!         // A real-world implementation would make sure this is wait-free.
116//!         // But for this example, println! is good enough.
117//!         println!("[{severity}] [Plugin] {message}")
118//!     }
119//! }
120//!
121//! struct MyHostMainThread<'a> {
122//!     shared: &'a MyHostShared,
123//!     instance: OnceCell<InitializedPluginHandle<'a>>,
124//!
125//!     latency_changed: Cell<bool>
126//! }
127//!
128//! impl<'a> MainThreadHandler<'a> for MyHostMainThread<'a> {
129//!     fn initialized(&self, instance: InitializedPluginHandle<'a>) {
130//!         self.instance.set(instance);
131//!     }
132//! }
133//!
134//! impl<'a> HostLatencyImpl for MyHostMainThread<'a> {
135//!     fn changed(&self) {
136//!         if let Some(Some(_latency)) = self.shared.latency_extension.get() {
137//!             self.latency_changed.set(true)
138//!         }   
139//!     }
140//! }
141//!
142//! struct MyHost;
143//! impl HostHandlers for MyHost {
144//!     type Shared<'a> = MyHostShared;
145//!
146//!     type MainThread<'a> = MyHostMainThread<'a>;
147//!     type AudioProcessor<'a> = ();
148//!
149//!     fn declare_extensions(builder: &mut HostExtensions<Self>, shared: &Self::Shared<'_>) {
150//!         builder
151//!             .register::<HostLog>()
152//!             .register::<HostLatency>();
153//!     }
154//! }
155//!
156//! # pub fn main() -> Result<(), Box<dyn std::error::Error>> {
157//! // Information about our totally legit host.
158//! let host_info = HostInfo::new("Legit Studio", "Legit Ltd.", "https://example.com", "4.3.2")?;
159//!
160//! # mod diva { include!("entry/diva_stub.rs"); }
161//! # let entry = PluginEntry::load_from_clack::<diva::Entry>(c"/home/user/.clap/u-he/libdiva.so")?;
162//! # /*
163//! let entry = PluginEntry::load("/home/user/.clap/u-he/libdiva.so")?;
164//! # */
165//!
166//! let mut plugin_instance = PluginInstance::<MyHost>::new(
167//!     |_| MyHostShared::default(),
168//!     |shared| MyHostMainThread { shared, instance: OnceCell::new(), latency_changed: false.into() },
169//!     &entry,
170//!     // We're hard-coding a specific plugin to load for this example
171//!     c"com.u-he.diva",
172//!     &host_info
173//! )?;
174//!
175//! // Assume we've activated the plugin and just done some processing.
176//! /* ... */
177//!
178//! // Let's check if the plugin requested a callback, by accessing our shared host data.
179//! let callback_requested: &AtomicBool = plugin_instance.access_shared_handler(|h| &h.callback_requested);
180//!
181//! // This fetches the previous value and sets it to false in a single atomic operation.
182//! if callback_requested.fetch_and(false, Ordering::SeqCst) {
183//!     plugin_instance.call_on_main_thread_callback();
184//! }
185//!
186//! // Do the same for the restart and process requests
187//! /* ... */
188//!
189//! # Ok(()) }
190//! ```
191
192mod error;
193mod extensions;
194mod info;
195
196pub use error::HostError;
197pub use extensions::HostExtensions;
198pub use info::HostInfo;
199
200use crate::plugin::{InitializedPluginHandle, InitializingPluginHandle};
201
202/// Host data and callbacks that are tied to `[main-thread]` operations.
203///
204/// This trait requires neither [`Send`] nor [`Sync`], as types implementing it are intended to
205/// contain host data staying on the main thread.
206///
207/// This trait is bound to a lifetime (`'a`) that lives at least as long as the plugin instance
208/// does. Since the plugin can call host callbacks during its destructor, this is actually kept alive
209/// slightly longer.
210///
211/// See the [module docs](self) for more information, and for an example implementation.
212pub trait MainThreadHandler<'a>: 'a {
213    /// Called when the plugin has been successfully instantiated.
214    ///
215    /// This is given a handle to the plugin's own main thread data ([`InitializedPluginHandle`]),
216    /// which can be used to call plugin callbacks, and can be kept for the remainder of this
217    /// handler's lifetime.
218    #[inline]
219    #[allow(unused)]
220    fn initialized(&self, instance: InitializedPluginHandle<'a>) {}
221}
222
223/// Host data and callbacks that are tied to `[audio-thread]` operations.
224///
225/// This trait requires [`Send`], as a plugin's audio processor can be sent between multiple
226/// threads, but can only be accessed by one thread at a time.
227///
228/// This trait is bound to the plugin audio processor's lifetime (`'a`).
229///
230/// This trait doesn't have any inherent methods, and serves only to restrict the lifetime and
231/// [`Send`] bounds. However, extensions can add more callbacks to the [`HostHandlers::AudioProcessor`]
232/// type.
233///
234/// See the [module docs](self) for more information, and for an example implementation.
235pub trait AudioProcessorHandler<'a>: Send + 'a {}
236
237/// Host data and callbacks that are tied to `[thread-safe]` operations.
238///
239/// This trait requires both [`Send`] *and* [`Sync`], as those callbacks and data can be used from
240/// multiple different threads at the same time.
241///
242/// This trait is bound to a lifetime (`'a`) that lives at least as long as the plugin instance
243/// does. Since the plugin can call host callbacks during its destructor, this is actually kept alive
244/// slightly longer.
245///
246/// See the [module docs](self) for more information, and for an example implementation.
247pub trait SharedHandler<'a>: Send + Sync {
248    /// Called either during the plugin's initialization, or right after it has completed.
249    ///
250    /// More specifically, this is called if the plugin calls any of the host's provided callbacks
251    /// during its initialization process (i.e. during the call of its provided `init()` function).
252    /// If it hasn't called any by the time `init()` finishes, the [`initializing`](Self::initializing) method is
253    /// then called right afterward.
254    ///
255    /// During this time, the host is only allowed to query the plugin's extensions but nothing else.
256    /// The given [`InitializingPluginHandle`] therefore only allows that operation.
257    ///
258    /// The full capability of the [`PluginSharedHandle`](crate::plugin::PluginSharedHandle) is only
259    /// given once [`MainThreadHandler::initialized`] is called, and can be obtained through its given
260    /// [`InitializedPluginHandle`].
261    ///
262    /// This function may be called multiple times concurrently while the plugin is initializing, if
263    /// it performs multithreaded accesses during that time.
264    ///
265    /// It is also possible for other host callbacks to be called *before* this, if the plugin's
266    /// `get_extension()` method calls host callbacks re-entrantly for some reason.
267    ///
268    /// This [`SharedHandler`] implementation should handle both of these cases as gracefully as
269    /// possible.
270    #[inline]
271    #[allow(unused)]
272    fn initializing(&self, instance: InitializingPluginHandle<'a>) {}
273
274    /// Called by the plugin when it requests to be deactivated and then restarted by the host.
275    ///
276    /// This operation may be delayed by the host.
277    fn request_restart(&self);
278
279    /// Called by the plugin when it requests to be activated and/or to start processing.
280    fn request_process(&self);
281
282    /// Called by the plugin when it requests a call to the
283    /// [`on_main_thread` callback](crate::plugin::PluginInstance::call_on_main_thread_callback)
284    /// to be scheduled on the main thread.
285    fn request_callback(&self);
286}
287
288/// A set of host callback handlers.
289///
290/// This type does not hold any data nor is used at runtime. It exists only to tie
291/// the [`Shared`](HostHandlers::Shared), [`MainThread`](HostHandlers::MainThread),
292/// and [`AudioProcessor`](HostHandlers::AudioProcessor)
293/// associated types together with the declared extensions.
294///
295/// See the [module docs](self) for more information, and for an example implementation.
296pub trait HostHandlers: 'static {
297    /// The type that handles host callbacks tied to `[thread-safe]` operations.
298    /// See the [`SharedHandler`] docs and the [module docs](self) for more information.
299    type Shared<'a>: SharedHandler<'a> + 'a;
300
301    /// The type that handles host callbacks tied to `[main-thread]` operations.
302    /// See the [`MainThreadHandler`] docs and the [module docs](self) for more information.
303    type MainThread<'a>: MainThreadHandler<'a> + 'a;
304
305    /// The type that handles host callbacks tied to `[audio-thread]` operations.
306    /// See the [`AudioProcessorHandler`] docs and the [module docs](self) for more information.
307    type AudioProcessor<'a>: AudioProcessorHandler<'a> + 'a;
308
309    /// Declares all the extensions supported by this host.
310    ///
311    /// Extension declaration is done using the [`HostExtensions::register`] method.
312    ///
313    /// A temporary reference to this host's [`Shared`](HostHandlers::Shared) type is also given, in case
314    /// extensions need to be dynamically declared.
315    #[inline]
316    #[allow(unused)]
317    fn declare_extensions(builder: &mut HostExtensions<Self>, shared: &Self::Shared<'_>) {}
318}
319
320// QoL implementations
321
322impl AudioProcessorHandler<'_> for () {}
323impl MainThreadHandler<'_> for () {}
324impl SharedHandler<'_> for () {
325    fn request_restart(&self) {}
326    fn request_process(&self) {}
327    fn request_callback(&self) {}
328}
329impl HostHandlers for () {
330    type Shared<'a> = ();
331    type MainThread<'a> = ();
332    type AudioProcessor<'a> = ();
333}