From f28032eceb88aaaeecb61c659163d7be32ddc896 Mon Sep 17 00:00:00 2001 From: Micah Johnston Date: Sun, 15 Oct 2023 21:32:33 -0500 Subject: [PATCH] clap: entry point --- examples/gain/src/lib.rs | 8 +++ src/format/clap/factory.rs | 106 +++++++++++++++++++++++++++++++++++++ src/format/clap/mod.rs | 68 ++++++++++++++++++++++++ src/format/mod.rs | 1 + 4 files changed, 183 insertions(+) create mode 100644 src/format/clap/factory.rs create mode 100644 src/format/clap/mod.rs diff --git a/examples/gain/src/lib.rs b/examples/gain/src/lib.rs index f78ec69..4162534 100644 --- a/examples/gain/src/lib.rs +++ b/examples/gain/src/lib.rs @@ -92,6 +92,14 @@ impl Vst3Plugin for Gain { } } +impl ClapPlugin for Gain { + fn clap_info() -> ClapInfo { + ClapInfo { + id: "rs.coupler.gain".to_string(), + } + } +} + pub struct GainProcessor { gain: f32, } diff --git a/src/format/clap/factory.rs b/src/format/clap/factory.rs new file mode 100644 index 0000000..70cd44a --- /dev/null +++ b/src/format/clap/factory.rs @@ -0,0 +1,106 @@ +use std::cell::UnsafeCell; +use std::ffi::{c_char, c_void, CStr, CString}; +use std::marker::PhantomData; +use std::ptr; + +use clap_sys::{host::*, plugin::*, plugin_factory::*, version::*}; + +use super::ClapPlugin; +use crate::Plugin; + +#[doc(hidden)] +#[repr(C)] +pub struct Factory

{ + #[allow(unused)] + factory: clap_plugin_factory, + descriptor: UnsafeCell>, + _marker: PhantomData

, +} + +unsafe impl

Sync for Factory

{} + +impl Factory

{ + pub const fn new() -> Self { + Factory { + factory: clap_plugin_factory { + get_plugin_count: Some(Self::get_plugin_count), + get_plugin_descriptor: Some(Self::get_plugin_descriptor), + create_plugin: Some(Self::create_plugin), + }, + descriptor: UnsafeCell::new(None), + _marker: PhantomData, + } + } + + pub unsafe fn init(&self) -> bool { + let clap_info = P::clap_info(); + let id = CString::new(&*clap_info.id).unwrap().into_raw(); + + let info = P::info(); + let name = CString::new(&*info.name).unwrap().into_raw(); + let vendor = CString::new(&*info.vendor).unwrap().into_raw(); + let url = CString::new(&*info.url).unwrap().into_raw(); + + const EMPTY: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") }; + const FEATURES: &'static [*const c_char] = &[ptr::null()]; + + *self.descriptor.get() = Some(clap_plugin_descriptor { + clap_version: CLAP_VERSION, + id, + name, + vendor, + url, + manual_url: EMPTY.as_ptr(), + support_url: EMPTY.as_ptr(), + version: EMPTY.as_ptr(), + description: EMPTY.as_ptr(), + features: FEATURES.as_ptr(), + }); + + true + } + + pub unsafe fn deinit(&self) { + if let Some(descriptor) = (*self.descriptor.get()).take() { + drop(CString::from_raw(descriptor.id as *mut c_char)); + drop(CString::from_raw(descriptor.name as *mut c_char)); + drop(CString::from_raw(descriptor.vendor as *mut c_char)); + drop(CString::from_raw(descriptor.url as *mut c_char)); + } + } + + pub unsafe fn get(&self, factory_id: *const c_char) -> *const c_void { + if CStr::from_ptr(factory_id) == CLAP_PLUGIN_FACTORY_ID { + return self as *const Self as *const c_void; + } + + ptr::null() + } + + unsafe extern "C" fn get_plugin_count(_factory: *const clap_plugin_factory) -> u32 { + 1 + } + + unsafe extern "C" fn get_plugin_descriptor( + factory: *const clap_plugin_factory, + index: u32, + ) -> *const clap_plugin_descriptor { + let factory = &*(factory as *const Self); + + if index == 0 { + if let Some(descriptor) = &*factory.descriptor.get() { + return descriptor; + } + } + + 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 { + ptr::null() + } +} diff --git a/src/format/clap/mod.rs b/src/format/clap/mod.rs new file mode 100644 index 0000000..393ccb4 --- /dev/null +++ b/src/format/clap/mod.rs @@ -0,0 +1,68 @@ +use std::ffi::{c_char, c_void}; + +use clap_sys::{entry::*, version::*}; + +mod factory; + +#[doc(hidden)] +pub use factory::Factory; + +pub struct ClapInfo { + pub id: String, +} + +pub trait ClapPlugin { + fn clap_info() -> ClapInfo; +} + +#[doc(hidden)] +#[repr(transparent)] +pub struct EntryPoint { + #[allow(unused)] + entry: clap_plugin_entry, +} + +impl EntryPoint { + pub const fn new( + init: unsafe extern "C" fn(_plugin_path: *const c_char) -> bool, + deinit: unsafe extern "C" fn(), + get_factory: unsafe extern "C" fn(factory_id: *const c_char) -> *const c_void, + ) -> EntryPoint { + EntryPoint { + entry: clap_plugin_entry { + clap_version: CLAP_VERSION, + init: Some(init), + deinit: Some(deinit), + get_factory: Some(get_factory), + }, + } + } +} + +#[macro_export] +macro_rules! clap { + ($plugin:ty) => { + #[allow(non_upper_case_globals)] + #[no_mangle] + static clap_entry: ::coupler::format::clap::EntryPoint = { + static FACTORY: ::coupler::format::clap::Factory<$plugin> = + ::coupler::format::clap::Factory::new(); + + unsafe extern "C" fn init(_plugin_path: *const ::std::ffi::c_char) -> bool { + FACTORY.init() + } + + unsafe extern "C" fn deinit() { + FACTORY.deinit(); + } + + unsafe extern "C" fn get_factory( + factory_id: *const ::std::ffi::c_char, + ) -> *const ::std::ffi::c_void { + FACTORY.get(factory_id) + } + + ::coupler::format::clap::EntryPoint::new(init, deinit, get_factory) + }; + }; +} diff --git a/src/format/mod.rs b/src/format/mod.rs index bdb9353..f032370 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -1 +1,2 @@ +pub mod clap; pub mod vst3;