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::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}