1use crate::extensions::PluginExtensions;
2use crate::extensions::wrapper::{PluginWrapper, PluginWrapperError, handle_panic};
3use crate::host::{HostInfo, HostMainThreadHandle, HostSharedHandle};
4use crate::plugin::instance::WrapperData::*;
5use crate::plugin::{Plugin, PluginAudioProcessor, PluginError, PluginMainThread};
6use crate::prelude::PluginDescriptor;
7use crate::process::{Audio, Events, PluginAudioConfiguration, Process};
8use clap_sys::plugin::{clap_plugin, clap_plugin_descriptor};
9use clap_sys::process::{CLAP_PROCESS_ERROR, clap_process, clap_process_status};
10use core::ffi::c_void;
11use std::cell::UnsafeCell;
12use std::ffi::CStr;
13use std::marker::PhantomData;
14use std::mem::ManuallyDrop;
15use std::sync::atomic::{AtomicU8, Ordering};
16
17pub(crate) trait PluginInitializer<'a, P: Plugin>: 'a {
18 fn init(
19 self: Box<Self>,
20 host: HostMainThreadHandle<'a>,
21 ) -> Result<PluginWrapper<'a, P>, PluginError>;
22}
23
24impl<'a, P: Plugin, FS: 'a, FM: 'a> PluginInitializer<'a, P> for (FS, FM)
25where
26 FS: FnOnce(HostSharedHandle<'a>) -> Result<P::Shared<'a>, PluginError>,
27 FM: FnOnce(
28 HostMainThreadHandle<'a>,
29 &'a P::Shared<'a>,
30 ) -> Result<P::MainThread<'a>, PluginError>,
31{
32 fn init(
33 self: Box<Self>,
34 host: HostMainThreadHandle<'a>,
35 ) -> Result<PluginWrapper<'a, P>, PluginError> {
36 let (shared_initializer, main_thread_initializer) = *self;
37 let shared_handle = host.shared();
38 let shared = Box::pin(shared_initializer(shared_handle)?);
39
40 let shared_ref = unsafe { &*(shared.as_ref().get_ref() as *const _) };
42 let main_thread = main_thread_initializer(host, shared_ref)?;
43
44 Ok(unsafe { PluginWrapper::new(shared_handle, shared, main_thread) })
46 }
47}
48
49impl<'a, P: Plugin, F: 'a, FM: 'a> PluginInitializer<'a, P> for F
50where
51 F: FnOnce(HostMainThreadHandle<'a>) -> Result<(P::Shared<'a>, FM), PluginError>,
52 FM: FnOnce(&'a P::Shared<'a>) -> Result<P::MainThread<'a>, PluginError>,
53{
54 fn init(
55 self: Box<Self>,
56 host: HostMainThreadHandle<'a>,
57 ) -> Result<PluginWrapper<'a, P>, PluginError> {
58 let shared_handle = host.shared();
59 let (shared, main_thread_initializer) = self(host)?;
60 let shared = Box::pin(shared);
61
62 let shared_ref = unsafe { &*(shared.as_ref().get_ref() as *const _) };
64 let main_thread = main_thread_initializer(shared_ref)?;
65
66 Ok(unsafe { PluginWrapper::new(shared_handle, shared, main_thread) })
68 }
69}
70
71pub(crate) enum WrapperData<'a, P: Plugin> {
72 Initialized(PluginWrapper<'a, P>),
73 Initializing,
74 Uninitialized(Box<dyn PluginInitializer<'a, P>>),
75}
76
77pub(crate) struct PluginBoxInner<'a, P: Plugin> {
78 host: HostSharedHandle<'a>,
79 state: AtomicU8,
80 plugin_data: UnsafeCell<WrapperData<'a, P>>,
81}
82
83const UNINITIALIZED: u8 = 0;
84const INITIALIZED: u8 = 1;
85const INITIALIZING: u8 = 2;
86const INITIALIZATION_FAILED: u8 = 3;
87const DESTROYING: u8 = 4;
88
89impl<'a, P: Plugin> PluginBoxInner<'a, P> {
90 #[inline]
91 pub(crate) fn wrapper_uninit(
92 &self,
93 ) -> Result<Option<&PluginWrapper<'a, P>>, PluginWrapperError> {
94 match self.state.load(Ordering::Acquire) {
95 INITIALIZING => Ok(None),
96 UNINITIALIZED => Err(PluginWrapperError::UninitializedPlugin),
97 INITIALIZATION_FAILED => Err(PluginWrapperError::InitializationAlreadyFailed),
98 DESTROYING => Err(PluginWrapperError::Destroying),
99
100 INITIALIZED => match unsafe { &*self.plugin_data.get() } {
102 Initialized(w) => Ok(Some(w)),
103 _ => unreachable!(),
105 },
106 _ => unreachable!(),
107 }
108 }
109
110 #[inline]
111 pub(crate) fn wrapper(&self) -> Result<&PluginWrapper<'a, P>, PluginWrapperError> {
112 match self.state.load(Ordering::Acquire) {
113 INITIALIZING => Err(PluginWrapperError::PluginCalledDuringInitialization),
114 UNINITIALIZED => Err(PluginWrapperError::UninitializedPlugin),
115 INITIALIZATION_FAILED => Err(PluginWrapperError::InitializationAlreadyFailed),
116 DESTROYING => Err(PluginWrapperError::Destroying),
117
118 INITIALIZED => match unsafe { &*self.plugin_data.get() } {
120 Initialized(w) => Ok(w),
121 _ => unreachable!(),
123 },
124 _ => unreachable!(),
125 }
126 }
127}
128
129impl<'a, P: Plugin> PluginBoxInner<'a, P> {
130 fn get_plugin_desc(
131 host: HostSharedHandle<'a>,
132 desc: &'a clap_plugin_descriptor,
133 initializer: Box<dyn PluginInitializer<'a, P>>,
134 ) -> clap_plugin {
135 clap_plugin {
136 desc,
137 plugin_data: Box::into_raw(Box::new(Self {
138 host,
139 plugin_data: UnsafeCell::new(Uninitialized(initializer)),
140 state: AtomicU8::new(UNINITIALIZED),
141 }))
142 .cast(),
143 init: Some(Self::init),
144 destroy: Some(Self::destroy),
145 activate: Some(Self::activate),
146 deactivate: Some(Self::deactivate),
147 reset: Some(Self::reset),
148 start_processing: Some(Self::start_processing),
149 stop_processing: Some(Self::stop_processing),
150 process: Some(Self::process),
151 get_extension: Some(Self::get_extension),
152 on_main_thread: Some(Self::on_main_thread),
153 }
154 }
155
156 #[inline]
157 pub fn host(&self) -> &HostSharedHandle<'a> {
158 &self.host
159 }
160
161 #[allow(clippy::missing_safety_doc)]
162 unsafe extern "C" fn init(plugin: *const clap_plugin) -> bool {
163 PluginWrapper::<P>::handle_plugin_data(plugin, |data| {
164 let data = data.as_ref();
166
167 let current_state = data.state.compare_exchange(
168 UNINITIALIZED,
169 INITIALIZING,
170 Ordering::SeqCst,
171 Ordering::Acquire,
172 );
173
174 let uninit_data = match current_state {
175 Ok(UNINITIALIZED) => data.plugin_data.get().replace(Initializing),
176 Ok(s) | Err(s) => match s {
177 INITIALIZED => return Err(PluginWrapperError::AlreadyInitialized),
178 DESTROYING => return Err(PluginWrapperError::Destroying),
179 INITIALIZATION_FAILED | INITIALIZING => {
180 return Err(PluginWrapperError::InitializationAlreadyFailed);
181 }
182 _ => unreachable!(),
183 },
184 };
185
186 let Uninitialized(initializer) = uninit_data else {
187 unreachable!()
188 };
189
190 let init_result = initializer.init(data.host.as_main_thread_unchecked());
191
192 match init_result {
193 Ok(wrapper) => {
194 data.plugin_data.get().write(Initialized(wrapper));
196 data.state.store(INITIALIZED, Ordering::Release);
198 Ok(())
199 }
200 Err(e) => {
201 data.state.store(INITIALIZATION_FAILED, Ordering::Release);
202 Err(e.into())
203 }
204 }
205 })
206 .is_some()
207 }
208
209 #[allow(clippy::missing_safety_doc)]
210 unsafe extern "C" fn destroy(plugin: *const clap_plugin) {
211 PluginWrapper::<P>::handle_plugin_data(plugin, |data| {
214 use PluginWrapperError::*;
215
216 let wrapper = match data.as_ref().wrapper() {
217 Ok(w) => w,
218 Err(
220 PluginCalledDuringInitialization
221 | UninitializedPlugin
222 | InitializationAlreadyFailed,
223 ) => return Ok(()),
224 Err(e) => return Err(e),
225 };
226
227 if wrapper.is_active() {
228 wrapper.deactivate()
229 } else {
230 Ok(())
231 }
232 });
233
234 if let Some(plugin) = plugin.cast_mut().as_mut() {
235 let Some(plugin_data) = plugin.plugin_data.cast::<PluginBoxInner<P>>().as_mut() else {
237 return;
238 };
239
240 if plugin_data.state.swap(DESTROYING, Ordering::SeqCst) != DESTROYING {
242 let _ = handle_panic(|| {
243 let _ = Box::<PluginBoxInner<P>>::from_raw(
244 plugin.plugin_data as *const c_void as *mut _,
245 );
246 });
247 }
248
249 plugin.plugin_data = core::ptr::null_mut();
253
254 let _ = Box::from_raw(plugin as *mut clap_plugin);
255 }
256 }
257
258 #[allow(clippy::missing_safety_doc)]
259 unsafe extern "C" fn activate(
260 plugin: *const clap_plugin,
261 sample_rate: f64,
262 min_sample_count: u32,
263 max_sample_count: u32,
264 ) -> bool {
265 PluginWrapper::<P>::handle(plugin, |p| {
266 let config = PluginAudioConfiguration {
267 sample_rate,
268 min_frames_count: min_sample_count,
269 max_frames_count: max_sample_count,
270 };
271
272 p.activate(config)
273 })
274 .is_some()
275 }
276
277 #[allow(clippy::missing_safety_doc)]
278 unsafe extern "C" fn deactivate(plugin: *const clap_plugin) {
279 PluginWrapper::<P>::handle(plugin, |p| p.deactivate());
280 }
281
282 #[allow(clippy::missing_safety_doc)]
283 unsafe extern "C" fn reset(plugin: *const clap_plugin) {
284 PluginWrapper::<P>::handle(plugin, |p| {
285 p.audio_processor()?.as_mut().reset();
286 Ok(())
287 });
288 }
289
290 #[allow(clippy::missing_safety_doc)]
291 unsafe extern "C" fn start_processing(plugin: *const clap_plugin) -> bool {
292 PluginWrapper::<P>::handle(plugin, |p| {
293 Ok(p.audio_processor()?.as_mut().start_processing()?)
294 })
295 .is_some()
296 }
297
298 #[allow(clippy::missing_safety_doc)]
299 unsafe extern "C" fn stop_processing(plugin: *const clap_plugin) {
300 PluginWrapper::<P>::handle(plugin, |p| {
301 p.audio_processor()?.as_mut().stop_processing();
302 Ok(())
303 });
304 }
305
306 #[allow(clippy::missing_safety_doc)]
307 unsafe extern "C" fn process(
308 plugin: *const clap_plugin,
309 process: *const clap_process,
310 ) -> clap_process_status {
311 PluginWrapper::<P>::handle(plugin, |p| {
313 Ok(p.audio_processor()?.as_mut().process(
314 Process::from_raw(&*process),
315 Audio::from_raw(&*process),
316 Events::from_raw(&*process),
317 )?)
318 })
319 .map(|s| s as clap_process_status)
320 .unwrap_or(CLAP_PROCESS_ERROR)
321 }
322
323 #[allow(clippy::missing_safety_doc)]
324 unsafe extern "C" fn get_extension(
325 plugin: *const clap_plugin,
326 identifier: *const std::os::raw::c_char,
327 ) -> *const c_void {
328 let identifier = CStr::from_ptr(identifier);
329 let mut builder = PluginExtensions::new(identifier);
330
331 PluginWrapper::<P>::handle_plugin_data(plugin, |data| {
332 let p = data.as_ref().wrapper_uninit()?;
333 P::declare_extensions(&mut builder, p.map(|p| p.shared()));
334 Ok(())
335 });
336 builder.found()
337 }
338
339 #[allow(clippy::missing_safety_doc)]
340 unsafe extern "C" fn on_main_thread(plugin: *const clap_plugin) {
341 PluginWrapper::<P>::handle(plugin, |p| {
342 p.main_thread().on_main_thread();
343 Ok(())
344 });
345 }
346}
347
348pub struct PluginInstance<'a> {
354 inner: Box<clap_plugin>,
355 lifetime: PhantomData<&'a clap_plugin_descriptor>,
356}
357
358impl<'a> PluginInstance<'a> {
359 #[inline]
360 pub(crate) fn into_owned_ptr(self) -> *mut clap_plugin {
361 ManuallyDrop::new(self).inner.as_mut()
362 }
363
364 pub fn new<P: Plugin>(
372 host_info: HostInfo<'a>,
373 descriptor: &'a PluginDescriptor,
374 shared_initializer: impl FnOnce(HostSharedHandle<'a>) -> Result<P::Shared<'a>, PluginError> + 'a,
375 main_thread_initializer: impl FnOnce(
376 HostMainThreadHandle<'a>,
377 &'a P::Shared<'a>,
378 ) -> Result<P::MainThread<'a>, PluginError>
379 + 'a,
380 ) -> PluginInstance<'a> {
381 let host = unsafe { host_info.to_handle() };
383 Self {
384 inner: Box::new(PluginBoxInner::<P>::get_plugin_desc(
385 host,
386 descriptor.as_raw(),
387 Box::new((shared_initializer, main_thread_initializer))
388 as Box<dyn PluginInitializer<'a, P>>,
389 )),
390 lifetime: PhantomData,
391 }
392 }
393
394 pub fn new_with_initializer<P: Plugin, FM>(
444 host_info: HostInfo<'a>,
445 descriptor: &'a PluginDescriptor,
446 initializer: impl FnOnce(HostMainThreadHandle<'a>) -> Result<(P::Shared<'a>, FM), PluginError>
447 + 'a,
448 ) -> PluginInstance<'a>
449 where
450 FM: 'a + FnOnce(&'a P::Shared<'a>) -> Result<P::MainThread<'a>, PluginError>,
451 {
452 let host = unsafe { host_info.to_handle() };
454 Self {
455 inner: Box::new(PluginBoxInner::<P>::get_plugin_desc(
456 host,
457 descriptor.as_raw(),
458 Box::new(initializer) as Box<dyn PluginInitializer<'a, P>>,
459 )),
460 lifetime: PhantomData,
461 }
462 }
463
464 #[inline]
466 pub fn as_raw(&self) -> &clap_plugin {
467 &self.inner
468 }
469}
470
471impl Drop for PluginInstance<'_> {
473 #[inline]
474 fn drop(&mut self) {
475 if let Some(destroy) = self.inner.destroy {
476 unsafe { destroy(self.inner.as_ref()) }
478 }
479 }
480}