clap_clap/
plugin.rs

1use std::{
2    ffi::NulError,
3    fmt::Display,
4    iter::empty,
5    marker::PhantomData,
6    sync::{
7        Arc, Mutex,
8        atomic::{AtomicBool, Ordering},
9    },
10};
11
12use crate::{
13    ext::{Extensions, audio_ports::PluginAudioPorts},
14    ffi::clap_plugin,
15    host::Host,
16    process,
17    process::{Process, Status::Continue},
18};
19
20pub trait Plugin: Default + Extensions<Self> {
21    type AudioThread: AudioThread<Self>;
22
23    const ID: &'static str;
24    const NAME: &'static str;
25    const VENDOR: &'static str = "";
26    const URL: &'static str = "";
27    const MANUAL_URL: &'static str = "";
28    const SUPPORT_URL: &'static str = "";
29    const VERSION: &'static str = "";
30    const DESCRIPTION: &'static str = "";
31
32    /// Plugin features as an arbitrary list of keywords.
33    ///
34    /// They can be matched by the host indexer and used to classify the plugin.
35    /// For some standard features, see module: [`plugin_features`].
36    ///
37    /// The default implementation returns an empty iterator.
38    ///
39    /// # Example
40    ///
41    /// ```no_compile,rust
42    /// fn features() -> impl Iterator<Item = &'static str> {
43    ///     "instrument stereo sampler".split_whitespace()
44    /// }
45    /// ```
46    ///
47    /// [`plugin_features`]: crate::plugin_features
48    fn features() -> impl Iterator<Item = &'static str> {
49        empty()
50    }
51
52    #[allow(unused_variables)]
53    fn init(&mut self, host: Arc<Host>) -> Result<(), crate::Error> {
54        Ok(())
55    }
56
57    fn activate(
58        &mut self,
59        sample_rate: f64,
60        min_frames: u32,
61        max_frames: u32,
62    ) -> Result<Self::AudioThread, crate::Error>;
63
64    fn on_main_thread(&mut self) {}
65}
66
67pub trait AudioThread<P: Plugin>: Send + Sync + Sized {
68    fn start_processing(&mut self) -> Result<(), crate::Error> {
69        Ok(())
70    }
71
72    fn stop_processing(&mut self) {}
73
74    fn process(&mut self, process: &mut Process) -> Result<process::Status, crate::Error>;
75
76    fn reset(&mut self) {}
77
78    #[allow(unused_variables)]
79    fn deactivate(self, plugin: &mut P) {}
80}
81
82impl<P: Plugin> AudioThread<P> for () {
83    fn process(&mut self, _: &mut Process) -> Result<process::Status, crate::Error> {
84        Ok(Continue)
85    }
86}
87
88struct PluginExtensions<P> {
89    audio_ports: Option<PluginAudioPorts<P>>,
90    latency: Option<PluginLatency<P>>,
91    note_ports: Option<PluginNotePorts<P>>,
92    params: Option<PluginParams<P>>,
93    state: Option<PluginState<P>>,
94    tail: Option<PluginTail<P>>,
95}
96
97impl<P: Plugin> PluginExtensions<P> {
98    fn new() -> Self {
99        Self {
100            audio_ports: <P as Extensions<P>>::audio_ports().map(PluginAudioPorts::new),
101            latency: <P as Extensions<P>>::latency().map(PluginLatency::new),
102            note_ports: <P as Extensions<P>>::note_ports().map(PluginNotePorts::new),
103            params: <P as Extensions<P>>::params().map(PluginParams::new),
104            state: <P as Extensions<P>>::state().map(PluginState::new),
105            tail: <P as Extensions<P>>::tail().map(PluginTail::new),
106        }
107    }
108}
109
110pub(crate) struct Runtime<P: Plugin> {
111    pub(crate) active: AtomicBool,
112    pub(crate) audio_thread: Option<P::AudioThread>,
113    pub(crate) descriptor: PluginDescriptor,
114    pub(crate) host: Arc<Host>,
115    pub(crate) plugin: P,
116    plugin_extensions: Mutex<PluginExtensions<P>>,
117}
118
119impl<P: Plugin> Runtime<P> {
120    pub(crate) fn initialize(host: Arc<Host>) -> Result<Self, Error> {
121        Ok(Self {
122            active: AtomicBool::new(false),
123            descriptor: PluginDescriptor::new::<P>()?,
124            plugin: P::default(),
125            audio_thread: None,
126            host,
127            plugin_extensions: Mutex::new(PluginExtensions::new()),
128        })
129    }
130
131    pub(crate) fn into_clap_plugin(self) -> ClapPlugin<P> {
132        // Safety:
133        // The leaked (via Box::into_raw) pointer satisfies requirements
134        // for a safe call to ClapPlugin::new():
135        // 1. it is non-null
136        // 2. it represents a valid clap_plugin tied to type P.
137        unsafe { ClapPlugin::new_unchecked(Box::into_raw(ffi::box_clap_plugin(self))) }
138    }
139
140    /// Retake ownership of the runtime from the pointer to  clap_plugin.
141    ///
142    /// # Safety:
143    ///
144    /// The caller must assure it's only them who have access to the entire
145    /// runtime: both main thread and the audio thread.
146    /// This can requirement can be met during plugin initialization and
147    /// destruction.
148    unsafe fn from_clap_plugin(clap_plugin: ClapPlugin<P>) -> Self {
149        let plugin_data = unsafe { clap_plugin.clap_plugin() }.plugin_data as *mut _;
150        // Safety:
151        // We can transmute the pointer to plugin_data like this, because:
152        // 1. We have exclusive reference to it.
153        // 2. We know the pointer's real type because of the constraints put on the
154        //    constructor of ClapPlugin.
155        // 3. We know that the pointer was initially leaked with Box::into_raw().
156        *unsafe { Box::from_raw(plugin_data) }
157    }
158}
159
160#[doc(hidden)]
161/// Safe wrapper around a pointer to clap_plugin.
162pub struct ClapPlugin<P: Plugin> {
163    clap_plugin: *const clap_plugin,
164    _marker: PhantomData<P>,
165}
166
167impl<P: Plugin> ClapPlugin<P> {
168    /// # Safety
169    ///
170    /// 1. The user must assure the pointer to plugin is non-null.
171    /// 2. The pointer must point to a valid clap_plugin structure tied to the
172    ///    plugin type P, and living in the host.
173    /// 3. There must be only one instance of ClapPlugin for a given pointer.
174    ///
175    /// Typically, a valid pointer comes from the host calling the plugin's
176    /// methods, or from Runtime::into_clap_plugin()
177    pub const unsafe fn new_unchecked(clap_plugin: *const clap_plugin) -> Self {
178        Self {
179            clap_plugin,
180            _marker: PhantomData,
181        }
182    }
183
184    /// # Safety
185    ///
186    /// The caller must ensure that the wrapped pointer to clap_plugin is
187    /// dereferencable and that Rust aliasing rules of shared references hold.
188    #[doc(hidden)]
189    pub const unsafe fn clap_plugin<'a>(&self) -> &'a clap_plugin {
190        // SAFETY: ClapPlugin constructor guarantees that dereferencing the inner
191        // pointer is safe.
192        unsafe { &*self.clap_plugin }
193    }
194
195    pub(crate) const fn into_inner(self) -> *const clap_plugin {
196        self.clap_plugin
197    }
198
199    /// Obtain a mutable reference to the entire runtime.
200    ///
201    /// # Safety
202    ///
203    /// The caller must assure they're the only ones who access the runtime.
204    pub(crate) const unsafe fn runtime(&mut self) -> &mut Runtime<P> {
205        let runtime: *mut Runtime<P> = unsafe { *self.clap_plugin }.plugin_data as *mut _;
206        unsafe { &mut *runtime }
207    }
208
209    pub fn is_active(&self) -> bool {
210        let runtime: *mut Runtime<P> = unsafe { *self.clap_plugin }.plugin_data as *mut _;
211        unsafe { (*runtime).active.load(Ordering::Acquire) }
212    }
213
214    /// Obtain a mutable reference to plugin.
215    ///
216    /// # Safety
217    ///
218    /// The caller must assure they're the only ones who access the plugin.
219    pub const unsafe fn plugin(&mut self) -> &mut P {
220        let runtime: *mut Runtime<P> = unsafe { *self.clap_plugin }.plugin_data as *mut _;
221        unsafe { &mut (*runtime).plugin }
222    }
223
224    /// Obtain a mutable reference to audio thread.
225    ///
226    /// # Safety
227    ///
228    /// The caller must assure they're the only ones who access the
229    /// audio_thread.
230    pub const unsafe fn audio_thread(&mut self) -> Option<&mut P::AudioThread> {
231        let runtime: *mut Runtime<P> = unsafe { *self.clap_plugin }.plugin_data as *mut _;
232        unsafe { &mut (*runtime).audio_thread }.as_mut()
233    }
234
235    /// Obtain a mutex to plugin extensions.
236    const fn plugin_extensions(&mut self) -> &Mutex<PluginExtensions<P>> {
237        let runtime: *mut Runtime<P> = unsafe { *self.clap_plugin }.plugin_data as *mut _;
238        unsafe { &(*runtime).plugin_extensions }
239    }
240}
241
242mod desc {
243    use std::{
244        ffi::{CStr, CString, c_char},
245        ptr::null,
246    };
247
248    use crate::{
249        ffi::{CLAP_VERSION, clap_plugin_descriptor},
250        plugin::{Error, Plugin},
251    };
252
253    #[allow(dead_code)]
254    pub struct PluginDescriptor {
255        clap_plugin_descriptor: clap_plugin_descriptor,
256        clap_features: Box<[*const c_char]>,
257
258        id: CString,
259        name: CString,
260        vendor: CString,
261        url: CString,
262        manual_url: CString,
263        support_url: CString,
264        version: CString,
265        description: CString,
266        features: Box<[CString]>,
267    }
268
269    impl PluginDescriptor {
270        pub fn new<P: Plugin>() -> Result<Self, Error> {
271            let id = CString::new(P::ID)?;
272            let name = CString::new(P::NAME)?;
273            let vendor = CString::new(P::VENDOR)?;
274            let url = CString::new(P::URL)?;
275            let manual_url = CString::new(P::MANUAL_URL)?;
276            let support_url = CString::new(P::SUPPORT_URL)?;
277            let version = CString::new(P::VERSION)?;
278            let description = CString::new(P::DESCRIPTION)?;
279
280            let features: Box<[CString]> =
281                P::features().map(CString::new).collect::<Result<_, _>>()?;
282            let mut clap_features: Vec<*const c_char> =
283                features.iter().map(|s| s.as_c_str().as_ptr()).collect();
284            clap_features.push(null());
285            let clap_features = clap_features.into_boxed_slice();
286
287            Ok(Self {
288                clap_plugin_descriptor: clap_plugin_descriptor {
289                    clap_version: CLAP_VERSION,
290                    id: id.as_c_str().as_ptr(),
291                    name: name.as_c_str().as_ptr(),
292                    vendor: vendor.as_c_str().as_ptr(),
293                    url: url.as_c_str().as_ptr(),
294                    manual_url: manual_url.as_c_str().as_ptr(),
295                    support_url: support_url.as_c_str().as_ptr(),
296                    version: version.as_c_str().as_ptr(),
297                    description: description.as_c_str().as_ptr(),
298                    features: clap_features.as_ptr(),
299                },
300                clap_features,
301                id,
302                name,
303                vendor,
304                url,
305                manual_url,
306                support_url,
307                version,
308                description,
309                features,
310            })
311        }
312
313        pub fn plugin_id(&self) -> &CStr {
314            self.id.as_c_str()
315        }
316
317        pub const fn clap_plugin_descriptor(&self) -> &clap_plugin_descriptor {
318            &self.clap_plugin_descriptor
319        }
320    }
321}
322
323#[doc(hidden)]
324pub use desc::PluginDescriptor;
325
326use crate::ext::{
327    latency::PluginLatency, note_ports::PluginNotePorts, params::PluginParams, state::PluginState,
328    tail::PluginTail,
329};
330
331mod ffi {
332    use std::{
333        ffi::{CStr, c_char, c_void},
334        mem,
335        ptr::{NonNull, null},
336        sync::atomic::Ordering,
337    };
338
339    use crate::{
340        ffi::{
341            CLAP_EXT_AUDIO_PORTS, CLAP_EXT_LATENCY, CLAP_EXT_NOTE_PORTS, CLAP_EXT_PARAMS,
342            CLAP_EXT_STATE, CLAP_EXT_TAIL, CLAP_PROCESS_ERROR, clap_plugin, clap_process,
343            clap_process_status,
344        },
345        plugin::{AudioThread, ClapPlugin, Plugin, Runtime},
346        process::Process,
347    };
348
349    #[allow(warnings, unused)]
350    unsafe extern "C-unwind" fn init<P: Plugin>(plugin: *const clap_plugin) -> bool {
351        if plugin.is_null() {
352            return false;
353        }
354        // SAFETY: We just checked that the pointer is non-null and the plugin
355        // has been obtained from host, and is tied to type P.
356        let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
357
358        // SAFETY: This function is called on the main thread during the initialization.
359        // It is guaranteed that we are the only function accessing the entire runtime.
360        let runtime = unsafe { clap_plugin.runtime() };
361        let host = runtime.host.clone();
362
363        runtime.plugin.init(host).is_ok()
364    }
365
366    unsafe extern "C-unwind" fn destroy<P: Plugin>(plugin: *const clap_plugin) {
367        if plugin.is_null() {
368            return;
369        }
370        // SAFETY: We just checked that the pointer is non-null and the plugin
371        // has been obtained from host and is tied to type P.
372        let clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
373
374        // SAFETY: This function is called on the main thread to destroy the plugin.
375        // It is guaranteed that we are the only function accessing the runtime now.
376        // So retaking the ownership of the runtime is safe.
377        let runtime = unsafe { Runtime::from_clap_plugin(clap_plugin) };
378
379        drop(runtime)
380    }
381
382    unsafe extern "C-unwind" fn activate<P: Plugin>(
383        plugin: *const clap_plugin,
384        sample_rate: f64,
385        min_frames_count: u32,
386        max_frames_count: u32,
387    ) -> bool {
388        if plugin.is_null() {
389            return false;
390        }
391        // SAFETY: We just checked that the pointer is non-null and the plugin
392        // has been obtained from host and is tied to type P.
393        let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
394
395        // SAFETY: This function is called on the main thread. It is guaranteed that we
396        // are the only function accessing runtime now, because the audio thread
397        // hasn't started yet. So a mutable reference to runtime is safe.
398        let runtime = unsafe { clap_plugin.runtime() };
399        let (plugin, audio_thread) = (&mut runtime.plugin, &mut runtime.audio_thread);
400
401        let should_be_none = mem::replace(
402            audio_thread,
403            plugin
404                .activate(sample_rate, min_frames_count, max_frames_count)
405                .ok(),
406        );
407
408        (should_be_none.is_none() && audio_thread.is_some())
409            .then(|| runtime.active.store(true, Ordering::Release))
410            .is_some()
411    }
412
413    unsafe extern "C-unwind" fn deactivate<P: Plugin>(plugin: *const clap_plugin) {
414        if plugin.is_null() {
415            return;
416        }
417        // SAFETY: We just checked that the pointer is non-null and the plugin
418        // has been obtained from host and is tied to type P.
419        let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
420
421        // SAFETY: This function is called on the main thread.
422        // It is guaranteed that we are the only function accessing runtime.audio_thread
423        // now, and we are on the main thread -- so it is guaranteed we are the only
424        // function that has access to the entire runtime now.
425        // So the mutable reference to the entire runtime for the duration of this call
426        // is safe.
427        let runtime = unsafe { clap_plugin.runtime() };
428
429        if let Some(audio_thread) = runtime.audio_thread.take() {
430            audio_thread.deactivate(&mut runtime.plugin);
431        }
432
433        runtime.active.store(false, Ordering::Release)
434    }
435
436    unsafe extern "C-unwind" fn start_processing<P: Plugin>(plugin: *const clap_plugin) -> bool {
437        if plugin.is_null() {
438            return false;
439        }
440        // SAFETY: We just checked that the pointer is non-null and the plugin
441        // has been obtained from host and is tied to type P.
442        let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
443
444        // SAFETY: This function is called on the audio thread.  It is guaranteed that
445        // we are the only function accessing audio_thread now. So a mutable reference
446        // to audio_thread for the duration of this call is safe.
447        let Some(audio_thread) = (unsafe { clap_plugin.audio_thread() }) else {
448            return false;
449        };
450
451        audio_thread.start_processing().is_ok()
452    }
453
454    unsafe extern "C-unwind" fn stop_processing<P: Plugin>(plugin: *const clap_plugin) {
455        if plugin.is_null() {
456            return;
457        }
458        // SAFETY: We just checked that the pointer is non-null and the plugin
459        // has been obtained from host and is tied to type P.
460        let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
461
462        // SAFETY: This function is called on the audio thread.  It is guaranteed that
463        // we are the only function accessing audio_thread now. So a mutable reference
464        // to audio_thread for the duration of this call is safe.
465        let Some(audio_thread) = (unsafe { clap_plugin.audio_thread() }) else {
466            return;
467        };
468
469        audio_thread.stop_processing();
470    }
471
472    unsafe extern "C-unwind" fn reset<P: Plugin>(plugin: *const clap_plugin) {
473        if plugin.is_null() {
474            return;
475        }
476        // SAFETY: We just checked that the pointer is non-null and the plugin
477        // has been obtained from host and is tied to type P.
478        let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
479
480        // SAFETY: This function is called on the audio thread.  It is guaranteed that
481        // we are the only function accessing audio_thread now. So a mutable reference
482        // to audio_thread for the duration of this call is safe.
483        let Some(audio_thread) = (unsafe { clap_plugin.audio_thread() }) else {
484            return;
485        };
486
487        audio_thread.reset();
488    }
489
490    #[allow(warnings, unused)]
491    unsafe extern "C-unwind" fn process<P: Plugin>(
492        plugin: *const clap_plugin,
493        process: *const clap_process,
494    ) -> clap_process_status {
495        if plugin.is_null() {
496            return CLAP_PROCESS_ERROR;
497        }
498        // SAFETY: We just checked that the pointer is non-null and the plugin
499        // has been obtained from host, and is tied to type P.
500        let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
501
502        // SAFETY: This function is called on the audio thread.  It is guaranteed that
503        // we are the only function accessing audio_thread now. So a mutable reference
504        // to audio_thread for the duration of this call is safe.
505        let Some(audio_thread) = (unsafe { clap_plugin.audio_thread() }) else {
506            return CLAP_PROCESS_ERROR;
507        };
508
509        if process.is_null() {
510            return CLAP_PROCESS_ERROR;
511        }
512        // SAFETY: The pointer to clap_process is guaranteed to be valid and pointing
513        // to an exclusive struct for the duration of this call.
514        // So a mutable reference to process is safe.
515        let process = unsafe { &mut *(process as *mut _) };
516        let process = &mut unsafe { Process::new_unchecked(NonNull::new_unchecked(process)) };
517        audio_thread
518            .process(process)
519            .map(Into::into)
520            .unwrap_or(CLAP_PROCESS_ERROR)
521    }
522
523    #[allow(warnings, unused)]
524    unsafe extern "C-unwind" fn get_extension<P: Plugin>(
525        plugin: *const clap_plugin,
526        id: *const c_char,
527    ) -> *const c_void {
528        if plugin.is_null() {
529            return null();
530        }
531        // SAFETY: We just checked that the pointer is non-null and the plugin
532        // has been obtained from host and is tied to type P.
533        let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
534
535        // SAFETY: The plugin id is a valid C string obtained from the host.  The C
536        // string lifetime extends for the duration of this function call.
537        let id = unsafe { CStr::from_ptr(id) };
538
539        // SAFETY: This function must be thread-safe.
540        // We're accessing only runtime.plugin_extensions that is guarded by a Mutex.
541        let mutex = clap_plugin.plugin_extensions();
542        let Ok(extensions) = mutex.lock() else {
543            return null();
544        };
545
546        if id == CLAP_EXT_AUDIO_PORTS {
547            if let Some(ext) = &extensions.audio_ports {
548                return (&raw const *ext).cast();
549            }
550        } else if id == CLAP_EXT_NOTE_PORTS {
551            if let Some(ext) = &extensions.note_ports {
552                return (&raw const *ext).cast();
553            }
554        } else if id == CLAP_EXT_LATENCY {
555            if let Some(ext) = &extensions.latency {
556                return (&raw const *ext).cast();
557            }
558        } else if id == CLAP_EXT_PARAMS {
559            if let Some(ext) = &extensions.params {
560                return (&raw const *ext).cast();
561            }
562        } else if id == CLAP_EXT_STATE {
563            if let Some(ext) = &extensions.state {
564                return (&raw const *ext).cast();
565            }
566        } else if id == CLAP_EXT_TAIL {
567            if let Some(ext) = &extensions.tail {
568                return (&raw const *ext).cast();
569            }
570        }
571
572        null()
573    }
574
575    unsafe extern "C-unwind" fn on_main_thread<P: Plugin>(plugin: *const clap_plugin) {
576        if plugin.is_null() {
577            return;
578        }
579        // SAFETY: We just checked that the pointer is non-null and the plugin
580        // has been obtained from host and is tied to type P.
581        let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
582
583        // SAFETY: This function is called on the main thread.
584        // It is guaranteed that we are the only function accessing the plugin now.
585        // So the mutable reference to plugin for the duration of this call is safe.
586        let plugin = unsafe { clap_plugin.plugin() };
587
588        plugin.on_main_thread();
589    }
590
591    pub(crate) fn box_clap_plugin<P: Plugin>(data: Runtime<P>) -> Box<clap_plugin> {
592        let data = Box::new(data);
593        let desc = &raw const *data.descriptor.clap_plugin_descriptor();
594        let data = Box::into_raw(data);
595
596        Box::new(clap_plugin {
597            desc,
598            plugin_data: data as *mut _,
599            init: Some(init::<P>),
600            destroy: Some(destroy::<P>),
601            activate: Some(activate::<P>),
602            deactivate: Some(deactivate::<P>),
603            start_processing: Some(start_processing::<P>),
604            stop_processing: Some(stop_processing::<P>),
605            reset: Some(reset::<P>),
606            process: Some(process::<P>),
607            get_extension: Some(get_extension::<P>),
608            on_main_thread: Some(on_main_thread::<P>),
609        })
610    }
611}
612
613#[derive(Debug, Clone, PartialEq)]
614pub enum Error {
615    MissingFields,
616    NulError(NulError),
617}
618
619impl Display for Error {
620    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
621        match self {
622            Error::MissingFields => write!(f, "missing fields in plugin description"),
623            Error::NulError(_) => write!(f, "null error while converting C string"),
624        }
625    }
626}
627
628impl std::error::Error for Error {}
629
630impl From<NulError> for Error {
631    fn from(value: NulError) -> Self {
632        Self::NulError(value)
633    }
634}
635
636impl From<Error> for crate::Error {
637    fn from(value: Error) -> Self {
638        Self::Plugin(value)
639    }
640}