From 35cf7ffc1d3878a8d2b18ff5471284d19a62f00b Mon Sep 17 00:00:00 2001 From: Robbert van der Helm <mail@robbertvanderhelm.nl> Date: Wed, 26 Jan 2022 21:12:13 +0100 Subject: [PATCH] Implement IComponent --- src/wrapper/vst3.rs | 155 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 147 insertions(+), 8 deletions(-) diff --git a/src/wrapper/vst3.rs b/src/wrapper/vst3.rs index 35f9e5bd..8490fffe 100644 --- a/src/wrapper/vst3.rs +++ b/src/wrapper/vst3.rs @@ -14,25 +14,164 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. +use std::ffi::c_void; use std::marker::PhantomData; use std::mem; -use std::pin::Pin; - -use vst3_com::base::{kInvalidArgument, kResultFalse, kResultOk}; // Alias needed for the VST3 attribute macro use vst3_sys as vst3_com; -use vst3_sys::base::tresult; -use vst3_sys::base::{IPluginFactory, IPluginFactory2, IPluginFactory3}; +use vst3_sys::base::{kInvalidArgument, kNoInterface, kResultFalse, kResultOk, tresult, TBool}; +use vst3_sys::base::{IPluginBase, IPluginFactory, IPluginFactory2, IPluginFactory3}; +use vst3_sys::vst::IComponent; use vst3_sys::VST3; -use crate::plugin::Plugin; -use crate::wrapper::util::strlcpy; +use crate::plugin::{BusConfig, Plugin}; +use crate::wrapper::util::{strlcpy, u16strlcpy}; /// Re-export for the wrapper. pub use vst3_sys::sys::GUID; +#[VST3(implements(IComponent))] pub struct Wrapper<P: Plugin> { - plugin: Pin<Box<P>>, + plugin: P, + current_bus_config: BusConfig, +} + +impl<P: Plugin> Wrapper<P> { + pub fn new() -> Box<Self> { + Self::allocate( + P::default(), + // Some hosts, like the current version of Bitwig and Ardour at the time of writing, + // will try using the plugin's default not yet initialized bus arrangement. Because of + // that, we'll always initialize this configuration even before the host requests a + // channel layout. + BusConfig { + num_input_channels: P::DEFAULT_NUM_INPUTS, + num_output_channels: P::DEFAULT_NUM_OUTPUTS, + }, + ) + } +} + +impl<P: Plugin> IPluginBase for Wrapper<P> { + unsafe fn initialize(&self, _context: *mut c_void) -> tresult { + // We currently don't need or allow any initialization logic + kResultOk + } + + unsafe fn terminate(&self) -> tresult { + kResultOk + } +} + +impl<P: Plugin> IComponent for Wrapper<P> { + unsafe fn get_controller_class_id(&self, _tuid: *mut vst3_sys::IID) -> tresult { + // We won't separate the edit controller to keep the implemetnation a bit smaller + kNoInterface + } + + unsafe fn set_io_mode(&self, _mode: vst3_sys::vst::IoMode) -> tresult { + // This would need to integrate with the GUI, which we currently don't have + kResultOk + } + + unsafe fn get_bus_count( + &self, + type_: vst3_sys::vst::MediaType, + _dir: vst3_sys::vst::BusDirection, + ) -> i32 { + // All plugins currently only have a single input and a single output bus + match type_ { + x if x == vst3_com::vst::MediaTypes::kAudio as i32 => 1, + _ => 0, + } + } + + unsafe fn get_bus_info( + &self, + type_: vst3_sys::vst::MediaType, + dir: vst3_sys::vst::BusDirection, + index: i32, + info: *mut vst3_sys::vst::BusInfo, + ) -> tresult { + match type_ { + t if t == vst3_com::vst::MediaTypes::kAudio as i32 => { + *info = mem::zeroed(); + + let info = &mut *info; + info.media_type = vst3_com::vst::MediaTypes::kAudio as i32; + info.bus_type = vst3_com::vst::BusTypes::kMain as i32; + info.flags = vst3_com::vst::BusFlags::kDefaultActive as u32; + match (dir, index) { + (d, 0) if d == vst3_com::vst::BusDirections::kInput as i32 => { + info.direction = vst3_com::vst::BusDirections::kInput as i32; + info.channel_count = self.current_bus_config.num_input_channels as i32; + u16strlcpy(&mut info.name, "Input"); + + kResultOk + } + (d, 0) if d == vst3_com::vst::BusDirections::kOutput as i32 => { + info.direction = vst3_com::vst::BusDirections::kOutput as i32; + info.channel_count = self.current_bus_config.num_output_channels as i32; + u16strlcpy(&mut info.name, "Output"); + + kResultOk + } + _ => kInvalidArgument, + } + } + _ => kInvalidArgument, + } + } + + unsafe fn get_routing_info( + &self, + in_info: *mut vst3_sys::vst::RoutingInfo, + out_info: *mut vst3_sys::vst::RoutingInfo, + ) -> tresult { + *out_info = mem::zeroed(); + + let in_info = &*in_info; + let out_info = &mut *out_info; + match (in_info.media_type, in_info.bus_index) { + (t, 0) if t == vst3_sys::vst::MediaTypes::kAudio as i32 => { + out_info.media_type = vst3_sys::vst::MediaTypes::kAudio as i32; + out_info.bus_index = in_info.bus_index; + out_info.channel = in_info.channel; + + kResultOk + } + _ => kInvalidArgument, + } + } + + unsafe fn activate_bus( + &self, + type_: vst3_sys::vst::MediaType, + _dir: vst3_sys::vst::BusDirection, + index: i32, + _state: vst3_sys::base::TBool, + ) -> tresult { + // We don't need any special handling here + match (type_, index) { + (t, 0) if t == vst3_sys::vst::MediaTypes::kAudio as i32 => kResultOk, + _ => kInvalidArgument, + } + } + + unsafe fn set_active(&self, _state: TBool) -> tresult { + // We don't need any special handling here + kResultOk + } + + unsafe fn set_state(&self, _state: *mut c_void) -> tresult { + // TODO: Implemnt state saving and restoring + kResultFalse + } + + unsafe fn get_state(&self, _state: *mut c_void) -> tresult { + // TODO: Implemnt state saving and restoring + kResultFalse + } } // TODO: Implement the rest