diff --git a/CHANGELOG.md b/CHANGELOG.md index da34edd9..ba76b939 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,18 @@ Since there is no stable release yet, the changes are organized per day in reverse chronological order. The main purpose of this document in its current state is to list breaking changes. +## [2023-08-05] + +### Breaking changes + +- The minimum supported Rust version has been bumped to 1.70 so we can start + using `OnceCell` and `OnceLock` to phase out uses of `lazy_static`. + +### Added + +- `nih_export_clap!()` can now take more than one plugin type argument to allow + exporting more than one plugin from a single plugin library. + ## [2023-05-13] ### Fixed diff --git a/Cargo.toml b/Cargo.toml index 7b58aa28..eb8294f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "nih_plug" version = "0.0.0" edition = "2021" -rust-version = "1.63" +rust-version = "1.70" authors = ["Robbert van der Helm "] license = "ISC" diff --git a/src/wrapper/clap.rs b/src/wrapper/clap.rs index b6d4bde0..76281208 100644 --- a/src/wrapper/clap.rs +++ b/src/wrapper/clap.rs @@ -3,31 +3,114 @@ mod util; mod context; mod descriptor; -mod factory; pub mod features; mod wrapper; -/// Re-export for the wrapper. -pub use self::factory::Factory; +/// 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; +pub use clap_sys::factory::plugin_factory::{clap_plugin_factory, CLAP_PLUGIN_FACTORY_ID}; +pub use clap_sys::host::clap_host; +pub use clap_sys::plugin::{clap_plugin, clap_plugin_descriptor}; pub use clap_sys::version::CLAP_VERSION; pub use lazy_static::lazy_static; /// Export a CLAP plugin from this library using the provided plugin type. #[macro_export] macro_rules! nih_export_clap { - ($plugin_ty:ty) => { - // We need a function pointer to a [wrapper::get_factory()] that creates a factory for `$plugin_ty`, so we need to generate the function inside of this macro + ($($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 { - // Because `$plugin_ty` is likely defined in the enclosing scope + // 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::*; - // We don't use generics inside of statics, so this lazy_static is used as kind of an - // escape hatch - ::nih_plug::wrapper::clap::lazy_static! { - static ref FACTORY: ::nih_plug::wrapper::clap::Factory<$plugin_ty> = ::nih_plug::wrapper::clap::Factory::default(); + const CLAP_PLUGIN_FACTORY: ::nih_plug::wrapper::clap::clap_plugin_factory = + ::nih_plug::wrapper::clap::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(); + + // We'll put these plugin descriptors in a tuple since we can't easily associate them + // with indices without involving even more macros. We can't initialize this tuple + // completely statically + static PLUGIN_DESCRIPTORS: ::std::sync::OnceLock< + [::nih_plug::wrapper::clap::PluginDescriptor; PLUGIN_COUNT] + > = ::std::sync::OnceLock::new(); + + fn plugin_descriptors() -> &'static [::nih_plug::wrapper::clap::PluginDescriptor; PLUGIN_COUNT] { + PLUGIN_DESCRIPTORS.get_or_init(|| { + let descriptors = [$(::nih_plug::wrapper::clap::PluginDescriptor::for_plugin::<$plugin_ty>()),+]; + + if cfg!(debug_assertions) { + let unique_plugin_ids: std::collections::HashSet<_> + = descriptors.iter().map(|d| d.clap_id()).collect(); + ::nih_plug::debug::nih_debug_assert_eq!( + unique_plugin_ids.len(), + descriptors.len(), + "Duplicate plugin IDs found in `nih_export_clap!()` call" + ); + } + + descriptors + }) + } + + unsafe extern "C" fn get_plugin_count( + _factory: *const ::nih_plug::wrapper::clap::clap_plugin_factory, + ) -> u32 { + plugin_descriptors().len() as u32 + } + + unsafe extern "C" fn get_plugin_descriptor ( + _factory: *const ::nih_plug::wrapper::clap::clap_plugin_factory, + index: u32, + ) -> *const ::nih_plug::wrapper::clap::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 ::nih_plug::wrapper::clap::clap_plugin_factory, + host: *const ::nih_plug::wrapper::clap::clap_host, + plugin_id: *const ::std::os::raw::c_char, + ) -> *const ::nih_plug::wrapper::clap::clap_plugin { + if plugin_id.is_null() { + return ::std::ptr::null(); + } + let plugin_id_cstr = ::std::ffi::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 (*Arc::into_raw(::nih_plug::wrapper::clap::Wrapper::<$plugin_ty>::new(host))) + .clap_plugin + .as_ptr(); + } + + descriptor_idx += 1; + })+ + + std::ptr::null() } pub extern "C" fn init(_plugin_path: *const ::std::os::raw::c_char) -> bool { @@ -44,7 +127,7 @@ macro_rules! nih_export_clap { && unsafe { ::std::ffi::CStr::from_ptr(factory_id) } == ::nih_plug::wrapper::clap::CLAP_PLUGIN_FACTORY_ID { - &*FACTORY as *const _ as *const ::std::ffi::c_void + &CLAP_PLUGIN_FACTORY as *const _ as *const ::std::ffi::c_void } else { std::ptr::null() } diff --git a/src/wrapper/clap/descriptor.rs b/src/wrapper/clap/descriptor.rs index 22200e9f..1938690f 100644 --- a/src/wrapper/clap/descriptor.rs +++ b/src/wrapper/clap/descriptor.rs @@ -1,7 +1,6 @@ use clap_sys::plugin::clap_plugin_descriptor; use clap_sys::version::CLAP_VERSION; use std::ffi::{CStr, CString}; -use std::marker::PhantomData; use std::os::raw::c_char; use crate::prelude::ClapPlugin; @@ -11,7 +10,7 @@ use crate::prelude::ClapPlugin; /// /// This cannot be cloned as [`Self::clap_features_ptrs`] contains pointers to /// [Self::clap_features]. -pub struct PluginDescriptor { +pub struct PluginDescriptor { // We need [CString]s for all of `ClapPlugin`'s `&str` fields clap_id: CString, name: CString, @@ -29,13 +28,14 @@ pub struct PluginDescriptor { /// descriptor upfront. We also need to initialize the `CString` fields above first before we /// can initialize this plugin descriptor. plugin_descriptor: Option, - - /// The plugin's type. - _phantom: PhantomData

