Skip to main content

nice_plug/wrapper/
clap.rs

1#[macro_use]
2mod util;
3
4mod context;
5mod descriptor;
6pub mod features;
7mod wrapper;
8
9use crate::wrapper::clap::features::ClapFeature;
10
11/// Re-export for the macro
12pub use self::descriptor::PluginDescriptor;
13pub use self::wrapper::Wrapper;
14pub use clap_sys::entry::clap_plugin_entry;
15pub use clap_sys::factory::plugin_factory::{CLAP_PLUGIN_FACTORY_ID, clap_plugin_factory};
16pub use clap_sys::host::clap_host;
17pub use clap_sys::plugin::{clap_plugin, clap_plugin_descriptor};
18pub use clap_sys::version::CLAP_VERSION;
19use nice_plug_core::context::remote_controls::RemoteControlsContext;
20use nice_plug_core::plugin::Plugin;
21
22/// Provides auxiliary metadata needed for a CLAP plugin.
23#[allow(unused_variables)]
24pub trait ClapPlugin: Plugin {
25    /// A unique ID that identifies this particular plugin. This is usually in reverse domain name
26    /// notation, e.g. `com.manufacturer.plugin-name`.
27    const CLAP_ID: &'static str;
28    /// An optional short description for the plugin.
29    const CLAP_DESCRIPTION: Option<&'static str>;
30    /// The URL to the plugin's manual, if available.
31    const CLAP_MANUAL_URL: Option<&'static str>;
32    /// The URL to the plugin's support page, if available.
33    const CLAP_SUPPORT_URL: Option<&'static str>;
34    /// Keywords describing the plugin. The host may use this to classify the plugin in its plugin
35    /// browser.
36    const CLAP_FEATURES: &'static [ClapFeature];
37
38    /// If set, this informs the host about the plugin's capabilities for polyphonic modulation.
39    const CLAP_POLY_MODULATION_CONFIG: Option<PolyModulationConfig> = None;
40
41    /// This function can be implemented to define plugin-specific [remote control
42    /// pages](https://github.com/free-audio/clap/blob/main/include/clap/ext/draft/remote-controls.h)
43    /// that the host can use to provide better hardware mapping for a plugin. See the linked
44    /// extension for more information.
45    fn remote_controls(&self, context: &mut impl RemoteControlsContext) {}
46}
47
48/// Configuration for the plugin's polyphonic modulation options, if it supports .
49pub struct PolyModulationConfig {
50    /// The maximum number of voices this plugin will ever use. Call the context's
51    /// `set_current_voice_capacity()` method during initialization or audio processing to set the
52    /// polyphony limit.
53    pub max_voice_capacity: u32,
54    /// If set to `true`, then the host may send note events for the same channel and key, but using
55    /// different voice IDs. Bitwig Studio, for instance, can use this to do voice stacking. After
56    /// enabling this, you should always prioritize using voice IDs to map note events to voices.
57    pub supports_overlapping_voices: bool,
58}
59
60/// Export one or more CLAP plugins from this library using the provided plugin types.
61#[macro_export]
62macro_rules! nice_export_clap {
63    ($($plugin_ty:ty),+) => {
64        // Earlier versions used a simple generic struct for this, but because we don't have
65        // variadic generics (yet) we can't generate the struct for multiple plugin types without
66        // macros. So instead we'll generate the implementation ad-hoc inside of this macro.
67        #[doc(hidden)]
68        mod clap {
69            use $crate::prelude::nice_debug_assert_eq;
70            use $crate::wrapper::setup_logger;
71            use $crate::wrapper::clap::{PluginDescriptor, Wrapper};
72            use $crate::wrapper::clap::{CLAP_PLUGIN_FACTORY_ID, clap_host, clap_plugin, clap_plugin_descriptor, clap_plugin_factory};
73            use ::std::collections::HashSet;
74            use ::std::ffi::{CStr, c_void};
75            use ::std::os::raw::c_char;
76            use ::std::sync::{Arc, OnceLock};
77
78            // Because the `$plugin_ty`s are likely defined in the enclosing scope. This works even
79            // if the types are not public because this is a child module.
80            use super::*;
81
82            const CLAP_PLUGIN_FACTORY: clap_plugin_factory = clap_plugin_factory {
83                get_plugin_count: Some(get_plugin_count),
84                get_plugin_descriptor: Some(get_plugin_descriptor),
85                create_plugin: Some(create_plugin),
86            };
87
88            // Sneaky way to get the number of expanded elements
89            const PLUGIN_COUNT: usize = [$(stringify!($plugin_ty)),+].len();
90
91            // This is a type erased version of the information stored on the plugin types
92            static PLUGIN_DESCRIPTORS: OnceLock<[PluginDescriptor; PLUGIN_COUNT]> = OnceLock::new();
93
94            fn plugin_descriptors() -> &'static [PluginDescriptor; PLUGIN_COUNT] {
95                PLUGIN_DESCRIPTORS.get_or_init(|| {
96                    let descriptors = [$(PluginDescriptor::for_plugin::<$plugin_ty>()),+];
97
98                    if cfg!(debug_assertions) {
99                        let unique_plugin_ids: HashSet<_> = descriptors.iter().map(|d| d.clap_id()).collect();
100                        nice_debug_assert_eq!(
101                            unique_plugin_ids.len(),
102                            descriptors.len(),
103                            "Duplicate plugin IDs found in `nice_export_clap!()` call"
104                        );
105                    }
106
107                    descriptors
108                })
109            }
110
111            unsafe extern "C" fn get_plugin_count(_factory: *const clap_plugin_factory) -> u32 {
112                plugin_descriptors().len() as u32
113            }
114
115            unsafe extern "C" fn get_plugin_descriptor (
116                _factory: *const clap_plugin_factory,
117                index: u32,
118            ) -> *const clap_plugin_descriptor  {
119                match plugin_descriptors().get(index as usize) {
120                    Some(descriptor) => descriptor.clap_plugin_descriptor(),
121                    None => ::std::ptr::null()
122                }
123            }
124
125            unsafe extern "C" fn create_plugin (
126                _factory: *const clap_plugin_factory,
127                host: *const clap_host,
128                plugin_id: *const c_char,
129            ) -> *const clap_plugin  {
130                if plugin_id.is_null() {
131                    return ::std::ptr::null();
132                }
133                let plugin_id_cstr = unsafe { CStr::from_ptr(plugin_id) };
134
135                // This isn't great, but we'll just assume that `$plugin_ids` and the descriptors
136                // are in the same order. We also can't directly enumerate over them with an index,
137                // which is why we do things the way we do. Otherwise we could have used a tuple
138                // instead.
139                let descriptors = plugin_descriptors();
140                let mut descriptor_idx = 0;
141                $({
142                    let descriptor = &descriptors[descriptor_idx];
143                    if plugin_id_cstr == descriptor.clap_id() {
144                        // Arc does not have a convenient leak function like Box, so this gets a bit awkward
145                        // This pointer gets turned into an Arc and its reference count decremented in
146                        // [Wrapper::destroy()]
147                        return unsafe {(*Arc::into_raw(Wrapper::<$plugin_ty>::new(host)))
148                            .clap_plugin
149                            .as_ptr()};
150                    }
151
152                    descriptor_idx += 1;
153                })+
154
155                ::std::ptr::null()
156            }
157
158            pub extern "C" fn init(_plugin_path: *const c_char) -> bool {
159                setup_logger();
160                true
161            }
162
163            pub extern "C" fn deinit() {}
164
165            pub extern "C" fn get_factory(factory_id: *const c_char) -> *const c_void {
166                if !factory_id.is_null() && unsafe { CStr::from_ptr(factory_id) } == CLAP_PLUGIN_FACTORY_ID {
167                    &CLAP_PLUGIN_FACTORY as *const _ as *const c_void
168                } else {
169                    ::std::ptr::null()
170                }
171            }
172        }
173
174        /// The CLAP plugin's entry point.
175        #[unsafe(no_mangle)]
176        #[used]
177        pub static clap_entry: $crate::wrapper::clap::clap_plugin_entry =
178            $crate::wrapper::clap::clap_plugin_entry {
179                clap_version: $crate::wrapper::clap::CLAP_VERSION,
180                init: Some(self::clap::init),
181                deinit: Some(self::clap::deinit),
182                get_factory: Some(self::clap::get_factory),
183            };
184    };
185}