Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 12 additions & 34 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ standalone = ["dep:baseview", "dep:clap", "dep:cpal", "dep:jack", "dep:midir", "
# exists mostly for GPL-compliance reasons, since even if you don't use the VST3
# wrapper you might otherwise still include a couple (unused) symbols from the
# `vst3-sys` crate.
vst3 = ["dep:vst3-sys"]
vst3 = ["dep:vst3"]
# Add adapters to the Buffer object for reading the channel data to and from
# `std::simd` vectors. Requires a nightly compiler.
simd = []
Expand Down Expand Up @@ -112,7 +112,7 @@ midir = { version = "0.9.1", optional = true }
rtrb = { version = "0.2.2", optional = true }

# Used for the `vst3` feature
vst3-sys = { git = "https://github.com/robbert-vdh/vst3-sys.git", branch = "fix/drop-box-from-raw", optional = true }
vst3 = { version = "0.3.0", optional = true }

# Used for the `zstd` feature
zstd = { version = "0.12.3", optional = true }
Expand Down
93 changes: 40 additions & 53 deletions src/wrapper/vst3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ mod wrapper;

/// Re-export for the wrapper.
pub use factory::PluginInfo;
pub use vst3_sys;
pub use vst3;
pub use wrapper::Wrapper;

/// Export one or more VST3 plugins from this library using the provided plugin types. The first
Expand All @@ -26,18 +26,16 @@ macro_rules! nih_export_vst3 {
#[doc(hidden)]
mod vst3 {
use ::std::collections::HashSet;
use ::std::ffi::c_void;

// `vst3_sys` is imported from the VST3 wrapper module
use $crate::wrapper::vst3::{vst3_sys, PluginInfo, Wrapper};
use vst3_sys::base::{kInvalidArgument, kResultOk, tresult};
use vst3_sys::base::{
FactoryFlags, IPluginFactory, IPluginFactory2, IPluginFactory3, IUnknown,
PClassInfo, PClassInfo2, PClassInfoW, PFactoryInfo,
use $crate::wrapper::vst3::{PluginInfo, Wrapper};
use $crate::wrapper::vst3::vst3::Steinberg::{kInvalidArgument, kResultOk, tresult, int32, FIDString, TUID};
use $crate::wrapper::vst3::vst3::Steinberg::{
PFactoryInfo_::FactoryFlags_, IPluginFactory, IPluginFactory2, IPluginFactory3, FUnknown,
PClassInfo, PClassInfo2, PClassInfoW, PFactoryInfo, IPluginFactoryTrait, IPluginFactory2Trait, IPluginFactory3Trait,
};
use vst3_sys::VST3;

// This alias is needed for the VST3 attribute macro
use vst3_sys as vst3_com;
use $crate::wrapper::vst3::vst3::{Class, ComWrapper};

// 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.
Expand All @@ -47,14 +45,17 @@ macro_rules! nih_export_vst3 {
const PLUGIN_COUNT: usize = [$(stringify!($plugin_ty)),+].len();

#[doc(hidden)]
#[VST3(implements(IPluginFactory, IPluginFactory2, IPluginFactory3))]
pub struct Factory {
// This is a type erased version of the information stored on the plugin types
plugin_infos: [PluginInfo; PLUGIN_COUNT],
}

impl Class for Factory {
type Interfaces = (IPluginFactory, IPluginFactory2, IPluginFactory3);
}

impl Factory {
pub fn new() -> Box<Self> {
pub fn new() -> Self {
let plugin_infos = [$(PluginInfo::for_plugin::<$plugin_ty>()),+];

if cfg!(debug_assertions) {
Expand All @@ -66,12 +67,12 @@ macro_rules! nih_export_vst3 {
);
}

Self::allocate(plugin_infos)
Factory { plugin_infos }
}
}

impl IPluginFactory for Factory {
unsafe fn get_factory_info(&self, info: *mut PFactoryInfo) -> tresult {
impl IPluginFactoryTrait for Factory {
unsafe fn getFactoryInfo(&self, info: *mut PFactoryInfo) -> tresult {
if info.is_null() {
return kInvalidArgument;
}
Expand All @@ -82,11 +83,11 @@ macro_rules! nih_export_vst3 {
kResultOk
}

unsafe fn count_classes(&self) -> i32 {
unsafe fn countClasses(&self) -> int32 {
self.plugin_infos.len() as i32
}

unsafe fn get_class_info(&self, index: i32, info: *mut PClassInfo) -> tresult {
unsafe fn getClassInfo(&self, index: int32, info: *mut PClassInfo) -> tresult {
if index < 0 || index >= self.plugin_infos.len() as i32 {
return kInvalidArgument;
}
Expand All @@ -96,48 +97,31 @@ macro_rules! nih_export_vst3 {
kResultOk
}

unsafe fn create_instance(
unsafe fn createInstance(
&self,
cid: *const vst3_sys::IID,
iid: *const vst3_sys::IID,
obj: *mut *mut vst3_sys::c_void,
cid: FIDString,
iid: FIDString,
obj: *mut *mut c_void,
) -> tresult {
// Can't use `check_null_ptr!()` here without polluting NIH-plug's general
// exports
if cid.is_null() || obj.is_null() {
return kInvalidArgument;
}

let cid = &*(cid as *const [u8; 16]);

// This is a poor man's way of treating `$plugin_ty` like an indexable array.
// Assuming `self.plugin_infos` is in the same order, we can simply check all of
// the registered plugin CIDs for matches using an unrolled loop.
let mut plugin_idx = 0;
$({
let plugin_info = &self.plugin_infos[plugin_idx];
if (*cid).data == *plugin_info.cid {
let wrapper = Wrapper::<$plugin_ty>::new();

// 99.999% of the times `iid` will be that of `IComponent`, but the
// caller is technically allowed to create an object for any support
// interface. We don't have a way to check whether our plugin supports
// the interface without creating it, but since the odds that a caller
// will create an object with an interface we don't support are
// basically zero this is not a problem.
let result = wrapper.query_interface(iid, obj);
if result == kResultOk {
// This is a bit awkward now but if the cast succeeds we need to get
// rid of the reference from the `wrapper` binding. The VST3 query
// interface always increments the reference count and returns an
// owned reference, so we need to explicitly release the reference
// from `wrapper` and leak the `Box` so the wrapper doesn't
// automatically get deallocated when this function returns (`Box`
// is an incorrect choice on vst3-sys' part, it should have used a
// `VstPtr` instead).
wrapper.release();
Box::leak(wrapper);

return kResultOk;
}
if cid == plugin_info.cid {
let wrapper = ComWrapper::new(Wrapper::<$plugin_ty>::new());
let unknown = wrapper.as_com_ref::<FUnknown>().unwrap();
let ptr = unknown.as_ptr();
return ((*(*ptr).vtbl).queryInterface)(ptr, iid as *const TUID, obj);
}

plugin_idx += 1;
Expand All @@ -147,8 +131,8 @@ macro_rules! nih_export_vst3 {
}
}

impl IPluginFactory2 for Factory {
unsafe fn get_class_info2(&self, index: i32, info: *mut PClassInfo2) -> tresult {
impl IPluginFactory2Trait for Factory {
unsafe fn getClassInfo2(&self, index: int32, info: *mut PClassInfo2) -> tresult {
if index < 0 || index >= self.plugin_infos.len() as i32 {
return kInvalidArgument;
}
Expand All @@ -159,10 +143,10 @@ macro_rules! nih_export_vst3 {
}
}

impl IPluginFactory3 for Factory {
unsafe fn get_class_info_unicode(
impl IPluginFactory3Trait for Factory {
unsafe fn getClassInfoUnicode(
&self,
index: i32,
index: int32,
info: *mut PClassInfoW,
) -> tresult {
if index < 0 || index >= self.plugin_infos.len() as i32 {
Expand All @@ -174,7 +158,7 @@ macro_rules! nih_export_vst3 {
kResultOk
}

unsafe fn set_host_context(&self, _context: *mut vst3_sys::c_void) -> tresult {
unsafe fn setHostContext(&self, _context: *mut FUnknown) -> tresult {
// We don't need to do anything with this
kResultOk
}
Expand All @@ -184,9 +168,12 @@ macro_rules! nih_export_vst3 {
/// The VST3 plugin factory entry point.
#[no_mangle]
pub extern "system" fn GetPluginFactory() -> *mut ::std::ffi::c_void {
let factory = self::vst3::Factory::new();
use $crate::wrapper::vst3::vst3::{ComWrapper, Steinberg::IPluginFactory};

Box::into_raw(factory) as *mut ::std::ffi::c_void
ComWrapper::new(self::vst3::Factory::new())
.to_com_ptr::<IPluginFactory>()
.unwrap()
.into_raw() as *mut ::std::ffi::c_void
}

// These two entry points are used on Linux, and they would theoretically also be used on
Expand Down
13 changes: 8 additions & 5 deletions src/wrapper/vst3/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::cell::Cell;
use std::collections::VecDeque;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use vst3_sys::vst::IComponentHandler;
use vst3::Steinberg::Vst::IComponentHandlerTrait;

use crate::prelude::{
GuiContext, InitContext, ParamPtr, PluginApi, PluginNoteEvent, PluginState, ProcessContext,
Expand Down Expand Up @@ -44,6 +44,9 @@ pub(crate) struct WrapperProcessContext<'a, P: Vst3Plugin> {
pub(super) transport: Transport,
}

unsafe impl<P: Vst3Plugin> Send for WrapperGuiContext<P> {}
unsafe impl<P: Vst3Plugin> Sync for WrapperGuiContext<P> {}

/// A [`GuiContext`] implementation for the wrapper. This is passed to the plugin in
/// [`Editor::spawn()`][crate::prelude::Editor::spawn()] so it can interact with the rest of the plugin and
/// with the host for things like setting parameters.
Expand Down Expand Up @@ -118,7 +121,7 @@ impl<P: Vst3Plugin> ProcessContext<P> for WrapperProcessContext<'_, P> {
}
}

impl<P: Vst3Plugin> GuiContext for WrapperGuiContext<P> {
impl<P: Vst3Plugin + Send> GuiContext for WrapperGuiContext<P> {
fn plugin_api(&self) -> PluginApi {
PluginApi::Vst3
}
Expand All @@ -138,7 +141,7 @@ impl<P: Vst3Plugin> GuiContext for WrapperGuiContext<P> {
match &*self.inner.component_handler.borrow() {
Some(handler) => match self.inner.param_ptr_to_hash.get(&param) {
Some(hash) => {
handler.begin_edit(*hash);
handler.beginEdit(*hash);
}
None => nih_debug_assert_failure!("Unknown parameter: {:?}", param),
},
Expand Down Expand Up @@ -179,7 +182,7 @@ impl<P: Vst3Plugin> GuiContext for WrapperGuiContext<P> {
);
}

handler.perform_edit(*hash, normalized as f64);
handler.performEdit(*hash, normalized as f64);
}
None => nih_debug_assert_failure!("Unknown parameter: {:?}", param),
},
Expand All @@ -202,7 +205,7 @@ impl<P: Vst3Plugin> GuiContext for WrapperGuiContext<P> {
match &*self.inner.component_handler.borrow() {
Some(handler) => match self.inner.param_ptr_to_hash.get(&param) {
Some(hash) => {
handler.end_edit(*hash);
handler.endEdit(*hash);
}
None => nih_debug_assert_failure!("Unknown parameter: {:?}", param),
},
Expand Down
Loading