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 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 unsafe { ClapPlugin::new_unchecked(Box::into_raw(ffi::box_clap_plugin(self))) }
138 }
139
140 unsafe fn from_clap_plugin(clap_plugin: ClapPlugin<P>) -> Self {
149 let plugin_data = unsafe { clap_plugin.clap_plugin() }.plugin_data as *mut _;
150 *unsafe { Box::from_raw(plugin_data) }
157 }
158}
159
160#[doc(hidden)]
161pub struct ClapPlugin<P: Plugin> {
163 clap_plugin: *const clap_plugin,
164 _marker: PhantomData<P>,
165}
166
167impl<P: Plugin> ClapPlugin<P> {
168 pub const unsafe fn new_unchecked(clap_plugin: *const clap_plugin) -> Self {
178 Self {
179 clap_plugin,
180 _marker: PhantomData,
181 }
182 }
183
184 #[doc(hidden)]
189 pub const unsafe fn clap_plugin<'a>(&self) -> &'a clap_plugin {
190 unsafe { &*self.clap_plugin }
193 }
194
195 pub(crate) const fn into_inner(self) -> *const clap_plugin {
196 self.clap_plugin
197 }
198
199 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 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 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 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 let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
357
358 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 let clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
373
374 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 let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
394
395 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 let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
420
421 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 let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
443
444 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 let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
461
462 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 let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
479
480 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 let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
501
502 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 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 let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
534
535 let id = unsafe { CStr::from_ptr(id) };
538
539 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 let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
582
583 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}