, } -impl Default for PluginDescriptor

{ - fn default() -> Self { +unsafe impl Send for PluginDescriptor {} +unsafe impl Sync for PluginDescriptor {} + +impl PluginDescriptor { + /// Construct the plugin descriptor for a specific CLAP plugin. + pub fn for_plugin() -> Self { let mut descriptor = Self { clap_id: CString::new(P::CLAP_ID).expect("`CLAP_ID` contained null bytes"), name: CString::new(P::NAME).expect("`NAME` contained null bytes"), @@ -59,8 +59,6 @@ impl Default for PluginDescriptor

{ // descriptor clap_features_ptrs: Vec::new(), plugin_descriptor: None, - - _phantom: PhantomData, }; // The keyword list is an environ-like list of char pointers terminated by a null pointer. @@ -101,12 +99,7 @@ impl Default for PluginDescriptor

{ descriptor } -} -unsafe impl Send for PluginDescriptor

{} -unsafe impl Sync for PluginDescriptor

{} - -impl PluginDescriptor

{ pub fn clap_plugin_descriptor(&self) -> &clap_plugin_descriptor { self.plugin_descriptor.as_ref().unwrap() } diff --git a/src/wrapper/clap/factory.rs b/src/wrapper/clap/factory.rs deleted file mode 100644 index 105e5331..00000000 --- a/src/wrapper/clap/factory.rs +++ /dev/null @@ -1,75 +0,0 @@ -use clap_sys::factory::plugin_factory::clap_plugin_factory; -use clap_sys::host::clap_host; -use clap_sys::plugin::{clap_plugin, clap_plugin_descriptor}; -use std::ffi::CStr; -use std::os::raw::c_char; -use std::ptr; -use std::sync::Arc; - -use super::descriptor::PluginDescriptor; -use super::wrapper::Wrapper; -use crate::prelude::ClapPlugin; - -/// The plugin's factory. Initialized using a lazy_static from the entry point's `get_factory()` -/// function. From this point onwards we don't need to generate code with macros anymore. -#[doc(hidden)] -#[repr(C)] -pub struct Factory { - // Keep the vtable as the first field so we can do a simple pointer cast. There's no data - // pointer as the API expects this thing to be entirely static, which in our case it isn't. - pub clap_plugin_factory: clap_plugin_factory, - - plugin_descriptor: PluginDescriptor

, -} - -impl Default for Factory

{ - fn default() -> Self { - Self { - clap_plugin_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), - }, - plugin_descriptor: PluginDescriptor::default(), - } - } -} - -impl Factory

{ - 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 { - factory.plugin_descriptor.clap_plugin_descriptor() - } else { - 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 { - let factory = &*(factory as *const Self); - - if !plugin_id.is_null() && CStr::from_ptr(plugin_id) == factory.plugin_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()] - (*Arc::into_raw(Wrapper::

::new(host))) - .clap_plugin - .as_ptr() - } else { - ptr::null() - } - } -} diff --git a/src/wrapper/clap/wrapper.rs b/src/wrapper/clap/wrapper.rs index 2eda36d5..77b33c23 100644 --- a/src/wrapper/clap/wrapper.rs +++ b/src/wrapper/clap/wrapper.rs @@ -174,7 +174,7 @@ pub struct Wrapper { pub clap_plugin: AtomicRefCell, /// Needs to be boxed because the plugin object is supposed to contain a static reference to /// this. - _plugin_descriptor: Box>, + _plugin_descriptor: Box, clap_plugin_audio_ports: clap_plugin_audio_ports, @@ -433,7 +433,8 @@ impl Wrapper

{ // on `Self::updated_state_sender` let (updated_state_sender, updated_state_receiver) = channel::bounded(0); - let plugin_descriptor: Box> = Box::default(); + let plugin_descriptor: Box = + Box::new(PluginDescriptor::for_plugin::

()); // We're not allowed to query any extensions until the init function has been called, so we // need a bunch of AtomicRefCells instead