diff --git a/Cargo.lock b/Cargo.lock index 81c28648b..80c4343ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1196,6 +1196,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "com-scrape-types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce183b975b8fc736a20919c4002717255fbd867df575c792aeefa9a56a73ad0" + [[package]] name = "combine" version = "4.6.7" @@ -3583,7 +3589,7 @@ dependencies = [ "rtrb", "serde", "serde_json", - "vst3-sys", + "vst3", "widestring", "windows 0.44.0", "zstd", @@ -6035,40 +6041,12 @@ dependencies = [ ] [[package]] -name = "vst3-com" -version = "0.1.0" -source = "git+https://github.com/robbert-vdh/vst3-sys.git?branch=fix/drop-box-from-raw#b3ff4d775940f5b476b9d1cca02a90e07e1922a2" -dependencies = [ - "vst3-com-macros", -] - -[[package]] -name = "vst3-com-macros" -version = "0.2.0" -source = "git+https://github.com/robbert-vdh/vst3-sys.git?branch=fix/drop-box-from-raw#b3ff4d775940f5b476b9d1cca02a90e07e1922a2" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "vst3-com-macros-support", -] - -[[package]] -name = "vst3-com-macros-support" -version = "0.2.0" -source = "git+https://github.com/robbert-vdh/vst3-sys.git?branch=fix/drop-box-from-raw#b3ff4d775940f5b476b9d1cca02a90e07e1922a2" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "vst3-sys" -version = "0.1.0" -source = "git+https://github.com/robbert-vdh/vst3-sys.git?branch=fix/drop-box-from-raw#b3ff4d775940f5b476b9d1cca02a90e07e1922a2" +name = "vst3" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31e3fe7a6f028ded8c5a9984cf283319e6dc63572c9b71da1d4ee9d71d3b6bf" dependencies = [ - "vst3-com", + "com-scrape-types", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6b70605d9..b3fcf613d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,8 +61,8 @@ standalone = ["dep:baseview", "dep:clap", "dep:cpal", "dep:jack", "dep:midir", " # Enables the `nih_export_vst3!()` macro. Enabled by default. This feature # 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` crate. +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 = [] @@ -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 } diff --git a/README.md b/README.md index 2969287ad..6521556f3 100644 --- a/README.md +++ b/README.md @@ -226,12 +226,7 @@ examples. ## Licensing The framework, its libraries, and the example plugins in `plugins/examples/` are -all licensed under the [ISC license](https://www.isc.org/licenses/). However, -the [VST3 bindings](https://github.com/RustAudio/vst3-sys) used by -`nih_export_vst3!()` are licensed under the GPLv3 license. This means that -unless you replace these bindings with your own bindings made from scratch, any -VST3 plugins built with NIH-plug need to be able to comply with the terms of the -GPLv3 license. +all licensed under the [ISC license](https://www.isc.org/licenses/). The other plugins in the `plugins/` directory may be licensed under the GPLv3 license. Check the plugin's `Cargo.toml` file for more information. diff --git a/src/buffer/blocks.rs b/src/buffer/blocks.rs index ccf8f9ecc..470d8915f 100644 --- a/src/buffer/blocks.rs +++ b/src/buffer/blocks.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; #[cfg(feature = "simd")] -use std::simd::{LaneCount, Simd, SupportedLaneCount}; +use std::simd::Simd; use super::SamplesIter; @@ -226,10 +226,7 @@ impl<'slice, 'sample> Block<'slice, 'sample> { pub fn to_channel_simd( &self, sample_index: usize, - ) -> Option> - where - LaneCount: SupportedLaneCount, - { + ) -> Option> { if sample_index > self.samples() { return None; } @@ -258,10 +255,7 @@ impl<'slice, 'sample> Block<'slice, 'sample> { pub unsafe fn to_channel_simd_unchecked( &self, sample_index: usize, - ) -> Simd - where - LaneCount: SupportedLaneCount, - { + ) -> Simd { let mut values = [0.0; LANES]; for (channel_idx, value) in values.iter_mut().enumerate() { *value = *(&(*self.buffers)) @@ -284,10 +278,7 @@ impl<'slice, 'sample> Block<'slice, 'sample> { &mut self, sample_index: usize, vector: Simd, - ) -> bool - where - LaneCount: SupportedLaneCount, - { + ) -> bool { if sample_index > self.samples() { return false; } @@ -319,9 +310,7 @@ impl<'slice, 'sample> Block<'slice, 'sample> { &mut self, sample_index: usize, vector: Simd, - ) where - LaneCount: SupportedLaneCount, - { + ) { let values = vector.to_array(); for (channel_idx, value) in values.into_iter().enumerate() { *(&mut (*self.buffers)) diff --git a/src/buffer/samples.rs b/src/buffer/samples.rs index fae69f42d..b1ddf1991 100644 --- a/src/buffer/samples.rs +++ b/src/buffer/samples.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; #[cfg(feature = "simd")] -use std::simd::{LaneCount, Simd, SupportedLaneCount}; +use std::simd::Simd; /// An iterator over all samples in a buffer or block, yielding iterators over each channel for /// every sample. This iteration order offers good cache locality for per-sample access. @@ -168,10 +168,7 @@ impl<'slice, 'sample> ChannelSamples<'slice, 'sample> { /// all values. #[cfg(feature = "simd")] #[inline] - pub fn to_simd(&self) -> Simd - where - LaneCount: SupportedLaneCount, - { + pub fn to_simd(&self) -> Simd { let used_lanes = self.len().max(LANES); let mut values = [0.0; LANES]; for (channel_idx, value) in values.iter_mut().enumerate().take(used_lanes) { @@ -193,10 +190,7 @@ impl<'slice, 'sample> ChannelSamples<'slice, 'sample> { /// Undefined behavior if `LANES > channels.len()`. #[cfg(feature = "simd")] #[inline] - pub unsafe fn to_simd_unchecked(&self) -> Simd - where - LaneCount: SupportedLaneCount, - { + pub unsafe fn to_simd_unchecked(&self) -> Simd { let mut values = [0.0; LANES]; for (channel_idx, value) in values.iter_mut().enumerate() { *value = *(&(*self.buffers)) @@ -212,10 +206,7 @@ impl<'slice, 'sample> ChannelSamples<'slice, 'sample> { #[cfg(feature = "simd")] #[allow(clippy::wrong_self_convention)] #[inline] - pub fn from_simd(&mut self, vector: Simd) - where - LaneCount: SupportedLaneCount, - { + pub fn from_simd(&mut self, vector: Simd) { let used_lanes = self.len().max(LANES); let values = vector.to_array(); for (channel_idx, value) in values.into_iter().enumerate().take(used_lanes) { @@ -236,10 +227,7 @@ impl<'slice, 'sample> ChannelSamples<'slice, 'sample> { #[cfg(feature = "simd")] #[allow(clippy::wrong_self_convention)] #[inline] - pub unsafe fn from_simd_unchecked(&mut self, vector: Simd) - where - LaneCount: SupportedLaneCount, - { + pub unsafe fn from_simd_unchecked(&mut self, vector: Simd) { let values = vector.to_array(); for (channel_idx, value) in values.into_iter().enumerate() { *(&mut (*self.buffers)) diff --git a/src/wrapper/vst3.rs b/src/wrapper/vst3.rs index 7856a6a4b..0cdada2ef 100644 --- a/src/wrapper/vst3.rs +++ b/src/wrapper/vst3.rs @@ -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 @@ -26,18 +26,16 @@ macro_rules! nih_export_vst3 { #[doc(hidden)] mod vst3 { use ::std::collections::HashSet; - - // `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 ::std::ffi::c_void; + + // `vst3` is imported from the VST3 wrapper module + 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. @@ -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 { + pub fn new() -> Self { let plugin_infos = [$(PluginInfo::for_plugin::<$plugin_ty>()),+]; if cfg!(debug_assertions) { @@ -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; } @@ -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; } @@ -96,11 +97,11 @@ 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 @@ -108,36 +109,19 @@ macro_rules! nih_export_vst3 { 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::().unwrap(); + let ptr = unknown.as_ptr(); + return ((*(*ptr).vtbl).queryInterface)(ptr, iid as *const TUID, obj); } plugin_idx += 1; @@ -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; } @@ -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 { @@ -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 } @@ -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::() + .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 diff --git a/src/wrapper/vst3/context.rs b/src/wrapper/vst3/context.rs index 75b5b5036..44cba2882 100644 --- a/src/wrapper/vst3/context.rs +++ b/src/wrapper/vst3/context.rs @@ -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, @@ -44,6 +44,9 @@ pub(crate) struct WrapperProcessContext<'a, P: Vst3Plugin> { pub(super) transport: Transport, } +unsafe impl Send for WrapperGuiContext

{} +unsafe impl Sync for WrapperGuiContext

{} + /// 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. @@ -118,7 +121,7 @@ impl ProcessContext

for WrapperProcessContext<'_, P> { } } -impl GuiContext for WrapperGuiContext

