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