Skip to main content

clack_host/
plugin.rs

1//! Core types and traits to instantiate and interact with CLAP plugins.
2
3use crate::prelude::*;
4use clap_sys::plugin::clap_plugin;
5use std::ffi::CStr;
6use std::marker::PhantomData;
7use std::mem::ManuallyDrop;
8use std::sync::Arc;
9
10mod error;
11mod handle;
12pub(crate) mod instance;
13
14pub use error::PluginInstanceError;
15pub use handle::*;
16use instance::*;
17
18pub use clack_common::plugin::*;
19
20/// A plugin instance.
21///
22/// This type is generic over `H`, the type of the host callback handlers, which the plugin can
23/// call use directly when its own methods are called by the host.
24pub struct PluginInstance<H: HostHandlers> {
25    pub(crate) inner: ManuallyDrop<Arc<PluginInstanceInner<H>>>,
26    _no_send: PhantomData<*const ()>,
27}
28
29impl<H: HostHandlers> PluginInstance<H> {
30    /// Creates a new plugin instance from a given [`PluginEntry`].
31    ///
32    /// The specific plugin to be instantiated is identified by the given `plugin_id`.
33    ///
34    /// This method takes two closures, here named `FS` and `FH`, which will respectively create
35    /// the [`Shared`](HostHandlers::Shared) and [`MainThread`](HostHandlers::MainThread) callback
36    /// handler instances associated with this plugin instance.
37    ///
38    /// The [`MainThread`](HostHandlers::MainThread)-producing callback takes a long-lived reference
39    /// to its [`Shared`](HostHandlers::Shared) counterpart, so that it can be accessed at any time
40    /// from the main thread if needed.
41    ///
42    /// The [`Shared`](HostHandlers::Shared)-producing callback takes a unit reference `&()`. It is
43    /// not used for anything, but is necessary for proper lifetime binding.
44    ///
45    /// This method also takes a reference to a [`HostInfo`], giving the plugin some metadata about
46    /// the host itself.
47    ///
48    /// # Errors
49    ///
50    /// If for some reason the given [`PluginEntry`] does not expose a [`PluginFactory`](crate::factory::plugin::PluginFactory),
51    /// then this will return [`PluginInstanceError::MissingPluginFactory`].
52    ///
53    /// If the `PluginFactory`'s `create_plugin` function pointer is NULL,
54    /// this will return [`PluginInstanceError::NullFactoryCreatePluginFunction`].
55    ///
56    /// If the given `plugin_id` does not exist, or if more generally the
57    /// `PluginFactory`'s `create_plugin` function returned NULL,
58    /// then this will return [`PluginInstanceError::PluginNotFound`].
59    ///
60    /// Otherwise, if the plugin instantiation implementation failed, it will return
61    /// [`PluginInstanceError::InstantiationFailed`].
62    ///
63    /// # Example
64    ///
65    /// ```
66    /// use clack_host::prelude::*;
67    ///
68    /// struct MyHost;
69    ///
70    /// struct MyHostMainThread { /* ... */ }
71    ///
72    /// impl HostHandlers for MyHost {
73    ///     type Shared<'a> = /* ... */
74    /// # ();
75    ///     type AudioProcessor<'a> = /* ... */
76    /// # ();
77    ///     type MainThread<'a> = MyHostMainThread;
78    /// }
79    ///
80    /// impl<'a> MainThreadHandler<'a> for MyHostMainThread {
81    ///     fn initialized(&self, instance: InitializedPluginHandle<'a>) {
82    ///         // Called whn the plugin has been fully initialized.
83    ///     }
84    /// }
85    ///
86    /// # mod diva { include!("./entry/diva_stub.rs"); }
87    ///
88    /// let entry = /* ... */
89    /// # PluginEntry::load_from_clack::<diva::Entry>(c"").unwrap();
90    /// let host_info = HostInfo::new("Foo DAW", "Foo Inc.", "https://example.com", "0.0.1").unwrap();
91    ///
92    /// let instance = PluginInstance::<MyHost>::new(
93    ///     |_|(),
94    ///     |_| MyHostMainThread {},
95    ///     &entry,
96    ///     c"com.audio.the-plugin",
97    ///     &host_info
98    /// );
99    ///
100    /// ```
101    pub fn new<FS, FH>(
102        shared: FS,
103        main_thread: FH,
104        entry: &PluginEntry,
105        plugin_id: &CStr,
106        host: &HostInfo,
107    ) -> Result<Self, PluginInstanceError>
108    where
109        FS: for<'b> FnOnce(&'b ()) -> <H as HostHandlers>::Shared<'b>,
110        FH: for<'b> FnOnce(
111            &'b <H as HostHandlers>::Shared<'b>,
112        ) -> <H as HostHandlers>::MainThread<'b>,
113    {
114        let inner = PluginInstanceInner::<H>::instantiate(
115            shared,
116            main_thread,
117            entry,
118            plugin_id,
119            host.clone(),
120        )?;
121
122        Ok(Self {
123            inner: ManuallyDrop::new(inner),
124            _no_send: PhantomData,
125        })
126    }
127
128    /// Activates the plugin instance, preparing it to process audio and events according to the given [`PluginAudioConfiguration`].
129    ///
130    /// This method takes a closure `FA`, which will create
131    /// the [`AudioProcessor`](HostHandlers::AudioProcessor) callback
132    /// handler instance associated with this plugin instance.
133    ///
134    /// This closure takes a long-lived reference to the [`Shared`](HostHandlers::Shared) handler
135    /// so that it can be accessed at any time from the audio thread / audio-processing context if needed.
136    ///
137    /// This closure also provides a temporary, exclusive reference to the [`MainThread`](HostHandlers::MainThread) handler.
138    ///
139    /// # Errors
140    ///
141    /// This will return [`PluginInstanceError::AlreadyActivatedPlugin`] if the plugin has already
142    /// been activated.
143    ///
144    /// If the plugin instance's `activate` function pointer is NULL,
145    /// this will return [`PluginInstanceError::NullActivateFunction`].
146    ///
147    /// Otherwise, if the plugin instance's `activate` function implementation failed for any reason,
148    /// this will return [`PluginInstanceError::ActivationFailed`].
149    ///
150    /// # Panics
151    ///
152    /// This function will panic if either `min_frames_count` or `max_frames_count` is 0, or if
153    /// `min_frames_count` is greater than `max_frames_count`.
154    pub fn activate<FA>(
155        &mut self,
156        audio_processor: FA,
157        configuration: PluginAudioConfiguration,
158    ) -> Result<StoppedPluginAudioProcessor<H>, PluginInstanceError>
159    where
160        FA: for<'a> FnOnce(
161            &'a <H as HostHandlers>::Shared<'a>,
162            &<H as HostHandlers>::MainThread<'a>,
163        ) -> <H as HostHandlers>::AudioProcessor<'a>,
164    {
165        configuration.validate();
166        let wrapper =
167            Arc::get_mut(&mut self.inner).ok_or(PluginInstanceError::AlreadyActivatedPlugin)?;
168        wrapper.activate(audio_processor, configuration)?;
169
170        Ok(StoppedPluginAudioProcessor::new(Arc::clone(&self.inner)))
171    }
172
173    /// De-activates the plugin instance, freeing its processing-related resources, and allows it
174    /// to be re-[activated](Self::activate) with a different audio configuration.
175    ///
176    /// This consumes the [`StoppedPluginAudioProcessor`] handle created by [`activate`](Self::activate),
177    /// in order to ensure it cannot be used anymore.
178    ///
179    /// For a version of this method that does not require the [`StoppedPluginAudioProcessor`] handle,
180    /// see [`try_deactivate`](Self::try_deactivate).
181    ///
182    /// # Panics
183    ///
184    /// This will panic if the provided `processor` handle did not come from this plugin instance.
185    #[inline]
186    pub fn deactivate(&mut self, processor: StoppedPluginAudioProcessor<H>) {
187        self.deactivate_with(processor, |_, _| ())
188    }
189
190    /// Attempts to de-activate a plugin instance, which [`PluginAudioProcessor`] handle has been
191    /// dropped.
192    ///
193    /// This is an alternative to [`deactivate`](Self::deactivate) which does not require the associated
194    /// [`PluginAudioProcessorHandle`] to be passed. However, this method is fallible, and will
195    /// return an error if the [`PluginAudioProcessorHandle`] has not been dropped.
196    ///
197    /// This may be useful if it is more practical for you to drop the [`PluginAudioProcessorHandle`]
198    /// (e.g. in another thread) compared to sending it back to the main thread to use with
199    /// [`deactivate`](Self::deactivate).
200    ///
201    /// However, if you can easily access or retrieve [`PluginAudioProcessorHandle`] back to the main
202    /// thread, you should use [`deactivate`](Self::deactivate) instead.
203    ///
204    /// # Errors
205    ///
206    /// This method will return [`PluginInstanceError::StillActivatedPlugin`] if the associated
207    /// [`PluginAudioProcessorHandle`] has not been dropped.
208    #[inline]
209    pub fn try_deactivate(&mut self) -> Result<(), PluginInstanceError> {
210        self.try_deactivate_with(|_, _| ())
211    }
212
213    /// De-activates the plugin instance, freeing its processing-related resources, and allows it
214    /// to be re-[activated](Self::activate) with a different audio configuration.
215    ///
216    /// This consumes the [`StoppedPluginAudioProcessor`] handle created by [`activate`](Self::activate),
217    /// in order to ensure it cannot be used anymore.
218    ///
219    /// This is equivalent to [`Self::deactivate`], except this method also takes a closure type `D`,
220    /// which allows to customize the destruction process of the [`AudioProcessor`](HostHandlers::AudioProcessor) callback
221    /// handler instance that was created during [`activate`](Self::activate).
222    ///
223    /// This may be useful to e.g. reuse or reprocess components or allocations the [`AudioProcessor`](HostHandlers::AudioProcessor) callback
224    /// handler instance owned, either by giving it to the [`MainThread`](HostHandlers::MainThread) handler
225    /// (which is also provided by a temporary, exclusive reference), or by returning it.
226    ///
227    /// This function will return whatever the destructor closure returned.
228    ///
229    /// # Panics
230    ///
231    /// This will panic if the provided `processor` handle did not come from this plugin instance.
232    pub fn deactivate_with<T, D>(
233        &mut self,
234        processor: StoppedPluginAudioProcessor<H>,
235        drop_with: D,
236    ) -> T
237    where
238        D: for<'s> FnOnce(
239            <H as HostHandlers>::AudioProcessor<'s>,
240            &<H as HostHandlers>::MainThread<'s>,
241        ) -> T,
242    {
243        if !Arc::ptr_eq(&self.inner, &processor.inner) {
244            panic!("Given plugin audio processor does not match the instance being deactivated")
245        }
246
247        drop(processor);
248
249        // PANIC: we dropped the only processor produced, and checked if it matched
250        self.try_deactivate_with(drop_with).unwrap()
251    }
252
253    /// Attempts to de-activate a plugin instance, which [`PluginAudioProcessor`] handle has been
254    /// dropped.
255    ///
256    /// This is equivalent to [`Self::try_deactivate`], except this method also takes a closure type `D`,
257    /// which allows to customize the destruction process of the [`AudioProcessor`](HostHandlers::AudioProcessor) callback
258    /// handler instance that was created during [`activate`](Self::activate).
259    ///
260    /// This is an alternative to [`deactivate_with`](Self::deactivate_with) which does not require the associated
261    /// [`PluginAudioProcessorHandle`] to be passed. However, this method is fallible, and will
262    /// return an error if the [`PluginAudioProcessorHandle`] has not been dropped.
263    ///
264    /// This may be useful if it is more practical for you to drop the [`PluginAudioProcessorHandle`]
265    /// (e.g. in another thread) compared to sending it back to the main thread to use with
266    /// [`deactivate_with`](Self::deactivate_with).
267    ///
268    /// However, if you can easily access or retrieve [`PluginAudioProcessorHandle`] back to the main
269    /// thread, you should use [`deactivate_with`](Self::deactivate_with) instead.
270    ///
271    /// # Errors
272    ///
273    /// This method will return [`PluginInstanceError::StillActivatedPlugin`] if the associated
274    /// [`PluginAudioProcessorHandle`] has not been dropped.
275    pub fn try_deactivate_with<T, D>(&mut self, drop_with: D) -> Result<T, PluginInstanceError>
276    where
277        D: for<'s> FnOnce(
278            <H as HostHandlers>::AudioProcessor<'s>,
279            &<H as HostHandlers>::MainThread<'s>,
280        ) -> T,
281    {
282        let wrapper =
283            Arc::get_mut(&mut self.inner).ok_or(PluginInstanceError::StillActivatedPlugin)?;
284
285        wrapper.deactivate_with(drop_with)
286    }
287
288    /// Calls the plugin's `on_main_thread` callback.
289    ///
290    /// This is usually done in response to the plugin calling [`SharedHandler::request_callback`]
291    /// earlier from a different thread, allowing it to "wake up" its main thread after e.g. another
292    /// of the plugin's threads sent a message to it.
293    #[inline]
294    pub fn call_on_main_thread_callback(&mut self) {
295        // SAFETY: this is done on the main thread, and the &mut reference guarantees no aliasing
296        unsafe { self.inner.on_main_thread() }
297    }
298
299    /// Returns a shared, read-only reference to the raw, C-FFI compatible representation
300    /// of thsi plugin instance.
301    #[inline]
302    pub fn raw_instance(&self) -> &clap_plugin {
303        self.inner.raw_instance()
304    }
305
306    /// Returns the [`PluginEntry`] this plugin instance was loaded from.
307    #[inline]
308    pub fn entry(&self) -> &PluginEntry {
309        self.inner.entry()
310    }
311
312    /// Returns `true` if the current plugin instance has been activated.
313    #[inline]
314    pub fn is_active(&self) -> bool {
315        self.inner.is_active()
316    }
317
318    /// Access the [`SharedHandler`] instance associated to this plugin instance using the given callback.
319    ///
320    /// The callback's return value `R` is returned directly by this method.
321    ///
322    /// Accessing the [`HostHandlers`] types can only be done with accessor callbacks because this
323    /// type is self-referential: both the [`HostHandlers`] and the plugin's instance data hold
324    /// references to each other, and both are owned by this type.
325    #[inline]
326    pub fn access_shared_handler<'s, R>(
327        &'s self,
328        access: impl for<'a> FnOnce(&'s <H as HostHandlers>::Shared<'a>) -> R,
329    ) -> R {
330        access(self.inner.wrapper().shared())
331    }
332
333    /// Access a shared reference to [`MainThreadHandler`] instance associated to this plugin instance using the given callback.
334    ///
335    /// The callback's return value `R` is returned directly by this method.
336    ///
337    /// Accessing the [`HostHandlers`] types can only be done with accessor callbacks because this
338    /// type is self-referential: both the [`HostHandlers`] and the plugin's instance data hold
339    /// references to each other, and both are owned by this type.
340    #[inline]
341    pub fn access_handler<R>(
342        &self,
343        access: impl for<'a> FnOnce(&<H as HostHandlers>::MainThread<'a>) -> R,
344    ) -> R {
345        // SAFETY: This type guarantees it can only be called on the main thread
346        let Some(result) = (unsafe { self.inner.wrapper().on_main_thread(access) }) else {
347            // PANIC: main_thread can only be None if called from within init()
348            unreachable!()
349        };
350
351        result
352    }
353
354    /// Returns a thread-safe handle to the plugin.
355    ///
356    /// Unlike the [`PluginMainThreadHandle`] returned by [`plugin_handle`](Self::plugin_handle),
357    /// this handle can be cloned and sent to other threads.
358    ///
359    /// However, it has fewer capabilities, and cannot be used for operations that have to be
360    /// performed on the main-thread (e.g. GUI).
361    #[inline]
362    pub fn plugin_shared_handle(&self) -> PluginSharedHandle<'_> {
363        self.inner.plugin_shared()
364    }
365
366    /// Returns a main-thread handle to the plugin.
367    #[inline]
368    pub fn plugin_handle(&mut self) -> PluginMainThreadHandle<'_> {
369        // SAFETY: this type can only exist on the main thread.
370        unsafe { PluginMainThreadHandle::new(self.inner.raw_instance().into()) }
371    }
372
373    /// Returns a main-thread handle to the plugin, that can only exist while the plugin is in the 'inactive' state.
374    ///
375    /// Calling this method also prevents the plugin instance to be activated as long as the handle exists.
376    ///
377    /// If the plugin is actually activated, this returns `None`.
378    #[inline]
379    pub fn inactive_plugin_handle(&mut self) -> Option<InactivePluginMainThreadHandle<'_>> {
380        if self.inner.is_active() {
381            return None;
382        }
383
384        // SAFETY: this type can only exist on the main thread.
385        // We also checked above that the plugin isn't in the active state, and the handle keeps
386        // this instance mutably borrowed, so it is not possible to activate the plugin while the
387        // handle still exists.
388        Some(unsafe { InactivePluginMainThreadHandle::new(self.inner.raw_instance().into()) })
389    }
390}
391
392impl<H: HostHandlers> Drop for PluginInstance<H> {
393    fn drop(&mut self) {
394        // Only drop our Arc if we are the sole owner.
395        // This leaks the plugin instance, but prevents accidentally transferring ownership to the
396        // audio thread if the audio processor handle is still around somewhere.
397        if Arc::get_mut(&mut self.inner).is_some() {
398            // SAFETY: We can only call this once (as we're in Drop), and we never use the inner
399            // value again afterward.
400            unsafe { ManuallyDrop::drop(&mut self.inner) }
401        };
402    }
403}
404
405#[cfg(test)]
406mod test {
407    extern crate static_assertions as sa;
408    use super::*;
409
410    sa::assert_not_impl_any!(PluginInstance<()>: Send, Sync);
411}