{ +impl GuiContext for WrapperGuiContext

{ fn plugin_api(&self) -> PluginApi { PluginApi::Vst3 } @@ -138,7 +141,7 @@ impl GuiContext for WrapperGuiContext

{ match &*self.inner.component_handler.borrow() { Some(handler) => match self.inner.param_ptr_to_hash.get(¶m) { Some(hash) => { - handler.begin_edit(*hash); + handler.beginEdit(*hash); } None => nih_debug_assert_failure!("Unknown parameter: {:?}", param), }, @@ -179,7 +182,7 @@ impl GuiContext for WrapperGuiContext

{ ); } - handler.perform_edit(*hash, normalized as f64); + handler.performEdit(*hash, normalized as f64); } None => nih_debug_assert_failure!("Unknown parameter: {:?}", param), }, @@ -202,7 +205,7 @@ impl GuiContext for WrapperGuiContext

{ match &*self.inner.component_handler.borrow() { Some(handler) => match self.inner.param_ptr_to_hash.get(¶m) { Some(hash) => { - handler.end_edit(*hash); + handler.endEdit(*hash); } None => nih_debug_assert_failure!("Unknown parameter: {:?}", param), }, diff --git a/src/wrapper/vst3/factory.rs b/src/wrapper/vst3/factory.rs index 4dad87a39..e4422db30 100644 --- a/src/wrapper/vst3/factory.rs +++ b/src/wrapper/vst3/factory.rs @@ -5,8 +5,10 @@ //! frustrating and error prone, most code that does not specifically depend on all of the exposed //! plugin types was moved back to this module so it can be compiled and type checked as normal. -use vst3_sys::base::{ - ClassCardinality, FactoryFlags, PClassInfo, PClassInfo2, PClassInfoW, PFactoryInfo, +use vst3::Steinberg::Vst::ComponentFlags_; +use vst3::Steinberg::{ + PClassInfo, PClassInfo2, PClassInfoW, PClassInfo_::ClassCardinality_, PFactoryInfo, + PFactoryInfo_::FactoryFlags_, }; use super::subcategories::Vst3SubCategory; @@ -53,7 +55,7 @@ impl PluginInfo { strlcpy(&mut info.vendor, self.vendor); strlcpy(&mut info.url, self.url); strlcpy(&mut info.email, self.email); - info.flags = FactoryFlags::kUnicode as i32; + info.flags = FactoryFlags_::kUnicode as i32; info } @@ -62,8 +64,8 @@ impl PluginInfo { /// `IPluginFactory`. pub fn create_class_info(&self) -> PClassInfo { let mut info: PClassInfo = unsafe { std::mem::zeroed() }; - info.cid.data = *self.cid; - info.cardinality = ClassCardinality::kManyInstances as i32; + info.cid = self.cid.map(|b| b as i8); + info.cardinality = ClassCardinality_::kManyInstances as i32; strlcpy(&mut info.category, "Audio Module Class"); strlcpy(&mut info.name, self.name); @@ -74,15 +76,15 @@ impl PluginInfo { /// `IPluginFactory2`. pub fn create_class_info_2(&self) -> PClassInfo2 { let mut info: PClassInfo2 = unsafe { std::mem::zeroed() }; - info.cid.data = *self.cid; - info.cardinality = ClassCardinality::kManyInstances as i32; + info.cid = self.cid.map(|b| b as i8); + info.cardinality = ClassCardinality_::kManyInstances as i32; strlcpy(&mut info.category, "Audio Module Class"); strlcpy(&mut info.name, self.name); - info.class_flags = 1 << 1; // kSimpleModeSupported - strlcpy(&mut info.subcategories, &self.subcategories); + info.classFlags = ComponentFlags_::kSimpleModeSupported as u32; + strlcpy(&mut info.subCategories, &self.subcategories); strlcpy(&mut info.vendor, self.vendor); strlcpy(&mut info.version, self.version); - strlcpy(&mut info.sdk_version, VST3_SDK_VERSION); + strlcpy(&mut info.sdkVersion, VST3_SDK_VERSION); info } @@ -91,15 +93,15 @@ impl PluginInfo { /// `IPluginFactory3`. pub fn create_class_info_unicode(&self) -> PClassInfoW { let mut info: PClassInfoW = unsafe { std::mem::zeroed() }; - info.cid.data = *self.cid; - info.cardinality = ClassCardinality::kManyInstances as i32; + info.cid = self.cid.map(|b| b as i8); + info.cardinality = ClassCardinality_::kManyInstances as i32; strlcpy(&mut info.category, "Audio Module Class"); u16strlcpy(&mut info.name, self.name); - info.class_flags = 1 << 1; // kSimpleModeSupported - strlcpy(&mut info.subcategories, &self.subcategories); + info.classFlags = ComponentFlags_::kSimpleModeSupported as u32; + strlcpy(&mut info.subCategories, &self.subcategories); u16strlcpy(&mut info.vendor, self.vendor); u16strlcpy(&mut info.version, self.version); - u16strlcpy(&mut info.sdk_version, VST3_SDK_VERSION); + u16strlcpy(&mut info.sdkVersion, VST3_SDK_VERSION); info } diff --git a/src/wrapper/vst3/inner.rs b/src/wrapper/vst3/inner.rs index add0a1a1f..164620fb1 100644 --- a/src/wrapper/vst3/inner.rs +++ b/src/wrapper/vst3/inner.rs @@ -6,13 +6,14 @@ use std::collections::{HashMap, HashSet, VecDeque}; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::Arc; use std::time::Duration; -use vst3_sys::base::{kInvalidArgument, kResultOk, tresult}; -use vst3_sys::vst::{IComponentHandler, RestartFlags}; +use vst3::Steinberg::Vst::{IComponentHandler, IComponentHandlerTrait, RestartFlags_}; +use vst3::Steinberg::{kInvalidArgument, kResultOk, tresult}; +use vst3::{ComPtr, ComWrapper}; use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext}; use super::note_expressions::NoteExpressionController; use super::param_units::ParamUnits; -use super::util::{ObjectPtr, VstPtr, VST3_MIDI_PARAMS_END, VST3_MIDI_PARAMS_START}; +use super::util::{VST3_MIDI_PARAMS_END, VST3_MIDI_PARAMS_START}; use super::view::WrapperView; use crate::event_loop::{EventLoop, MainThreadExecutor, OsEventLoop}; use crate::prelude::{ @@ -43,11 +44,13 @@ pub(crate) struct WrapperInner { /// The host's [`IComponentHandler`] instance, if passed through /// [`IEditController::set_component_handler`]. - pub component_handler: AtomicRefCell>>, + pub component_handler: AtomicRefCell>>, - /// Our own [`IPlugView`] instance. This is set while the editor is actually visible (which is - /// different form the lifetime of [`WrapperView`][super::WrapperView] itself). - pub plug_view: RwLock>>>, + /// Our own [`IPlugView`] instance. + pub plug_view: RwLock>>>, + + /// Whether the editor is currently open. This is changed by the attached and removed functions on IPlugViewTrait + pub is_editor_open: AtomicBool, /// A realtime-safe task queue so the plugin can schedule tasks that need to be run later on the /// GUI thread. This field should not be used directly for posting tasks. This should be done @@ -139,6 +142,9 @@ pub(crate) struct WrapperInner { pub param_ptr_to_hash: HashMap, } +unsafe impl Send for WrapperInner

{} +unsafe impl Sync for WrapperInner

{} + /// Tasks that can be sent from the plugin to be executed on the main thread in a non-blocking /// realtime-safe way (either a random thread or `IRunLoop` on Linux, the OS' message loop on /// Windows and macOS). @@ -152,7 +158,7 @@ pub enum Task { /// since the task will be created from the audio thread. ParameterValueChanged(u32, f32), /// Trigger a restart with the given restart flags. This is a bit set of the flags from - /// [`vst3_sys::vst::RestartFlags`]. + /// [`vst3::Steinberg::Vst::RestartFlags`]. TriggerRestart(i32), /// Request the editor to be resized according to its current size. Right now there is no way to /// handle "denied resize" requests yet. @@ -285,6 +291,8 @@ impl WrapperInner

{ plug_view: RwLock::new(None), + is_editor_open: AtomicBool::new(false), + event_loop: AtomicRefCell::new(None), is_processing: AtomicBool::new(false), @@ -410,12 +418,19 @@ impl WrapperInner

{ // regular event loop. If the editor gets dropped while there's still outstanding work // left in the run loop task queue, then those tasks will be posted to the regular event // loop so no work is lost. - match &*self.plug_view.read() { - Some(plug_view) => match plug_view.do_maybe_in_run_loop(task) { + if self.is_editor_open.load(Ordering::Relaxed) { + match self + .plug_view + .read() + .clone() + .unwrap() + .do_maybe_in_run_loop(task) + { Ok(()) => true, Err(task) => event_loop.schedule_gui(task), - }, - None => event_loop.schedule_gui(task), + } + } else { + event_loop.schedule_gui(task) } } } @@ -521,7 +536,7 @@ impl WrapperInner

{ .as_ref() .unwrap() .schedule_gui(Task::TriggerRestart( - RestartFlags::kParamValuesChanged as i32, + RestartFlags_::kParamValuesChanged as i32, )); nih_debug_assert!(task_posted, "The task queue is full, dropping task..."); } @@ -531,7 +546,7 @@ impl WrapperInner

{ let old_latency = self.current_latency.swap(samples, Ordering::SeqCst); if old_latency != samples { let task_posted = - self.schedule_gui(Task::TriggerRestart(RestartFlags::kLatencyChanged as i32)); + self.schedule_gui(Task::TriggerRestart(RestartFlags_::kLatencyChanged as i32)); nih_debug_assert!(task_posted, "The task queue is full, dropping task..."); } } @@ -596,7 +611,7 @@ impl WrapperInner

{ // TODO: Right now there's no way to know if loading the state changed the GUI's size. We // could keep track of the last known size and compare the GUI's current size against // that but that also seems brittle. - if self.plug_view.read().is_some() { + if self.is_editor_open.load(Ordering::SeqCst) { let task_posted = self.schedule_gui(Task::RequestResize); nih_debug_assert!(task_posted, "The task queue is full, dropping task..."); } @@ -611,14 +626,14 @@ impl MainThreadExecutor> for WrapperInner

{ match task { Task::PluginTask(task) => (self.task_executor.lock())(task), Task::ParameterValuesChanged => { - if self.plug_view.read().is_some() { + if self.is_editor_open.load(Ordering::SeqCst) { if let Some(editor) = self.editor.borrow().as_ref() { editor.lock().param_values_changed(); } } } Task::ParameterValueChanged(param_hash, normalized_value) => { - if self.plug_view.read().is_some() { + if self.is_editor_open.load(Ordering::SeqCst) { if let Some(editor) = self.editor.borrow().as_ref() { let param_id = &self.param_id_by_hash[¶m_hash]; editor @@ -630,7 +645,7 @@ impl MainThreadExecutor> for WrapperInner

{ Task::TriggerRestart(flags) => match &*self.component_handler.borrow() { Some(handler) => unsafe { nih_debug_assert!(is_gui_thread); - let result = handler.restart_component(flags); + let result = handler.restartComponent(flags); nih_debug_assert_eq!( result, kResultOk, @@ -640,14 +655,18 @@ impl MainThreadExecutor> for WrapperInner

{ }, None => nih_debug_assert_failure!("Component handler not yet set"), }, - Task::RequestResize => match &*self.plug_view.read() { - Some(plug_view) => unsafe { - nih_debug_assert!(is_gui_thread); - let success = plug_view.request_resize(); - nih_debug_assert!(success, "Failed requesting a window resize"); - }, - None => nih_debug_assert_failure!("Can't resize a closed editor"), - }, + Task::RequestResize => { + if self.is_editor_open.load(Ordering::SeqCst) { + unsafe { + nih_debug_assert!(is_gui_thread); + let success = + WrapperView::request_resize(&self.plug_view.read().clone().unwrap()); + nih_debug_assert!(success, "Failed requesting a window resize"); + } + } else { + nih_debug_assert_failure!("Can't resize a closed editor"); + } + } } } } diff --git a/src/wrapper/vst3/note_expressions.rs b/src/wrapper/vst3/note_expressions.rs index 810a7af41..21748267b 100644 --- a/src/wrapper/vst3/note_expressions.rs +++ b/src/wrapper/vst3/note_expressions.rs @@ -1,7 +1,7 @@ //! Special handling for note expressions, because VST3 makes this a lot more complicated than it //! needs to be. We only support the predefined expressions. -use vst3_sys::vst::{NoteExpressionValueEvent, NoteOnEvent}; +use vst3::Steinberg::Vst::{NoteExpressionValueEvent, NoteOnEvent}; use crate::prelude::{NoteEvent, SysExMessage}; @@ -93,7 +93,7 @@ impl NoteExpressionController { /// Register the note ID from a note on event so it can later be retrieved when handling a note /// expression value event. pub fn register_note(&mut self, event: &NoteOnEvent) { - self.note_ids[self.note_ids_idx] = (event.note_id, event.pitch as u8, event.channel as u8); + self.note_ids[self.note_ids_idx] = (event.noteId, event.pitch as u8, event.channel as u8); self.note_ids_idx = (self.note_ids_idx + 1) % NOTE_IDS_LEN; } @@ -109,9 +109,9 @@ impl NoteExpressionController { let (note_id, note, channel) = *self .note_ids .iter() - .find(|(note_id, _, _)| *note_id == event.note_id)?; + .find(|(note_id, _, _)| *note_id == event.noteId)?; - match event.type_id { + match event.typeId { VOLUME_EXPRESSION_ID => Some(NoteEvent::PolyVolume { timing, voice_id: Some(note_id), @@ -172,33 +172,33 @@ impl NoteExpressionController { ) -> Option { match &event { NoteEvent::PolyVolume { gain, .. } => Some(NoteExpressionValueEvent { - type_id: VOLUME_EXPRESSION_ID, - note_id, + typeId: VOLUME_EXPRESSION_ID, + noteId: note_id, value: *gain as f64 / 4.0, }), NoteEvent::PolyPan { pan, .. } => Some(NoteExpressionValueEvent { - type_id: PAN_EXPRESSION_ID, - note_id, + typeId: PAN_EXPRESSION_ID, + noteId: note_id, value: (*pan as f64 + 1.0) / 2.0, }), NoteEvent::PolyTuning { tuning, .. } => Some(NoteExpressionValueEvent { - type_id: TUNING_EXPRESSION_ID, - note_id, + typeId: TUNING_EXPRESSION_ID, + noteId: note_id, value: (*tuning as f64 / 240.0) + 0.5, }), NoteEvent::PolyVibrato { vibrato, .. } => Some(NoteExpressionValueEvent { - type_id: VIBRATO_EXPRESSION_ID, - note_id, + typeId: VIBRATO_EXPRESSION_ID, + noteId: note_id, value: *vibrato as f64, }), NoteEvent::PolyExpression { expression, .. } => Some(NoteExpressionValueEvent { - type_id: EXPRESSION_EXPRESSION_ID, - note_id, + typeId: EXPRESSION_EXPRESSION_ID, + noteId: note_id, value: *expression as f64, }), NoteEvent::PolyBrightness { brightness, .. } => Some(NoteExpressionValueEvent { - type_id: BRIGHTNESS_EXPRESSION_ID, - note_id, + typeId: BRIGHTNESS_EXPRESSION_ID, + noteId: note_id, value: *brightness as f64, }), _ => None, diff --git a/src/wrapper/vst3/param_units.rs b/src/wrapper/vst3/param_units.rs index 1ded726dc..d583772dc 100644 --- a/src/wrapper/vst3/param_units.rs +++ b/src/wrapper/vst3/param_units.rs @@ -7,7 +7,7 @@ use std::collections::{HashMap, HashSet}; -use vst3_sys::vst::kRootUnitId; +use vst3::Steinberg::Vst::kRootUnitId; /// Transforms a map containing parameter hashes and slash-separated paths to an array of VST3 units /// and a mapping for each parameter hash to a unit (or to `None` if they belong to the root unit). diff --git a/src/wrapper/vst3/util.rs b/src/wrapper/vst3/util.rs index fb02eaa3e..1894c4087 100644 --- a/src/wrapper/vst3/util.rs +++ b/src/wrapper/vst3/util.rs @@ -1,13 +1,10 @@ use std::cmp; -use std::ops::Deref; -use vst3_sys::interfaces::IUnknown; -use vst3_sys::vst::TChar; -use vst3_sys::ComInterface; +use vst3::Steinberg::Vst::TChar; use widestring::U16CString; /// When `Plugin::MIDI_INPUT` is set to `MidiConfig::MidiCCs` or higher then we'll register 130*16 /// additional parameters to handle MIDI CCs, channel pressure, and pitch bend, in that order. -/// vst3-sys doesn't expose these constants. +/// vst3 doesn't expose these constants. pub const VST3_MIDI_CCS: u32 = 130; pub const VST3_MIDI_CHANNELS: u32 = 16; /// The number of parameters we'll need to register if the plugin accepts MIDI CCs. @@ -59,63 +56,6 @@ pub fn u16strlcpy(dest: &mut [TChar], src: &str) { dest[copy_len] = 0; } -/// Send+Sync wrapper for these interface pointers. -#[repr(transparent)] -pub struct VstPtr { - ptr: vst3_sys::VstPtr, -} - -/// The same as [`VstPtr`] with shared semnatics, but for objects we defined ourself since `VstPtr` -/// only works for interfaces. -#[repr(transparent)] -pub struct ObjectPtr { - ptr: *const T, -} - -impl Deref for VstPtr { - type Target = vst3_sys::VstPtr; - - fn deref(&self) -> &Self::Target { - &self.ptr - } -} - -impl Deref for ObjectPtr { - type Target = T; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.ptr } - } -} - -impl From> for VstPtr { - fn from(ptr: vst3_sys::VstPtr) -> Self { - Self { ptr } - } -} - -impl From<&T> for ObjectPtr { - /// Create a smart pointer for an existing reference counted object. - fn from(obj: &T) -> Self { - unsafe { obj.add_ref() }; - Self { ptr: obj } - } -} - -impl Drop for ObjectPtr { - fn drop(&mut self) { - unsafe { (*self).release() }; - } -} - -/// SAFETY: Sharing these pointers across thread is s safe as they have internal atomic reference -/// counting, so as long as a `VstPtr` handle exists the object will stay alive. -unsafe impl Send for VstPtr {} -unsafe impl Sync for VstPtr {} - -unsafe impl Send for ObjectPtr {} -unsafe impl Sync for ObjectPtr {} - #[cfg(test)] mod miri { use widestring::U16CStr; diff --git a/src/wrapper/vst3/view.rs b/src/wrapper/vst3/view.rs index fd0fd255a..d3b17eab6 100644 --- a/src/wrapper/vst3/view.rs +++ b/src/wrapper/vst3/view.rs @@ -5,30 +5,35 @@ use std::ffi::{c_void, CStr}; use std::mem; use std::sync::atomic::Ordering; use std::sync::Arc; -use vst3_sys::base::{kInvalidArgument, kNotImplemented, kResultFalse, kResultOk, tresult, TBool}; -use vst3_sys::gui::{IPlugFrame, IPlugView, IPlugViewContentScaleSupport, ViewRect}; -use vst3_sys::utils::SharedVstPtr; -use vst3_sys::VST3; +use vst3::Steinberg::{ + char16, int16, kInvalidArgument, kNotImplemented, kResultFalse, kResultOk, tresult, FIDString, + TBool, +}; +use vst3::Steinberg::{ + IPlugFrame, IPlugFrameTrait, IPlugView, IPlugViewContentScaleSupport, + IPlugViewContentScaleSupportTrait, IPlugViewContentScaleSupport_::ScaleFactor, IPlugViewTrait, + ViewRect, +}; +use vst3::{Class, ComPtr, ComRef, ComWrapper}; use super::inner::{Task, WrapperInner}; -use super::util::{ObjectPtr, VstPtr}; use crate::plugin::vst3::Vst3Plugin; use crate::prelude::{Editor, ParentWindowHandle}; -// Alias needed for the VST3 attribute macro -use vst3_sys as vst3_com; - // Thanks for putting this behind a platform-specific ifdef... -// NOTE: This should also be used on the BSDs, but vst3-sys exposes these interfaces only for Linux +// NOTE: This should also be used on the BSDs, which should be possible since vst3-sys is replaced by the vst3 crate #[cfg(target_os = "linux")] use { crate::event_loop::{EventLoop, MainThreadExecutor, TASK_QUEUE_CAPACITY}, crossbeam::queue::ArrayQueue, libc, - vst3_sys::gui::linux::{FileDescriptor, IEventHandler, IRunLoop}, + std::cell::Cell, + vst3::Steinberg::Linux::{ + FileDescriptor, IEventHandler, IEventHandlerTrait, IRunLoop, IRunLoopTrait, + }, }; -// Window handle type constants missing from vst3-sys +// Window handle type constants missing from vst3 #[allow(unused)] const VST3_PLATFORM_HWND: &str = "HWND"; #[allow(unused)] @@ -40,26 +45,28 @@ const VST3_PLATFORM_UIVIEW: &str = "UIView"; #[allow(unused)] const VST3_PLATFORM_X11_WINDOW: &str = "X11EmbedWindowID"; -/// FIXME: vst3-sys does not allow you to conditionally define fields with #[cfg()], so this is a -/// workaround to define the field outside of the struct +/// FIXME: vst3-sys did not allow you to conditionally define fields with #[cfg()], so this is a +/// workaround to define the field outside of the struct. Since vst3-sys is replaced by the vst3 +/// crate this might be fixable. #[cfg(target_os = "linux")] -struct RunLoopEventHandlerWrapper(RwLock>>>); +struct RunLoopEventHandlerWrapper( + RwLock>>>, +); #[cfg(not(target_os = "linux"))] struct RunLoopEventHandlerWrapper(std::marker::PhantomData

); /// The plugin's [`IPlugView`] instance created in [`IEditController::create_view()`] if `P` has an /// editor. This is managed separately so the lifetime bounds match up. -#[VST3(implements(IPlugView, IPlugViewContentScaleSupport))] pub(crate) struct WrapperView { inner: Arc>, editor: Arc>>, editor_handle: RwLock>>, /// The `IPlugFrame` instance passed by the host during [IPlugView::set_frame()]. - plug_frame: RwLock>>, + plug_frame: RwLock>>, /// Allows handling events events on the host's GUI thread when using Linux. Needed because - /// otherwise REAPER doesn't like us very much. The event handler could be implemented directly - /// on this object but vst3-sys does not let us conditionally implement interfaces. + /// otherwise REAPER doesn't like us very much. The event handler could possibly be implemented directly + /// on this object since vst3-sys is replaced by the vst3 crate. run_loop_event_handler: RunLoopEventHandlerWrapper

, /// The DPI scaling factor as passed to the [IPlugViewContentScaleSupport::set_scale_factor()] @@ -69,19 +76,23 @@ pub(crate) struct WrapperView { scaling_factor: AtomicF32, } +impl Class for WrapperView

{ + type Interfaces = (IPlugView, IPlugViewContentScaleSupport); +} + /// Allow handling tasks on the host's GUI thread on Linux. This doesn't need to be a separate -/// struct, but vst3-sys does not let us implement interfaces conditionally and the interface is -/// only exposed when compiling on Linux. The struct will register itself when calling -/// [`RunLoopEventHandler::new()`] and it will unregister itself when it gets dropped. +/// struct, but vst3-sys did not let us implement interfaces conditionally and the interface is +/// only exposed when compiling on Linux. This should be fixable since we use the vst3 crate. +/// The struct will register itself when calling [`RunLoopEventHandler::new()`] and it will +/// unregister itself when it gets dropped. #[cfg(target_os = "linux")] -#[VST3(implements(IEventHandler))] struct RunLoopEventHandler { /// We need access to the inner wrapper so we that we can post any outstanding tasks there when /// this object gets dropped so no work is lost. inner: Arc>, /// The host's run loop interface. This lets us run tasks on the same thread as the host's UI. - run_loop: VstPtr, + run_loop: ComPtr, /// We need a Unix domain socket the host can poll to know that we have an event to handle. In /// theory eventfd would be much better suited for this, but Ardour doesn't respond to fds that @@ -96,21 +107,29 @@ struct RunLoopEventHandler { /// [`on_main_thread()`][Self::on_main_thread()] on the main thread, and then continue to pop /// tasks off this queue there until it is empty. tasks: ArrayQueue>, + + /// We need access to the IEventHandler pointer to be able to call `IRunLoop::unregisterEventHandler()` when this object gets dropped. + event_handler_ptr: Cell<*mut IEventHandler>, +} + +#[cfg(target_os = "linux")] +impl Class for RunLoopEventHandler

{ + type Interfaces = (IEventHandler,); } impl WrapperView

{ - pub fn new(inner: Arc>, editor: Arc>>) -> Box { - Self::allocate( + pub fn new(inner: Arc>, editor: Arc>>) -> Self { + Self { inner, editor, - RwLock::new(None), - RwLock::new(None), + editor_handle: RwLock::new(None), + plug_frame: RwLock::new(None), #[cfg(target_os = "linux")] - RunLoopEventHandlerWrapper(RwLock::new(None)), + run_loop_event_handler: RunLoopEventHandlerWrapper(RwLock::new(None)), #[cfg(not(target_os = "linux"))] - RunLoopEventHandlerWrapper(Default::default()), - AtomicF32::new(1.0), - ) + run_loop_event_handler: RunLoopEventHandlerWrapper(Default::default()), + scaling_factor: AtomicF32::new(1.0), + } } /// Ask the host to resize the view to the size specified by [`Editor::size()`]. Will return false @@ -120,9 +139,9 @@ impl WrapperView

{ /// /// May cause memory corruption in Linux REAPER when called from outside of the `IRunLoop`. #[must_use] - pub unsafe fn request_resize(&self) -> bool { + pub unsafe fn request_resize(this: &ComWrapper) -> bool { // Don't do anything if the editor is not open, because that would be strange - if self + if this .editor_handle .try_read() .map(|e| e.is_none()) @@ -131,21 +150,19 @@ impl WrapperView

{ return false; } - match &*self.plug_frame.read() { + match &*this.plug_frame.read() { Some(plug_frame) => { - let (unscaled_width, unscaled_height) = self.editor.lock().size(); - let scaling_factor = self.scaling_factor.load(Ordering::Relaxed); + let (unscaled_width, unscaled_height) = this.editor.lock().size(); + let scaling_factor = this.scaling_factor.load(Ordering::Relaxed); let mut size = ViewRect { + left: 0, + top: 0, right: (unscaled_width as f32 * scaling_factor).round() as i32, bottom: (unscaled_height as f32 * scaling_factor).round() as i32, - ..Default::default() }; - // The argument types are a bit wonky here because you can't construct a - // `SharedVstPtr`. This _should_ work however. - let plug_view: SharedVstPtr = - mem::transmute(&self.__iplugviewvptr as *const *const _); - let result = plug_frame.resize_view(plug_view, &mut size); + let plug_view = this.as_com_ref::().unwrap(); + let result = plug_frame.resizeView(plug_view.as_ptr(), &mut size); debug_assert_eq!( result, kResultOk, @@ -180,7 +197,7 @@ impl WrapperView

{ #[cfg(target_os = "linux")] impl RunLoopEventHandler

{ - pub fn new(inner: Arc>, run_loop: VstPtr) -> Box { + pub fn new(inner: Arc>, run_loop: ComPtr) -> ComWrapper { let mut sockets = [0i32; 2]; assert_eq!( unsafe { @@ -195,23 +212,22 @@ impl RunLoopEventHandler

{ ); let [socket_read_fd, socket_write_fd] = sockets; - let handler = RunLoopEventHandler::allocate( + let mut handler = ComWrapper::new(RunLoopEventHandler { inner, run_loop, socket_read_fd, socket_write_fd, - ArrayQueue::new(TASK_QUEUE_CAPACITY), - ); + tasks: ArrayQueue::new(TASK_QUEUE_CAPACITY), + event_handler_ptr: Cell::new(std::ptr::null_mut()), + }); + let event_handler_ptr = handler.to_com_ptr::().unwrap().into_raw(); + handler.event_handler_ptr.set(event_handler_ptr); - // vst3-sys provides no way to convert to a SharedVstPtr, so, uh, yeah. These are pointers - // to vtable poitners. - let event_handler: SharedVstPtr = - unsafe { mem::transmute(&handler.__ieventhandlervptr as *const *const _) }; assert_eq!( unsafe { handler .run_loop - .register_event_handler(event_handler, handler.socket_read_fd) + .registerEventHandler(event_handler_ptr, handler.socket_read_fd) }, kResultOk ); @@ -244,9 +260,9 @@ impl RunLoopEventHandler

{ } } -impl IPlugView for WrapperView

{ +impl IPlugViewTrait for WrapperView

{ #[cfg(all(target_family = "unix", not(target_os = "macos")))] - unsafe fn is_platform_type_supported(&self, type_: vst3_sys::base::FIDString) -> tresult { + unsafe fn isPlatformTypeSupported(&self, type_: FIDString) -> tresult { let type_ = CStr::from_ptr(type_); match type_.to_str() { Ok(type_) if type_ == VST3_PLATFORM_X11_WINDOW => kResultOk, @@ -258,7 +274,7 @@ impl IPlugView for WrapperView

{ } #[cfg(target_os = "macos")] - unsafe fn is_platform_type_supported(&self, type_: vst3_sys::base::FIDString) -> tresult { + unsafe fn isPlatformTypeSupported(&self, type_: FIDString) -> tresult { let type_ = CStr::from_ptr(type_); match type_.to_str() { Ok(type_) if type_ == VST3_PLATFORM_NSVIEW => kResultOk, @@ -270,7 +286,7 @@ impl IPlugView for WrapperView

{ } #[cfg(target_os = "windows")] - unsafe fn is_platform_type_supported(&self, type_: vst3_sys::base::FIDString) -> tresult { + unsafe fn isPlatformTypeSupported(&self, type_: FIDString) -> tresult { let type_ = CStr::from_ptr(type_); match type_.to_str() { Ok(type_) if type_ == VST3_PLATFORM_HWND => kResultOk, @@ -281,7 +297,7 @@ impl IPlugView for WrapperView

{ } } - unsafe fn attached(&self, parent: *mut c_void, type_: vst3_sys::base::FIDString) -> tresult { + unsafe fn attached(&self, parent: *mut c_void, type_: FIDString) -> tresult { let mut editor_handle = self.editor_handle.write(); if editor_handle.is_none() { let type_ = CStr::from_ptr(type_); @@ -304,7 +320,7 @@ impl IPlugView for WrapperView

{ .lock() .spawn(parent_handle, self.inner.clone().make_gui_context()), ); - *self.inner.plug_view.write() = Some(ObjectPtr::from(self)); + self.inner.is_editor_open.store(true, Ordering::SeqCst); kResultOk } else { @@ -319,7 +335,7 @@ impl IPlugView for WrapperView

{ unsafe fn removed(&self) -> tresult { let mut editor_handle = self.editor_handle.write(); if editor_handle.is_some() { - *self.inner.plug_view.write() = None; + self.inner.is_editor_open.store(false, Ordering::SeqCst); *editor_handle = None; kResultOk @@ -330,31 +346,21 @@ impl IPlugView for WrapperView

{ } } - unsafe fn on_wheel(&self, _distance: f32) -> tresult { + unsafe fn onWheel(&self, _distance: f32) -> tresult { // We'll let the plugin use the OS' input mechanisms because not all DAWs (or very few // actually) implement these functions kNotImplemented } - unsafe fn on_key_down( - &self, - _key: vst3_sys::base::char16, - _key_code: i16, - _modifiers: i16, - ) -> tresult { + unsafe fn onKeyDown(&self, _key: char16, _key_code: int16, _modifiers: int16) -> tresult { kNotImplemented } - unsafe fn on_key_up( - &self, - _key: vst3_sys::base::char16, - _key_code: i16, - _modifiers: i16, - ) -> tresult { + unsafe fn onKeyUp(&self, _key: char16, _key_code: int16, _modifiers: int16) -> tresult { kNotImplemented } - unsafe fn get_size(&self, size: *mut ViewRect) -> tresult { + unsafe fn getSize(&self, size: *mut ViewRect) -> tresult { check_null_ptr!(size); *size = mem::zeroed(); @@ -373,7 +379,7 @@ impl IPlugView for WrapperView

{ kResultOk } - unsafe fn on_size(&self, new_size: *mut ViewRect) -> tresult { + unsafe fn onSize(&self, new_size: *mut ViewRect) -> tresult { check_null_ptr!(new_size); // TODO: Implement Host->Plugin resizing @@ -393,24 +399,22 @@ impl IPlugView for WrapperView

{ } } - unsafe fn on_focus(&self, _state: TBool) -> tresult { + unsafe fn onFocus(&self, _state: TBool) -> tresult { kNotImplemented } - unsafe fn set_frame(&self, frame: *mut c_void) -> tresult { - // The correct argument type is missing from the bindings - let frame: SharedVstPtr = mem::transmute(frame); - match frame.upgrade() { + unsafe fn setFrame(&self, frame: *mut IPlugFrame) -> tresult { + match ComRef::from_raw(frame) { Some(frame) => { // On Linux the host will expose another interface that lets us run code on the // host's GUI thread. REAPER will segfault when we don't do this for resizes. #[cfg(target_os = "linux")] { - *self.run_loop_event_handler.0.write() = frame.cast().map(|run_loop| { - RunLoopEventHandler::new(self.inner.clone(), VstPtr::from(run_loop)) - }); + *self.run_loop_event_handler.0.write() = frame + .cast() + .map(|run_loop| RunLoopEventHandler::new(self.inner.clone(), run_loop)); } - *self.plug_frame.write() = Some(VstPtr::from(frame)); + *self.plug_frame.write() = Some(frame.to_com_ptr()); } None => { #[cfg(target_os = "linux")] @@ -424,12 +428,12 @@ impl IPlugView for WrapperView

{ kResultOk } - unsafe fn can_resize(&self) -> tresult { + unsafe fn canResize(&self) -> tresult { // TODO: Implement Host->Plugin resizing kResultFalse } - unsafe fn check_size_constraint(&self, rect: *mut ViewRect) -> tresult { + unsafe fn checkSizeConstraint(&self, rect: *mut ViewRect) -> tresult { check_null_ptr!(rect); // TODO: Implement Host->Plugin resizing @@ -441,8 +445,8 @@ impl IPlugView for WrapperView

{ } } -impl IPlugViewContentScaleSupport for WrapperView

{ - unsafe fn set_scale_factor(&self, factor: f32) -> tresult { +impl IPlugViewContentScaleSupportTrait for WrapperView

{ + unsafe fn setContentScaleFactor(&self, factor: ScaleFactor) -> tresult { // TODO: So apparently Ableton Live doesn't call this function. Right now we'll hardcode the // default scale to 1.0 on Linux and Windows since we can't easily get the scale from // baseview. A better alternative would be to do the fallback DPI scale detection @@ -466,8 +470,8 @@ impl IPlugViewContentScaleSupport for WrapperView

{ } #[cfg(target_os = "linux")] -impl IEventHandler for RunLoopEventHandler

{ - unsafe fn on_fd_is_set(&self, _fd: FileDescriptor) { +impl IEventHandlerTrait for RunLoopEventHandler

{ + unsafe fn onFDIsSet(&self, _fd: FileDescriptor) { // There should be a one-to-one correlation to bytes being written to `self.socket_read_fd` // and events being pushed to `self.tasks`, but because the process of pushing a task and // notifying this thread through the socket is not atomic we can't reliably just read a byte @@ -528,8 +532,9 @@ impl Drop for RunLoopEventHandler

{ libc::close(self.socket_write_fd); } - let event_handler: SharedVstPtr = - unsafe { mem::transmute(&self.__ieventhandlervptr as *const _) }; - unsafe { self.run_loop.unregister_event_handler(event_handler) }; + unsafe { + self.run_loop + .unregisterEventHandler(self.event_handler_ptr.get()); + } } } diff --git a/src/wrapper/vst3/wrapper.rs b/src/wrapper/vst3/wrapper.rs index f284af54a..59295e850 100644 --- a/src/wrapper/vst3/wrapper.rs +++ b/src/wrapper/vst3/wrapper.rs @@ -5,25 +5,33 @@ use std::num::NonZeroU32; use std::ptr::NonNull; use std::sync::atomic::Ordering; use std::sync::Arc; -use vst3_com::vst::{DataEvent, IProcessContextRequirementsFlags, ProcessModes}; -use vst3_sys::base::{kInvalidArgument, kNoInterface, kResultFalse, kResultOk, tresult, TBool}; -use vst3_sys::base::{IBStream, IPluginBase}; -use vst3_sys::utils::SharedVstPtr; -use vst3_sys::vst::{ - kNoParamId, kNoParentUnitId, kNoProgramListId, kRootUnitId, Event, EventTypes, IAudioProcessor, - IComponent, IEditController, IEventList, IMidiMapping, INoteExpressionController, - IParamValueQueue, IParameterChanges, IProcessContextRequirements, IUnitInfo, - LegacyMidiCCOutEvent, NoteExpressionTypeInfo, NoteExpressionValueDescription, NoteOffEvent, - NoteOnEvent, ParameterFlags, PolyPressureEvent, ProgramListInfo, TChar, UnitInfo, +use vst3::Steinberg::Vst::ProcessContext_::StatesAndFlags_::{ + kBarPositionValid, kCycleActive, kCycleValid, kPlaying, kProjectTimeMusicValid, kRecording, + kTempoValid, kTimeSigValid, }; -use vst3_sys::VST3; +use vst3::Steinberg::Vst::{ + kNoParamId, kNoParentUnitId, kNoProgramListId, kRootUnitId, BusDirection, CString, CtrlNumber, + DataEvent, Event, Event_::EventTypes_, IAudioProcessor, IAudioProcessorTrait, IComponent, + IComponentHandler, IComponentTrait, IEditController, IEditControllerTrait, IEventListTrait, + IMidiMapping, IMidiMappingTrait, INoteExpressionController, INoteExpressionControllerTrait, + IParamValueQueueTrait, IParameterChangesTrait, IProcessContextRequirements, + IProcessContextRequirementsTrait, IProcessContextRequirements_, IUnitInfo, IUnitInfoTrait, + IoMode, LegacyMIDICCOutEvent, MediaType, NoteExpressionTypeID, NoteExpressionTypeInfo, + NoteExpressionValue, NoteExpressionValueDescription, NoteOffEvent, NoteOnEvent, ParamID, + ParamValue, ParameterInfo, ParameterInfo_::ParameterFlags_, PolyPressureEvent, ProcessData, + ProcessModes_, ProcessSetup, ProgramListID, ProgramListInfo, SpeakerArrangement, String128, + TChar, UnitID, UnitInfo, +}; +use vst3::Steinberg::{ + int16, int32, kInvalidArgument, kNoInterface, kResultFalse, kResultOk, tresult, uint32, + FIDString, FUnknown, IBStream, IBStreamTrait, IPlugView, IPluginBaseTrait, TBool, TUID, +}; +use vst3::{Class, ComRef, ComWrapper}; use widestring::U16CStr; use super::inner::{ProcessEvent, WrapperInner}; use super::note_expressions::{self, NoteExpressionController}; -use super::util::{ - u16strlcpy, VstPtr, VST3_MIDI_CCS, VST3_MIDI_NUM_PARAMS, VST3_MIDI_PARAMS_START, -}; +use super::util::{u16strlcpy, VST3_MIDI_CCS, VST3_MIDI_NUM_PARAMS, VST3_MIDI_PARAMS_START}; use super::util::{VST3_MIDI_CHANNELS, VST3_MIDI_PARAMS_END}; use super::view::WrapperView; use crate::prelude::{ @@ -35,25 +43,27 @@ use crate::wrapper::state; use crate::wrapper::util::buffer_management::{BufferManager, ChannelPointers}; use crate::wrapper::util::{clamp_input_event_timing, clamp_output_event_timing, process_wrapper}; -// Alias needed for the VST3 attribute macro -use vst3_sys as vst3_com; - -#[VST3(implements( - IComponent, - IEditController, - IAudioProcessor, - IMidiMapping, - INoteExpressionController, - IProcessContextRequirements, - IUnitInfo -))] pub struct Wrapper { inner: Arc>, } +impl Class for Wrapper

{ + type Interfaces = ( + IComponent, + IEditController, + IAudioProcessor, + IMidiMapping, + INoteExpressionController, + IProcessContextRequirements, + IUnitInfo, + ); +} + impl Wrapper

{ - pub fn new() -> Box { - Self::allocate(WrapperInner::new()) + pub fn new() -> Self { + Self { + inner: WrapperInner::new(), + } } } @@ -63,8 +73,8 @@ impl Drop for Wrapper

{ } } -impl IPluginBase for Wrapper

{ - unsafe fn initialize(&self, _context: *mut c_void) -> tresult { +impl IPluginBaseTrait for Wrapper

{ + unsafe fn initialize(&self, _context: *mut FUnknown) -> tresult { // We currently don't need or allow any initialization logic kResultOk } @@ -74,30 +84,30 @@ impl IPluginBase for Wrapper

{ } } -impl IComponent for Wrapper

{ - unsafe fn get_controller_class_id(&self, _tuid: *mut vst3_sys::IID) -> tresult { +impl IComponentTrait for Wrapper

{ + unsafe fn getControllerClassId(&self, _classId: *mut TUID) -> tresult { // We won't separate the edit controller to keep the implementation a bit smaller kNoInterface } - unsafe fn set_io_mode(&self, _mode: vst3_sys::vst::IoMode) -> tresult { + unsafe fn setIoMode(&self, _mode: IoMode) -> tresult { // Not quite sure what the point of this is when the processing setup also receives similar // information kResultOk } - unsafe fn get_bus_count( + unsafe fn getBusCount( &self, - type_: vst3_sys::vst::MediaType, - dir: vst3_sys::vst::BusDirection, - ) -> i32 { + type_: vst3::Steinberg::Vst::MediaType, + dir: vst3::Steinberg::Vst::BusDirection, + ) -> int32 { let current_audio_io_layout = self.inner.current_audio_io_layout.load(); // A plugin has a main input and output bus if the default number of channels is non-zero, // and a plugin can also have auxiliary input and output busses match type_ { - x if x == vst3_sys::vst::MediaTypes::kAudio as i32 - && dir == vst3_sys::vst::BusDirections::kInput as i32 => + x if x == vst3::Steinberg::Vst::MediaTypes_::kAudio as i32 + && dir == vst3::Steinberg::Vst::BusDirections_::kInput as i32 => { let main_busses = if current_audio_io_layout.main_input_channels.is_some() { 1 @@ -108,8 +118,8 @@ impl IComponent for Wrapper

{ main_busses + aux_busses } - x if x == vst3_sys::vst::MediaTypes::kAudio as i32 - && dir == vst3_sys::vst::BusDirections::kOutput as i32 => + x if x == vst3::Steinberg::Vst::MediaTypes_::kAudio as i32 + && dir == vst3::Steinberg::Vst::BusDirections_::kOutput as i32 => { let main_busses = if current_audio_io_layout.main_output_channels.is_some() { 1 @@ -120,14 +130,14 @@ impl IComponent for Wrapper

{ main_busses + aux_busses } - x if x == vst3_sys::vst::MediaTypes::kEvent as i32 - && dir == vst3_sys::vst::BusDirections::kInput as i32 + x if x == vst3::Steinberg::Vst::MediaTypes_::kEvent as i32 + && dir == vst3::Steinberg::Vst::BusDirections_::kInput as i32 && P::MIDI_INPUT >= MidiConfig::Basic => { 1 } - x if x == vst3_sys::vst::MediaTypes::kEvent as i32 - && dir == vst3_sys::vst::BusDirections::kOutput as i32 + x if x == vst3::Steinberg::Vst::MediaTypes_::kEvent as i32 + && dir == vst3::Steinberg::Vst::BusDirections_::kOutput as i32 && P::MIDI_OUTPUT >= MidiConfig::Basic => { 1 @@ -136,12 +146,12 @@ impl IComponent for Wrapper

{ } } - unsafe fn get_bus_info( + unsafe fn getBusInfo( &self, - type_: vst3_sys::vst::MediaType, - dir: vst3_sys::vst::BusDirection, - index: i32, - info: *mut vst3_sys::vst::BusInfo, + type_: vst3::Steinberg::Vst::MediaType, + dir: vst3::Steinberg::Vst::BusDirection, + index: int32, + info: *mut vst3::Steinberg::Vst::BusInfo, ) -> tresult { check_null_ptr!(info); @@ -149,29 +159,29 @@ impl IComponent for Wrapper

{ match (type_, dir, index) { (t, d, _) - if t == vst3_sys::vst::MediaTypes::kAudio as i32 - && d == vst3_sys::vst::BusDirections::kInput as i32 => + if t == vst3::Steinberg::Vst::MediaTypes_::kAudio as i32 + && d == vst3::Steinberg::Vst::BusDirections_::kInput as i32 => { *info = mem::zeroed(); let info = &mut *info; - info.media_type = vst3_sys::vst::MediaTypes::kAudio as i32; + info.mediaType = vst3::Steinberg::Vst::MediaTypes_::kAudio as i32; info.direction = dir; - info.flags = vst3_sys::vst::BusFlags::kDefaultActive as u32; + info.flags = vst3::Steinberg::Vst::BusInfo_::BusFlags_::kDefaultActive as u32; let has_main_input = current_audio_io_layout.main_input_channels.is_some(); let aux_input_start_idx = if has_main_input { 1 } else { 0 }; let aux_input_idx = (index - aux_input_start_idx).max(0) as usize; if index == 0 && has_main_input { - info.bus_type = vst3_sys::vst::BusTypes::kMain as i32; - info.channel_count = + info.busType = vst3::Steinberg::Vst::BusTypes_::kMain as i32; + info.channelCount = current_audio_io_layout.main_input_channels.unwrap().get() as i32; u16strlcpy(&mut info.name, ¤t_audio_io_layout.main_input_name()); kResultOk } else if aux_input_idx < current_audio_io_layout.aux_input_ports.len() { - info.bus_type = vst3_sys::vst::BusTypes::kAux as i32; - info.channel_count = + info.busType = vst3::Steinberg::Vst::BusTypes_::kAux as i32; + info.channelCount = current_audio_io_layout.aux_input_ports[aux_input_idx].get() as i32; u16strlcpy( &mut info.name, @@ -186,24 +196,24 @@ impl IComponent for Wrapper

{ } } (t, d, _) - if t == vst3_sys::vst::MediaTypes::kAudio as i32 - && d == vst3_sys::vst::BusDirections::kOutput as i32 => + if t == vst3::Steinberg::Vst::MediaTypes_::kAudio as i32 + && d == vst3::Steinberg::Vst::BusDirections_::kOutput as i32 => { *info = mem::zeroed(); let info = &mut *info; - info.media_type = vst3_sys::vst::MediaTypes::kAudio as i32; + info.mediaType = vst3::Steinberg::Vst::MediaTypes_::kAudio as i32; info.direction = dir; - info.flags = vst3_sys::vst::BusFlags::kDefaultActive as u32; + info.flags = vst3::Steinberg::Vst::BusInfo_::BusFlags_::kDefaultActive as u32; let has_main_output = current_audio_io_layout.main_output_channels.is_some(); let aux_output_start_idx = if has_main_output { 1 } else { 0 }; let aux_output_idx = (index - aux_output_start_idx).max(0) as usize; if index == 0 && has_main_output { - info.bus_type = vst3_sys::vst::BusTypes::kMain as i32; + info.busType = vst3::Steinberg::Vst::BusTypes_::kMain as i32; // NOTE: See above, this becomes a 0 channel output if the plugin doesn't have a // main output - info.channel_count = current_audio_io_layout + info.channelCount = current_audio_io_layout .main_output_channels .map(NonZeroU32::get) .unwrap_or_default() as i32; @@ -211,8 +221,8 @@ impl IComponent for Wrapper

{ kResultOk } else if aux_output_idx < current_audio_io_layout.aux_output_ports.len() { - info.bus_type = vst3_sys::vst::BusTypes::kAux as i32; - info.channel_count = + info.busType = vst3::Steinberg::Vst::BusTypes_::kAux as i32; + info.channelCount = current_audio_io_layout.aux_output_ports[aux_output_idx].get() as i32; u16strlcpy( &mut info.name, @@ -227,45 +237,45 @@ impl IComponent for Wrapper

{ } } (t, d, 0) - if t == vst3_sys::vst::MediaTypes::kEvent as i32 - && d == vst3_sys::vst::BusDirections::kInput as i32 + if t == vst3::Steinberg::Vst::MediaTypes_::kEvent as i32 + && d == vst3::Steinberg::Vst::BusDirections_::kInput as i32 && P::MIDI_INPUT >= MidiConfig::Basic => { *info = mem::zeroed(); let info = &mut *info; - info.media_type = vst3_sys::vst::MediaTypes::kEvent as i32; - info.direction = vst3_sys::vst::BusDirections::kInput as i32; - info.channel_count = 16; + info.mediaType = vst3::Steinberg::Vst::MediaTypes_::kEvent as i32; + info.direction = vst3::Steinberg::Vst::BusDirections_::kInput as i32; + info.channelCount = 16; u16strlcpy(&mut info.name, "Note Input"); - info.bus_type = vst3_sys::vst::BusTypes::kMain as i32; - info.flags = vst3_sys::vst::BusFlags::kDefaultActive as u32; + info.busType = vst3::Steinberg::Vst::BusTypes_::kMain as i32; + info.flags = vst3::Steinberg::Vst::BusInfo_::BusFlags_::kDefaultActive as u32; kResultOk } (t, d, 0) - if t == vst3_sys::vst::MediaTypes::kEvent as i32 - && d == vst3_sys::vst::BusDirections::kOutput as i32 + if t == vst3::Steinberg::Vst::MediaTypes_::kEvent as i32 + && d == vst3::Steinberg::Vst::BusDirections_::kOutput as i32 && P::MIDI_OUTPUT >= MidiConfig::Basic => { *info = mem::zeroed(); let info = &mut *info; - info.media_type = vst3_sys::vst::MediaTypes::kEvent as i32; - info.direction = vst3_sys::vst::BusDirections::kOutput as i32; - info.channel_count = 16; + info.mediaType = vst3::Steinberg::Vst::MediaTypes_::kEvent as i32; + info.direction = vst3::Steinberg::Vst::BusDirections_::kOutput as i32; + info.channelCount = 16; u16strlcpy(&mut info.name, "Note Output"); - info.bus_type = vst3_sys::vst::BusTypes::kMain as i32; - info.flags = vst3_sys::vst::BusFlags::kDefaultActive as u32; + info.busType = vst3::Steinberg::Vst::BusTypes_::kMain as i32; + info.flags = vst3::Steinberg::Vst::BusInfo_::BusFlags_::kDefaultActive as u32; kResultOk } _ => kInvalidArgument, } } - unsafe fn get_routing_info( + unsafe fn getRoutingInfo( &self, - in_info: *mut vst3_sys::vst::RoutingInfo, - out_info: *mut vst3_sys::vst::RoutingInfo, + in_info: *mut vst3::Steinberg::Vst::RoutingInfo, + out_info: *mut vst3::Steinberg::Vst::RoutingInfo, ) -> tresult { check_null_ptr!(in_info, out_info); @@ -275,26 +285,26 @@ impl IComponent for Wrapper

{ let in_info = &*in_info; let out_info = &mut *out_info; - match (in_info.media_type, in_info.bus_index) { + match (in_info.mediaType, in_info.busIndex) { (t, 0) - if t == vst3_sys::vst::MediaTypes::kAudio as i32 + if t == vst3::Steinberg::Vst::MediaTypes_::kAudio as i32 // We only have an IO pair when the plugin has both a main input and a main output && current_audio_io_layout.main_input_channels.is_some() && current_audio_io_layout.main_output_channels.is_some() => { - out_info.media_type = vst3_sys::vst::MediaTypes::kAudio as i32; - out_info.bus_index = in_info.bus_index; + out_info.mediaType = vst3::Steinberg::Vst::MediaTypes_::kAudio as i32; + out_info.busIndex = in_info.busIndex; out_info.channel = in_info.channel; kResultOk } (t, 0) - if t == vst3_sys::vst::MediaTypes::kEvent as i32 + if t == vst3::Steinberg::Vst::MediaTypes_::kEvent as i32 && P::MIDI_INPUT >= MidiConfig::Basic && P::MIDI_OUTPUT >= MidiConfig::Basic => { - out_info.media_type = vst3_sys::vst::MediaTypes::kEvent as i32; - out_info.bus_index = in_info.bus_index; + out_info.mediaType = vst3::Steinberg::Vst::MediaTypes_::kEvent as i32; + out_info.busIndex = in_info.busIndex; out_info.channel = in_info.channel; kResultOk @@ -303,12 +313,12 @@ impl IComponent for Wrapper

{ } } - unsafe fn activate_bus( + unsafe fn activateBus( &self, - type_: vst3_sys::vst::MediaType, - dir: vst3_sys::vst::BusDirection, - index: i32, - _state: vst3_sys::base::TBool, + type_: vst3::Steinberg::Vst::MediaType, + dir: vst3::Steinberg::Vst::BusDirection, + index: int32, + _state: TBool, ) -> tresult { let current_audio_io_layout = self.inner.current_audio_io_layout.load(); @@ -316,8 +326,8 @@ impl IComponent for Wrapper

{ // that match (type_, dir, index) { (t, d, _) - if t == vst3_sys::vst::MediaTypes::kAudio as i32 - && d == vst3_sys::vst::BusDirections::kInput as i32 => + if t == vst3::Steinberg::Vst::MediaTypes_::kAudio as i32 + && d == vst3::Steinberg::Vst::BusDirections_::kInput as i32 => { let main_busses = if current_audio_io_layout.main_input_channels.is_some() { 1 @@ -333,8 +343,8 @@ impl IComponent for Wrapper

{ } } (t, d, _) - if t == vst3_sys::vst::MediaTypes::kAudio as i32 - && d == vst3_sys::vst::BusDirections::kOutput as i32 => + if t == vst3::Steinberg::Vst::MediaTypes_::kAudio as i32 + && d == vst3::Steinberg::Vst::BusDirections_::kOutput as i32 => { let main_busses = if current_audio_io_layout.main_output_channels.is_some() { 1 @@ -350,15 +360,15 @@ impl IComponent for Wrapper

{ } } (t, d, 0) - if t == vst3_sys::vst::MediaTypes::kEvent as i32 - && d == vst3_sys::vst::BusDirections::kInput as i32 + if t == vst3::Steinberg::Vst::MediaTypes_::kEvent as i32 + && d == vst3::Steinberg::Vst::BusDirections_::kInput as i32 && P::MIDI_INPUT >= MidiConfig::Basic => { kResultOk } (t, d, 0) - if t == vst3_sys::vst::MediaTypes::kEvent as i32 - && d == vst3_sys::vst::BusDirections::kOutput as i32 + if t == vst3::Steinberg::Vst::MediaTypes_::kEvent as i32 + && d == vst3::Steinberg::Vst::BusDirections_::kOutput as i32 && P::MIDI_OUTPUT >= MidiConfig::Basic => { kResultOk @@ -367,7 +377,7 @@ impl IComponent for Wrapper

{ } } - unsafe fn set_active(&self, state: TBool) -> tresult { + unsafe fn setActive(&self, state: TBool) -> tresult { // We could call initialize in `IAudioProcessor::setup_processing()`, but REAPER will set // the bus arrangements between that function and this function. So to be able to handle // custom channel layout overrides we need to initialize here. @@ -409,10 +419,12 @@ impl IComponent for Wrapper

{ } } - unsafe fn set_state(&self, state: SharedVstPtr) -> tresult { + unsafe fn setState(&self, state: *mut IBStream) -> tresult { + use vst3::Steinberg::IBStream_::IStreamSeekMode_::*; + check_null_ptr!(state); - let state = state.upgrade().unwrap(); + let state = ComRef::from_raw(state).unwrap(); // We need to know how large the state is before we can read it. The current position can be // zero, but it can also be something else. Bitwig prepends the preset header in the stream, @@ -420,12 +432,8 @@ impl IComponent for Wrapper

{ let mut current_pos = 0; let mut eof_pos = 0; if state.tell(&mut current_pos) != kResultOk - || state.seek(0, vst3_sys::base::kIBSeekEnd, &mut eof_pos) != kResultOk - || state.seek( - current_pos, - vst3_sys::base::kIBSeekSet, - std::ptr::null_mut(), - ) != kResultOk + || state.seek(0, kIBSeekEnd as int32, &mut eof_pos) != kResultOk + || state.seek(current_pos, kIBSeekSet as int32, std::ptr::null_mut()) != kResultOk { nih_debug_assert_failure!("Could not get the stream length"); return kResultFalse; @@ -462,10 +470,10 @@ impl IComponent for Wrapper

{ } } - unsafe fn get_state(&self, state: SharedVstPtr) -> tresult { + unsafe fn getState(&self, state: *mut IBStream) -> tresult { check_null_ptr!(state); - let state = state.upgrade().unwrap(); + let state = ComRef::from_raw(state).unwrap(); let serialized = state::serialize_json::

( self.inner.params.clone(), @@ -475,7 +483,7 @@ impl IComponent for Wrapper

{ Ok(serialized) => { let mut num_bytes_written = 0; let result = state.write( - serialized.as_ptr() as *const c_void, + serialized.as_ptr() as *mut c_void, serialized.len() as i32, &mut num_bytes_written, ); @@ -495,25 +503,25 @@ impl IComponent for Wrapper

{ } } -impl IEditController for Wrapper

{ - unsafe fn set_component_state(&self, _state: SharedVstPtr) -> tresult { +impl IEditControllerTrait for Wrapper

{ + unsafe fn setComponentState(&self, _state: *mut IBStream) -> tresult { // We have a single file component, so we don't need to do anything here kResultOk } - unsafe fn set_state(&self, _state: SharedVstPtr) -> tresult { + unsafe fn setState(&self, _state: *mut IBStream) -> tresult { // We don't store any separate state here. The plugin's state will have been restored // through the component. Calling that same function here will likely lead to duplicate // state restores kResultOk } - unsafe fn get_state(&self, _state: SharedVstPtr) -> tresult { + unsafe fn getState(&self, _state: *mut IBStream) -> tresult { // Same for this function kResultOk } - unsafe fn get_parameter_count(&self) -> i32 { + unsafe fn getParameterCount(&self) -> int32 { // We need to add a whole bunch of parameters if the plugin accepts MIDI CCs if P::MIDI_INPUT >= MidiConfig::MidiCCs { self.inner.param_hashes.len() as i32 + VST3_MIDI_NUM_PARAMS as i32 @@ -522,14 +530,10 @@ impl IEditController for Wrapper

{ } } - unsafe fn get_parameter_info( - &self, - param_index: i32, - info: *mut vst3_sys::vst::ParameterInfo, - ) -> tresult { + unsafe fn getParameterInfo(&self, param_index: int32, info: *mut ParameterInfo) -> tresult { check_null_ptr!(info); - if param_index < 0 || param_index > self.get_parameter_count() { + if param_index < 0 || param_index > self.getParameterCount() { return kInvalidArgument; } @@ -554,8 +558,8 @@ impl IEditController for Wrapper

{ info.id = VST3_MIDI_PARAMS_START + midi_param_relative_idx; u16strlcpy(&mut info.title, &name); - u16strlcpy(&mut info.short_title, &name); - info.flags = ParameterFlags::kIsReadOnly as i32 | (1 << 4); // kIsHidden + u16strlcpy(&mut info.shortTitle, &name); + info.flags = ParameterFlags_::kIsReadOnly as i32 | (1 << 4); // kIsHidden } else { let param_hash = &self.inner.param_hashes[param_index as usize]; let param_unit = &self @@ -572,31 +576,31 @@ impl IEditController for Wrapper

{ info.id = *param_hash; u16strlcpy(&mut info.title, param_ptr.name()); - u16strlcpy(&mut info.short_title, param_ptr.name()); + u16strlcpy(&mut info.shortTitle, param_ptr.name()); u16strlcpy(&mut info.units, param_ptr.unit()); - info.step_count = param_ptr.step_count().unwrap_or(0) as i32; - info.default_normalized_value = default_value as f64; - info.unit_id = *param_unit; + info.stepCount = param_ptr.step_count().unwrap_or(0) as i32; + info.defaultNormalizedValue = default_value as f64; + info.unitId = *param_unit; info.flags = 0; if automatable && !hidden { - info.flags |= ParameterFlags::kCanAutomate as i32; + info.flags |= ParameterFlags_::kCanAutomate as i32; } if hidden { - info.flags |= ParameterFlags::kIsReadOnly as i32 | (1 << 4); // kIsHidden + info.flags |= ParameterFlags_::kIsReadOnly as i32 | (1 << 4); // kIsHidden } if is_bypass { - info.flags |= ParameterFlags::kIsBypass as i32; + info.flags |= ParameterFlags_::kIsBypass as i32; } } kResultOk } - unsafe fn get_param_string_by_value( + unsafe fn getParamStringByValue( &self, - id: u32, - value_normalized: f64, - string: *mut TChar, + id: ParamID, + value_normalized: ParamValue, + string: *mut String128, ) -> tresult { check_null_ptr!(string); @@ -617,11 +621,11 @@ impl IEditController for Wrapper

{ } } - unsafe fn get_param_value_by_string( + unsafe fn getParamValueByString( &self, - id: u32, - string: *const TChar, - value_normalized: *mut f64, + id: ParamID, + string: *mut TChar, + value_normalized: *mut ParamValue, ) -> tresult { check_null_ptr!(string, value_normalized); @@ -644,28 +648,32 @@ impl IEditController for Wrapper

{ } } - unsafe fn normalized_param_to_plain(&self, id: u32, value_normalized: f64) -> f64 { + unsafe fn normalizedParamToPlain( + &self, + id: ParamID, + value_normalized: ParamValue, + ) -> ParamValue { match self.inner.param_by_hash.get(&id) { Some(param_ptr) => param_ptr.preview_plain(value_normalized as f32) as f64, _ => value_normalized, } } - unsafe fn plain_param_to_normalized(&self, id: u32, plain_value: f64) -> f64 { + unsafe fn plainParamToNormalized(&self, id: ParamID, plain_value: ParamValue) -> ParamValue { match self.inner.param_by_hash.get(&id) { Some(param_ptr) => param_ptr.preview_normalized(plain_value as f32) as f64, _ => plain_value, } } - unsafe fn get_param_normalized(&self, id: u32) -> f64 { + unsafe fn getParamNormalized(&self, id: ParamID) -> ParamValue { match self.inner.param_by_hash.get(&id) { Some(param_ptr) => param_ptr.modulated_normalized_value() as f64, _ => 0.5, } } - unsafe fn set_param_normalized(&self, id: u32, value: f64) -> tresult { + unsafe fn setParamNormalized(&self, id: ParamID, value: ParamValue) -> tresult { // If the plugin is currently processing audio, then this parameter change will also be sent // to the process function if self.inner.is_processing.load(Ordering::SeqCst) { @@ -681,33 +689,35 @@ impl IEditController for Wrapper

{ .set_normalized_value_by_hash(id, value as f32, sample_rate) } - unsafe fn set_component_handler( - &self, - handler: SharedVstPtr, - ) -> tresult { - *self.inner.component_handler.borrow_mut() = handler.upgrade().map(VstPtr::from); + unsafe fn setComponentHandler(&self, handler: *mut IComponentHandler) -> tresult { + *self.inner.component_handler.borrow_mut() = + ComRef::from_raw(handler).map(|r| r.to_com_ptr()); kResultOk } - unsafe fn create_view(&self, _name: vst3_sys::base::FIDString) -> *mut c_void { + unsafe fn createView(&self, _name: FIDString) -> *mut IPlugView { // Without specialization this is the least redundant way to check if the plugin has an // editor. The default implementation returns a None here. match self.inner.editor.borrow().as_ref() { - Some(editor) => Box::into_raw(WrapperView::new(self.inner.clone(), editor.clone())) - as *mut vst3_sys::c_void, + Some(editor) => { + let view = ComWrapper::new(WrapperView::new(self.inner.clone(), editor.clone())); + let plug_view_ptr = view.to_com_ptr::().unwrap().into_raw(); + *self.inner.plug_view.write() = Some(view); + plug_view_ptr + } None => std::ptr::null_mut(), } } } -impl IAudioProcessor for Wrapper

{ - unsafe fn set_bus_arrangements( +impl IAudioProcessorTrait for Wrapper

{ + unsafe fn setBusArrangements( &self, - inputs: *mut vst3_sys::vst::SpeakerArrangement, - num_ins: i32, - outputs: *mut vst3_sys::vst::SpeakerArrangement, - num_outs: i32, + inputs: *mut SpeakerArrangement, + num_ins: int32, + outputs: *mut SpeakerArrangement, + num_outs: int32, ) -> tresult { check_null_ptr!(inputs, outputs); @@ -789,22 +799,22 @@ impl IAudioProcessor for Wrapper

{ } } - unsafe fn get_bus_arrangement( + unsafe fn getBusArrangement( &self, - dir: vst3_sys::vst::BusDirection, + dir: BusDirection, index: i32, - arr: *mut vst3_sys::vst::SpeakerArrangement, + arr: *mut SpeakerArrangement, ) -> tresult { check_null_ptr!(arr); let channel_count_to_map = |count| match count { - 0 => vst3_sys::vst::kEmpty, - 1 => vst3_sys::vst::kMono, - 2 => vst3_sys::vst::kStereo, - 5 => vst3_sys::vst::k50, - 6 => vst3_sys::vst::k51, - 7 => vst3_sys::vst::k70Cine, - 8 => vst3_sys::vst::k71Cine, + 0 => vst3::Steinberg::Vst::SpeakerArr::kEmpty, + 1 => vst3::Steinberg::Vst::SpeakerArr::kMono, + 2 => vst3::Steinberg::Vst::SpeakerArr::kStereo, + 5 => vst3::Steinberg::Vst::SpeakerArr::k50, + 6 => vst3::Steinberg::Vst::SpeakerArr::k51, + 7 => vst3::Steinberg::Vst::SpeakerArr::k70Cine, + 8 => vst3::Steinberg::Vst::SpeakerArr::k71Cine, n => { nih_debug_assert_failure!( "No defined layout for {} channels, making something up on the spot...", @@ -815,7 +825,7 @@ impl IAudioProcessor for Wrapper

{ }; let current_audio_io_layout = self.inner.current_audio_io_layout.load(); - let num_channels = if dir == vst3_sys::vst::BusDirections::kInput as i32 { + let num_channels = if dir == vst3::Steinberg::Vst::BusDirections_::kInput as i32 { let has_main_input = current_audio_io_layout.main_input_channels.is_some(); let aux_input_start_idx = if has_main_input { 1 } else { 0 }; let aux_input_idx = (index - aux_input_start_idx).max(0) as usize; @@ -826,7 +836,7 @@ impl IAudioProcessor for Wrapper

{ } else { return kInvalidArgument; } - } else if dir == vst3_sys::vst::BusDirections::kOutput as i32 { + } else if dir == vst3::Steinberg::Vst::BusDirections_::kOutput as i32 { let has_main_output = current_audio_io_layout.main_output_channels.is_some(); let aux_output_start_idx = if has_main_output { 1 } else { 0 }; let aux_output_idx = (index - aux_output_start_idx).max(0) as usize; @@ -848,40 +858,40 @@ impl IAudioProcessor for Wrapper

{ kResultOk } - unsafe fn can_process_sample_size(&self, symbolic_sample_size: i32) -> tresult { - if symbolic_sample_size == vst3_sys::vst::SymbolicSampleSizes::kSample32 as i32 { + unsafe fn canProcessSampleSize(&self, symbolic_sample_size: int32) -> tresult { + if symbolic_sample_size == vst3::Steinberg::Vst::SymbolicSampleSizes_::kSample32 as i32 { kResultOk } else { kResultFalse } } - unsafe fn get_latency_samples(&self) -> u32 { + unsafe fn getLatencySamples(&self) -> uint32 { self.inner.current_latency.load(Ordering::SeqCst) } - unsafe fn setup_processing(&self, setup: *const vst3_sys::vst::ProcessSetup) -> tresult { + unsafe fn setupProcessing(&self, setup: *mut ProcessSetup) -> tresult { check_null_ptr!(setup); // There's no special handling for offline processing at the moment let setup = &*setup; nih_debug_assert_eq!( - setup.symbolic_sample_size, - vst3_sys::vst::SymbolicSampleSizes::kSample32 as i32 + setup.symbolicSampleSize, + vst3::Steinberg::Vst::SymbolicSampleSizes_::kSample32 as i32 ); // This is needed when activating the plugin and when restoring state self.inner.current_buffer_config.store(Some(BufferConfig { - sample_rate: setup.sample_rate as f32, + sample_rate: setup.sampleRate as f32, min_buffer_size: None, - max_buffer_size: setup.max_samples_per_block as u32, + max_buffer_size: setup.maxSamplesPerBlock as u32, process_mode: self.inner.current_process_mode.load(), })); - let mode = match setup.process_mode { - n if n == ProcessModes::kRealtime as i32 => ProcessMode::Realtime, - n if n == ProcessModes::kPrefetch as i32 => ProcessMode::Buffered, - n if n == ProcessModes::kOffline as i32 => ProcessMode::Offline, + let mode = match setup.processMode { + n if n == ProcessModes_::kRealtime as i32 => ProcessMode::Realtime, + n if n == ProcessModes_::kPrefetch as i32 => ProcessMode::Buffered, + n if n == ProcessModes_::kOffline as i32 => ProcessMode::Offline, n => { nih_debug_assert_failure!("Unknown rendering mode '{}', defaulting to realtime", n); ProcessMode::Realtime @@ -895,7 +905,7 @@ impl IAudioProcessor for Wrapper

{ kResultOk } - unsafe fn set_processing(&self, state: TBool) -> tresult { + unsafe fn setProcessing(&self, state: TBool) -> tresult { let state = state != 0; // Always reset the processing status when the plugin gets activated or deactivated @@ -930,7 +940,7 @@ impl IAudioProcessor for Wrapper

{ // Clippy doesn't understand our `event_start_idx` #[allow(clippy::mut_range_bound)] - unsafe fn process(&self, data: *mut vst3_sys::vst::ProcessData) -> tresult { + unsafe fn process(&self, data: *mut ProcessData) -> tresult { check_null_ptr!(data); // Panic on allocations if the `assert_process_allocs` feature has been enabled, and make @@ -945,14 +955,14 @@ impl IAudioProcessor for Wrapper

{ .expect("Process call without prior setup call") .sample_rate; - nih_debug_assert!(data.num_inputs >= 0 && data.num_outputs >= 0); + nih_debug_assert!(data.numInputs >= 0 && data.numOutputs >= 0); nih_debug_assert_eq!( - data.symbolic_sample_size, - vst3_sys::vst::SymbolicSampleSizes::kSample32 as i32 + data.symbolicSampleSize, + vst3::Steinberg::Vst::SymbolicSampleSizes_::kSample32 as i32 ); - nih_debug_assert!(data.num_samples >= 0); + nih_debug_assert!(data.numSamples >= 0); - let total_buffer_len = data.num_samples as usize; + let total_buffer_len = data.numSamples as usize; let current_audio_io_layout = self.inner.current_audio_io_layout.load(); let has_main_input = current_audio_io_layout.main_input_channels.is_some(); @@ -966,7 +976,7 @@ impl IAudioProcessor for Wrapper

{ // and instead only set the number of channels to 0. In that case the // 'buffer_is_valid' check from below should still prevent audio processing. let mut is_param_flush = total_buffer_len == 0; - if (data.num_outputs == 0 || data.outputs.is_null()) + if (data.numOutputs == 0 || data.outputs.is_null()) && (has_main_output || !current_audio_io_layout.aux_output_ports.is_empty()) { is_param_flush = true; @@ -983,14 +993,14 @@ impl IAudioProcessor for Wrapper

{ // First we'll go through the parameter changes. This may also include MIDI CC messages // if the plugin supports those - if let Some(param_changes) = data.input_param_changes.upgrade() { - let num_param_queues = param_changes.get_parameter_count(); + if let Some(param_changes) = ComRef::from_raw(data.inputParameterChanges) { + let num_param_queues = param_changes.getParameterCount(); for change_queue_idx in 0..num_param_queues { if let Some(param_change_queue) = - param_changes.get_parameter_data(change_queue_idx).upgrade() + ComRef::from_raw(param_changes.getParameterData(change_queue_idx)) { - let param_hash = param_change_queue.get_parameter_id(); - let num_changes = param_change_queue.get_point_count(); + let param_hash = param_change_queue.getParameterId(); + let num_changes = param_change_queue.getPointCount(); if num_changes <= 0 { continue; } @@ -998,7 +1008,7 @@ impl IAudioProcessor for Wrapper

{ let mut sample_offset = 0i32; let mut value = 0.0f64; for change_idx in 0..num_changes { - if param_change_queue.get_point( + if param_change_queue.getPoint( change_idx, &mut sample_offset, &mut value, @@ -1068,22 +1078,22 @@ impl IAudioProcessor for Wrapper

{ if P::MIDI_INPUT >= MidiConfig::Basic { let mut note_expression_controller = self.inner.note_expression_controller.borrow_mut(); - if let Some(events) = data.input_events.upgrade() { - let num_events = events.get_event_count(); + if let Some(events) = ComRef::from_raw(data.inputEvents) { + let num_events = events.getEventCount(); let mut event: MaybeUninit<_> = MaybeUninit::uninit(); for i in 0..num_events { - let result = events.get_event(i, event.as_mut_ptr()); + let result = events.getEvent(i, event.as_mut_ptr()); nih_debug_assert_eq!(result, kResultOk); let event = event.assume_init(); let timing = clamp_input_event_timing( - event.sample_offset as u32, + event.sampleOffset as u32, total_buffer_len as u32, ); - if event.type_ == EventTypes::kNoteOnEvent as u16 { - let event = event.event.note_on; + if event.r#type == EventTypes_::kNoteOnEvent as u16 { + let event = event.__field0.noteOn; // We need to keep track of note IDs to be able to handle not // expression value events @@ -1091,8 +1101,8 @@ impl IAudioProcessor for Wrapper

{ process_events.push(ProcessEvent::NoteEvent(NoteEvent::NoteOn { timing, - voice_id: if event.note_id != -1 { - Some(event.note_id) + voice_id: if event.noteId != -1 { + Some(event.noteId) } else { None }, @@ -1100,12 +1110,12 @@ impl IAudioProcessor for Wrapper

{ note: event.pitch as u8, velocity: event.velocity, })); - } else if event.type_ == EventTypes::kNoteOffEvent as u16 { - let event = event.event.note_off; + } else if event.r#type == EventTypes_::kNoteOffEvent as u16 { + let event = event.__field0.noteOff; process_events.push(ProcessEvent::NoteEvent(NoteEvent::NoteOff { timing, - voice_id: if event.note_id != -1 { - Some(event.note_id) + voice_id: if event.noteId != -1 { + Some(event.noteId) } else { None }, @@ -1113,12 +1123,12 @@ impl IAudioProcessor for Wrapper

{ note: event.pitch as u8, velocity: event.velocity, })); - } else if event.type_ == EventTypes::kPolyPressureEvent as u16 { - let event = event.event.poly_pressure; + } else if event.r#type == EventTypes_::kPolyPressureEvent as u16 { + let event = event.__field0.polyPressure; process_events.push(ProcessEvent::NoteEvent(NoteEvent::PolyPressure { timing, - voice_id: if event.note_id != -1 { - Some(event.note_id) + voice_id: if event.noteId != -1 { + Some(event.noteId) } else { None }, @@ -1126,22 +1136,22 @@ impl IAudioProcessor for Wrapper

{ note: event.pitch as u8, pressure: event.pressure, })); - } else if event.type_ == EventTypes::kNoteExpressionValueEvent as u16 { - let event = event.event.note_expression_value; + } else if event.r#type == EventTypes_::kNoteExpressionValueEvent as u16 { + let event = event.__field0.noteExpressionValue; match note_expression_controller.translate_event(timing, &event) { Some(translated_event) => { process_events.push(ProcessEvent::NoteEvent(translated_event)) } None => nih_debug_assert_failure!( "Unhandled note expression type: {}", - event.type_id + event.typeId ), } - } else if event.type_ == EventTypes::kDataEvent as u16 - && event.event.data.type_ == 0 + } else if event.r#type == EventTypes_::kDataEvent as u16 + && event.__field0.data.r#type == 0 { // 0 = kMidiSysEx - let event = event.event.data; + let event = event.__field0.data; // `NoteEvent::from_midi` prints some tracing if parsing fails, which is // not necessarily an error @@ -1231,29 +1241,33 @@ impl IAudioProcessor for Wrapper

{ let mut buffer_manager = self.inner.buffer_manager.borrow_mut(); let buffers = buffer_manager.create_buffers(block_start, block_len, |buffer_source| { - if data.num_outputs > 0 + if data.numOutputs > 0 && !data.outputs.is_null() - && !(*data.outputs).buffers.is_null() + && !(*data.outputs).__field0.channelBuffers32.is_null() && has_main_output { let audio_output = &*data.outputs; - let ptrs = - NonNull::new(audio_output.buffers as *mut *mut f32).unwrap(); - let num_channels = audio_output.num_channels as usize; + let ptrs = NonNull::new( + audio_output.__field0.channelBuffers32 as *mut *mut f32, + ) + .unwrap(); + let num_channels = audio_output.numChannels as usize; *buffer_source.main_output_channel_pointers = Some(ChannelPointers { ptrs, num_channels }); } - if data.num_inputs > 0 + if data.numInputs > 0 && !data.inputs.is_null() - && !(*data.inputs).buffers.is_null() + && !(*data.inputs).__field0.channelBuffers32.is_null() && has_main_input { let audio_input = &*data.inputs; - let ptrs = - NonNull::new(audio_input.buffers as *mut *mut f32).unwrap(); - let num_channels = audio_input.num_channels as usize; + let ptrs = NonNull::new( + audio_input.__field0.channelBuffers32 as *mut *mut f32, + ) + .unwrap(); + let num_channels = audio_input.numChannels as usize; *buffer_source.main_input_channel_pointers = Some(ChannelPointers { ptrs, num_channels }); @@ -1266,14 +1280,16 @@ impl IAudioProcessor for Wrapper

{ .enumerate() { let aux_input_idx = aux_input_no + aux_input_start_idx; - if aux_input_idx > data.num_outputs as usize { + if aux_input_idx > data.numOutputs as usize { break; } let audio_input = &*data.inputs.add(aux_input_idx); - match NonNull::new(audio_input.buffers as *mut *mut f32) { + match NonNull::new( + audio_input.__field0.channelBuffers32 as *mut *mut f32, + ) { Some(ptrs) => { - let num_channels = audio_input.num_channels as usize; + let num_channels = audio_input.numChannels as usize; *aux_input_channel_pointers = Some(ChannelPointers { ptrs, num_channels }); @@ -1290,14 +1306,16 @@ impl IAudioProcessor for Wrapper

{ .enumerate() { let aux_output_idx = aux_output_no + aux_output_start_idx; - if aux_output_idx > data.num_outputs as usize { + if aux_output_idx > data.numOutputs as usize { break; } let audio_output = &*data.outputs.add(aux_output_idx); - match NonNull::new(audio_output.buffers as *mut *mut f32) { + match NonNull::new( + audio_output.__field0.channelBuffers32 as *mut *mut f32, + ) { Some(ptrs) => { - let num_channels = audio_output.num_channels as usize; + let num_channels = audio_output.numChannels as usize; *aux_output_channel_pointers = Some(ChannelPointers { ptrs, num_channels }); @@ -1332,60 +1350,54 @@ impl IAudioProcessor for Wrapper

{ // information, but the methods on [`Transport`] can reconstruct these values // from the other fields let mut transport = Transport::new(sample_rate); - if !data.context.is_null() { - let context = &*data.context; - - // These constants are missing from vst3-sys, see: - // https://steinbergmedia.github.io/vst3_doc/vstinterfaces/structSteinberg_1_1Vst_1_1ProcessContext.html - transport.playing = context.state & (1 << 1) != 0; // kPlaying - transport.recording = context.state & (1 << 3) != 0; // kRecording - if context.state & (1 << 10) != 0 { - // kTempoValid + if !data.processContext.is_null() { + let context = &*data.processContext; + + transport.playing = context.state & kPlaying as u32 != 0; + transport.recording = context.state & kRecording as u32 != 0; + if context.state & kTempoValid as u32 != 0 { transport.tempo = Some(context.tempo); } - if context.state & (1 << 13) != 0 { - // kTimeSigValid - transport.time_sig_numerator = Some(context.time_sig_num); - transport.time_sig_denominator = Some(context.time_sig_den); + if context.state & kTimeSigValid as u32 != 0 { + transport.time_sig_numerator = Some(context.timeSigNumerator); + transport.time_sig_denominator = Some(context.timeSigDenominator); } // We need to compensate for the block splitting here transport.pos_samples = - Some(context.project_time_samples + block_start as i64); - if context.state & (1 << 9) != 0 { - // kProjectTimeMusicValid + Some(context.projectTimeSamples + block_start as i64); + if context.state & kProjectTimeMusicValid as u32 != 0 { if P::SAMPLE_ACCURATE_AUTOMATION && block_start > 0 - && (context.state & (1 << 10) != 0) + && (context.state & kTempoValid as u32 != 0) { - // kTempoValid transport.pos_beats = Some( - context.project_time_music + context.projectTimeMusic + (block_start as f64 / sample_rate as f64 / 60.0 * context.tempo), ); } else { - transport.pos_beats = Some(context.project_time_music); + transport.pos_beats = Some(context.projectTimeMusic); } } - if context.state & (1 << 11) != 0 { - // kBarPositionValid + if context.state & kBarPositionValid as u32 != 0 { if P::SAMPLE_ACCURATE_AUTOMATION && block_start > 0 { // The transport object knows how to recompute this from the other information transport.bar_start_pos_beats = match transport.bar_start_pos_beats() { Some(updated) => Some(updated), - None => Some(context.bar_position_music), + None => Some(context.barPositionMusic), }; } else { - transport.bar_start_pos_beats = Some(context.bar_position_music); + transport.bar_start_pos_beats = Some(context.barPositionMusic); } } - if context.state & (1 << 2) != 0 && context.state & (1 << 12) != 0 { - // kCycleActive && kCycleValid + if context.state & kCycleActive as u32 != 0 + && context.state & kCycleValid as u32 != 0 + { transport.loop_range_beats = - Some((context.cycle_start_music, context.cycle_end_music)); + Some((context.cycleStartMusic, context.cycleEndMusic)); } } @@ -1416,15 +1428,15 @@ impl IAudioProcessor for Wrapper

{ }; // Send any events output by the plugin during the process cycle - if let Some(events) = data.output_events.upgrade() { + if let Some(events) = ComRef::from_raw(data.outputEvents) { let mut output_events = self.inner.output_events.borrow_mut(); while let Some(event) = output_events.pop_front() { // We'll set the correct variant on this struct, or skip to the next loop // iteration if we don't handle the event type let mut vst3_event: Event = mem::zeroed(); - vst3_event.bus_index = 0; + vst3_event.busIndex = 0; // There's also a ppqPos field, but uh how about no - vst3_event.sample_offset = clamp_output_event_timing( + vst3_event.sampleOffset = clamp_output_event_timing( event.timing() + block_start as u32, total_buffer_len as u32, ) as i32; @@ -1440,8 +1452,8 @@ impl IAudioProcessor for Wrapper

{ note, velocity, } if P::MIDI_OUTPUT >= MidiConfig::Basic => { - vst3_event.type_ = EventTypes::kNoteOnEvent as u16; - vst3_event.event.note_on = NoteOnEvent { + vst3_event.r#type = EventTypes_::kNoteOnEvent as u16; + vst3_event.__field0.noteOn = NoteOnEvent { channel: channel as i16, pitch: note as i16, tuning: 0.0, @@ -1449,7 +1461,7 @@ impl IAudioProcessor for Wrapper

{ length: 0, // What? // We'll use this for our note IDs, that way we don't have to do // anything complicated here - note_id: voice_id + noteId: voice_id .unwrap_or_else(|| ((channel as i32) << 8) | note as i32), }; } @@ -1460,12 +1472,12 @@ impl IAudioProcessor for Wrapper

{ note, velocity, } if P::MIDI_OUTPUT >= MidiConfig::Basic => { - vst3_event.type_ = EventTypes::kNoteOffEvent as u16; - vst3_event.event.note_off = NoteOffEvent { + vst3_event.r#type = EventTypes_::kNoteOffEvent as u16; + vst3_event.__field0.noteOff = NoteOffEvent { channel: channel as i16, pitch: note as i16, velocity, - note_id: voice_id + noteId: voice_id .unwrap_or_else(|| ((channel as i32) << 8) | note as i32), tuning: 0.0, }; @@ -1485,11 +1497,11 @@ impl IAudioProcessor for Wrapper

{ note, pressure, } if P::MIDI_OUTPUT >= MidiConfig::Basic => { - vst3_event.type_ = EventTypes::kPolyPressureEvent as u16; - vst3_event.event.poly_pressure = PolyPressureEvent { + vst3_event.r#type = EventTypes_::kPolyPressureEvent as u16; + vst3_event.__field0.polyPressure = PolyPressureEvent { channel: channel as i16, pitch: note as i16, - note_id: voice_id + noteId: voice_id .unwrap_or_else(|| ((channel as i32) << 8) | note as i32), pressure, }; @@ -1536,9 +1548,9 @@ impl IAudioProcessor for Wrapper

{ event, ) { Some(translated_event) => { - vst3_event.type_ = - EventTypes::kNoteExpressionValueEvent as u16; - vst3_event.event.note_expression_value = translated_event; + vst3_event.r#type = + EventTypes_::kNoteExpressionValueEvent as u16; + vst3_event.__field0.noteExpressionValue = translated_event; } None => { nih_debug_assert_failure!( @@ -1552,9 +1564,9 @@ impl IAudioProcessor for Wrapper

{ channel, pressure, } if P::MIDI_OUTPUT >= MidiConfig::MidiCCs => { - vst3_event.type_ = EventTypes::kLegacyMIDICCOutEvent as u16; - vst3_event.event.legacy_midi_cc_out = LegacyMidiCCOutEvent { - control_number: 128, // kAfterTouch + vst3_event.r#type = EventTypes_::kLegacyMIDICCOutEvent as u16; + vst3_event.__field0.midiCCOut = LegacyMIDICCOutEvent { + controlNumber: 128, // kAfterTouch channel: channel as i8, value: (pressure * 127.0).round() as i8, value2: 0, @@ -1567,9 +1579,9 @@ impl IAudioProcessor for Wrapper

{ } if P::MIDI_OUTPUT >= MidiConfig::MidiCCs => { let scaled = (value * ((1 << 14) - 1) as f32).round() as i32; - vst3_event.type_ = EventTypes::kLegacyMIDICCOutEvent as u16; - vst3_event.event.legacy_midi_cc_out = LegacyMidiCCOutEvent { - control_number: 129, // kPitchBend + vst3_event.r#type = EventTypes_::kLegacyMIDICCOutEvent as u16; + vst3_event.__field0.midiCCOut = LegacyMIDICCOutEvent { + controlNumber: 129, // kPitchBend channel: channel as i8, value: (scaled & 0b01111111) as i8, value2: ((scaled >> 7) & 0b01111111) as i8, @@ -1581,9 +1593,9 @@ impl IAudioProcessor for Wrapper

{ cc, value, } if P::MIDI_OUTPUT >= MidiConfig::MidiCCs => { - vst3_event.type_ = EventTypes::kLegacyMIDICCOutEvent as u16; - vst3_event.event.legacy_midi_cc_out = LegacyMidiCCOutEvent { - control_number: cc, + vst3_event.r#type = EventTypes_::kLegacyMIDICCOutEvent as u16; + vst3_event.__field0.midiCCOut = LegacyMIDICCOutEvent { + controlNumber: cc, channel: channel as i8, value: (value * 127.0).round() as i8, value2: 0, @@ -1594,9 +1606,9 @@ impl IAudioProcessor for Wrapper

{ channel, program, } if P::MIDI_OUTPUT >= MidiConfig::MidiCCs => { - vst3_event.type_ = EventTypes::kLegacyMIDICCOutEvent as u16; - vst3_event.event.legacy_midi_cc_out = LegacyMidiCCOutEvent { - control_number: 130, // kCtrlProgramChange + vst3_event.r#type = EventTypes_::kLegacyMIDICCOutEvent as u16; + vst3_event.__field0.midiCCOut = LegacyMIDICCOutEvent { + controlNumber: 130, // kCtrlProgramChange channel: channel as i8, value: program as i8, value2: 0, @@ -1610,16 +1622,16 @@ impl IAudioProcessor for Wrapper

{ nih_debug_assert!(padded_sysex_buffer.len() >= length); let sysex_buffer = &padded_sysex_buffer[..length]; - vst3_event.type_ = EventTypes::kDataEvent as u16; - vst3_event.event.data = DataEvent { + vst3_event.r#type = EventTypes_::kDataEvent as u16; + vst3_event.__field0.data = DataEvent { size: sysex_buffer.len() as u32, - type_: 0, // kMidiSysEx + r#type: 0, // kMidiSysEx bytes: sysex_buffer.as_ptr(), }; // NOTE: We need to have this call here while `sysex_buffer` is // still in scope since the event contains pointers to it - let result = events.add_event(&mut vst3_event); + let result = events.addEvent(&mut vst3_event); nih_debug_assert_eq!(result, kResultOk); continue; } @@ -1631,7 +1643,7 @@ impl IAudioProcessor for Wrapper

{ } }; - let result = events.add_event(&mut vst3_event); + let result = events.addEvent(&mut vst3_event); nih_debug_assert_eq!(result, kResultOk); } } @@ -1670,7 +1682,7 @@ impl IAudioProcessor for Wrapper

{ }) } - unsafe fn get_tail_samples(&self) -> u32 { + unsafe fn getTailSamples(&self) -> uint32 { // https://github.com/steinbergmedia/vst3_pluginterfaces/blob/2ad397ade5b51007860bedb3b01b8afd2c5f6fba/vst/ivstaudioprocessor.h#L145-L159 match self.inner.last_process_status.load() { ProcessStatus::Tail(samples) => samples, @@ -1680,13 +1692,13 @@ impl IAudioProcessor for Wrapper

{ } } -impl IMidiMapping for Wrapper

{ - unsafe fn get_midi_controller_assignment( +impl IMidiMappingTrait for Wrapper

{ + unsafe fn getMidiControllerAssignment( &self, - bus_index: i32, - channel: i16, - midi_cc_number: vst3_com::vst::CtrlNumber, - param_id: *mut vst3_com::vst::ParamID, + bus_index: int32, + channel: int16, + midi_cc_number: CtrlNumber, + param_id: *mut ParamID, ) -> tresult { if P::MIDI_INPUT < MidiConfig::MidiCCs || bus_index != 0 @@ -1707,8 +1719,8 @@ impl IMidiMapping for Wrapper

{ } } -impl INoteExpressionController for Wrapper

{ - unsafe fn get_note_expression_count(&self, bus_idx: i32, _channel: i16) -> i32 { +impl INoteExpressionControllerTrait for Wrapper

{ + unsafe fn getNoteExpressionCount(&self, bus_idx: int32, _channel: int16) -> int32 { // Apparently you need to define the predefined note expressions. Thanks VST3. if P::MIDI_INPUT >= MidiConfig::Basic && bus_idx == 0 { note_expressions::KNOWN_NOTE_EXPRESSIONS.len() as i32 @@ -1717,11 +1729,11 @@ impl INoteExpressionController for Wrapper

{ } } - unsafe fn get_note_expression_info( + unsafe fn getNoteExpressionInfo( &self, - bus_idx: i32, - _channel: i16, - note_expression_idx: i32, + bus_idx: int32, + _channel: int16, + note_expression_idx: int32, info: *mut NoteExpressionTypeInfo, ) -> tresult { if P::MIDI_INPUT < MidiConfig::Basic @@ -1739,65 +1751,65 @@ impl INoteExpressionController for Wrapper

{ let info = &mut *info; let note_expression_info = ¬e_expressions::KNOWN_NOTE_EXPRESSIONS[note_expression_idx as usize]; - info.type_id = note_expression_info.type_id; + info.typeId = note_expression_info.type_id; u16strlcpy(&mut info.title, note_expression_info.title); - u16strlcpy(&mut info.short_title, note_expression_info.title); + u16strlcpy(&mut info.shortTitle, note_expression_info.title); u16strlcpy(&mut info.units, note_expression_info.unit); - info.unit_id = kNoParentUnitId; + info.unitId = kNoParentUnitId; // This should not be needed since they're predefined, but then again you'd think you also // wouldn't need to define predefined note expressions now do you? - info.value_desc = NoteExpressionValueDescription { - default_value: 0.5, - min: 0.0, - max: 1.0, - step_count: 0, + info.valueDesc = NoteExpressionValueDescription { + defaultValue: 0.5, + minimum: 0.0, + maximum: 1.0, + stepCount: 0, }; - info.id = kNoParamId; + info.associatedParameterId = kNoParamId; info.flags = 1 << 2; // kIsAbsolute kResultOk } - unsafe fn get_note_expression_string_by_value( + unsafe fn getNoteExpressionStringByValue( &self, - _bus_idx: i32, - _channel: i16, - _id: u32, - _value: f64, - _string: *mut TChar, + _bus_idx: int32, + _channel: int16, + _id: NoteExpressionTypeID, + _value: NoteExpressionValue, + _string: *mut String128, ) -> tresult { kResultFalse } - unsafe fn get_note_expression_value_by_string( + unsafe fn getNoteExpressionValueByString( &self, - _bus_idx: i32, - _channel: i16, - _id: u32, + _bus_idx: int32, + _channel: int16, + _id: NoteExpressionTypeID, _string: *const TChar, - _value: *mut f64, + _value: *mut NoteExpressionValue, ) -> tresult { kResultFalse } } -impl IProcessContextRequirements for Wrapper

{ - unsafe fn get_process_context_requirements(&self) -> u32 { - IProcessContextRequirementsFlags::kNeedProjectTimeMusic - | IProcessContextRequirementsFlags::kNeedBarPositionMusic - | IProcessContextRequirementsFlags::kNeedCycleMusic - | IProcessContextRequirementsFlags::kNeedTimeSignature - | IProcessContextRequirementsFlags::kNeedTempo - | IProcessContextRequirementsFlags::kNeedTransportState +impl IProcessContextRequirementsTrait for Wrapper

{ + unsafe fn getProcessContextRequirements(&self) -> uint32 { + (IProcessContextRequirements_::Flags_::kNeedProjectTimeMusic + | IProcessContextRequirements_::Flags_::kNeedBarPositionMusic + | IProcessContextRequirements_::Flags_::kNeedCycleMusic + | IProcessContextRequirements_::Flags_::kNeedTimeSignature + | IProcessContextRequirements_::Flags_::kNeedTempo + | IProcessContextRequirements_::Flags_::kNeedTransportState) as u32 } } -impl IUnitInfo for Wrapper

{ - unsafe fn get_unit_count(&self) -> i32 { +impl IUnitInfoTrait for Wrapper

{ + unsafe fn getUnitCount(&self) -> int32 { self.inner.param_units.len() as i32 } - unsafe fn get_unit_info(&self, unit_index: i32, info: *mut UnitInfo) -> tresult { + unsafe fn getUnitInfo(&self, unit_index: int32, info: *mut UnitInfo) -> tresult { check_null_ptr!(info); match self.inner.param_units.info(unit_index as usize) { @@ -1806,9 +1818,9 @@ impl IUnitInfo for Wrapper

{ let info = &mut *info; info.id = unit_id; - info.parent_unit_id = unit_info.parent_id; + info.parentUnitId = unit_info.parent_id; u16strlcpy(&mut info.name, &unit_info.name); - info.program_list_id = kNoProgramListId; + info.programListId = kNoProgramListId; kResultOk } @@ -1816,79 +1828,79 @@ impl IUnitInfo for Wrapper

{ } } - unsafe fn get_program_list_count(&self) -> i32 { + unsafe fn getProgramListCount(&self) -> int32 { // TODO: Do we want program lists? Probably not, CLAP doesn't even support them. 0 } - unsafe fn get_program_list_info( + unsafe fn getProgramListInfo( &self, - _list_index: i32, + _list_index: int32, _info: *mut ProgramListInfo, ) -> tresult { kInvalidArgument } - unsafe fn get_program_name( + unsafe fn getProgramName( &self, - _list_id: i32, - _program_index: i32, - _name: *mut u16, + _list_id: ProgramListID, + _program_index: int32, + _name: *mut String128, ) -> tresult { kInvalidArgument } - unsafe fn get_program_info( + unsafe fn getProgramInfo( &self, - _list_id: i32, - _program_index: i32, - _attribute_id: *const u8, - _attribute_value: *mut u16, + _list_id: ProgramListID, + _program_index: int32, + _attribute_id: CString, + _attribute_value: *mut String128, ) -> tresult { kInvalidArgument } - unsafe fn has_program_pitch_names(&self, _id: i32, _index: i32) -> tresult { + unsafe fn hasProgramPitchNames(&self, _id: ProgramListID, _index: int32) -> tresult { // TODO: Support note names once someone requests it kInvalidArgument } - unsafe fn get_program_pitch_name( + unsafe fn getProgramPitchName( &self, - _id: i32, - _index: i32, - _pitch: i16, - _name: *mut u16, + _id: ProgramListID, + _index: int32, + _pitch: int16, + _name: *mut String128, ) -> tresult { kInvalidArgument } - unsafe fn get_selected_unit(&self) -> i32 { + unsafe fn getSelectedUnit(&self) -> UnitID { // No! Steinberg! I don't want any of this! I just want to group parameters! kRootUnitId } - unsafe fn select_unit(&self, _id: i32) -> tresult { + unsafe fn selectUnit(&self, _id: UnitID) -> tresult { kResultFalse } - unsafe fn get_unit_by_bus( + unsafe fn getUnitByBus( &self, - _type_: i32, - _dir: i32, - _bus_index: i32, - _channel: i32, - _unit_id: *mut i32, + _type_: MediaType, + _dir: BusDirection, + _bus_index: int32, + _channel: int32, + _unit_id: *mut UnitID, ) -> tresult { // Stahp it! kResultFalse } - unsafe fn set_unit_program_data( + unsafe fn setUnitProgramData( &self, - _list_or_unit: i32, - _program_idx: i32, - _data: SharedVstPtr, + _list_or_unit: int32, + _program_idx: int32, + _data: *mut IBStream, ) -> tresult { kInvalidArgument }