diff --git a/.gitignore b/.gitignore
index 9b430e1..e7d9874 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ Cargo.lock
.idea
.vscode
*.iml
+validator
diff --git a/Cargo.toml b/Cargo.toml
index 42ce5de..675877e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,5 +11,15 @@ edition = "2018"
vst3-com = { path = "./com" }
bitflags = "1.2.0"
+[dev-dependencies]
+log = "0.4"
+simple_logger = "1.6.0"
+lazy_static = "1.4.0"
+widestring = "0.4.0"
+
[workspace]
-members = ["examples/passthru", "examples/again", "com", "com/macros", "com/macros/support"]
+members = ["examples/again", "com", "com/macros", "com/macros/support"]
+
+[[example]]
+name = "passthru"
+crate-type = ["cdylib"]
diff --git a/LICENSE.txt b/LICENSE.txt
deleted file mode 100644
index 51974c9..0000000
--- a/LICENSE.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-This crate is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see .
\ No newline at end of file
diff --git a/Makefile.toml b/Makefile.toml
index 4b0a354..3ddfbee 100644
--- a/Makefile.toml
+++ b/Makefile.toml
@@ -2,34 +2,47 @@
workspace = false
script = []
+[tasks.passthru.mac]
+script = [
+ "cargo build --example passthru",
+ "./osx-bundler.sh passthru target/debug/examples/libpassthru.dylib"
+]
+
[tasks.passthru.linux]
workspace = false
script = [
- "cargo build --package passthru",
- "mkdir -p target/debug/plugin.vst3/Contents/x86_64-linux",
- "mkdir -p target/debug/plugin.vst3/Contents/Resources",
- "cp target/debug/libpassthru.so target/debug/plugin.vst3/Contents/x86_64-linux/plugin.so"
+ "cargo build --example passthru",
+ "mkdir -p target/debug/passthru.vst3/Contents/x86_64-linux",
+ "mkdir -p target/debug/passthru.vst3/Contents/Resources",
+ "cp target/debug/examples/libpassthru.so target/debug/passthru.vst3/Contents/x86_64-linux/passthru.so"
]
[tasks.passthru.windows]
workspace = false
script_runner = "@shell"
script = [
- "cargo build --package passthru",
- "cp target/debug/passthru.dll target/debug/passthru.vst3"
+ "cargo build --example passthru",
+ "cp target/debug/examples/passthru.dll target/debug/passthru.vst3"
]
[tasks.again]
workspace = false
script = []
+[tasks.again.mac]
+workspace = false
+script = [
+ "cargo build --package again",
+ "./osx-bundler.sh again target/debug/libagain.dylib"
+]
+
[tasks.again.linux]
workspace = false
script = [
"cargo build --package again",
- "mkdir -p target/debug/plugin.vst3/Contents/x86_64-linux",
- "mkdir -p target/debug/plugin.vst3/Contents/Resources",
- "cp target/debug/libagain.so target/debug/plugin.vst3/Contents/x86_64-linux/plugin.so"
+ "mkdir -p target/debug/again.vst3/Contents/x86_64-linux",
+ "mkdir -p target/debug/again.vst3/Contents/Resources",
+ "cp target/debug/libagain.so target/debug/again.vst3/Contents/x86_64-linux/again.so"
]
[tasks.again.windows]
diff --git a/com/macros/support/src/co_class/class_factory.rs b/com/macros/support/src/co_class/class_factory.rs
index d080246..b98c57c 100644
--- a/com/macros/support/src/co_class/class_factory.rs
+++ b/com/macros/support/src/co_class/class_factory.rs
@@ -45,7 +45,7 @@ pub fn generate(struct_item: &ItemStruct) -> HelperTokenStream {
// Bringing trait into scope to access IUnknown methods.
use vst3_com::interfaces::iunknown::IUnknown;
- if aggr != std::ptr::null_mut() {
+ if !aggr.is_null() {
return vst3_com::sys::CLASS_E_NOAGGREGATION;
}
diff --git a/examples/again/src/lib.rs b/examples/again/src/lib.rs
index ab37194..decc9ca 100644
--- a/examples/again/src/lib.rs
+++ b/examples/again/src/lib.rs
@@ -1006,6 +1006,19 @@ pub extern "system" fn ModuleExit() -> bool {
true
}
+#[no_mangle]
+#[allow(non_snake_case)]
+pub extern "system" fn bundleEntry(_: *mut c_void) -> bool {
+ true
+}
+
+#[no_mangle]
+#[allow(non_snake_case)]
+pub extern "system" fn bundleExit() -> bool {
+ info!("Module exited");
+ true
+}
+
static mut INIT_LOGGER: bool = false;
#[no_mangle]
diff --git a/examples/passthru.rs b/examples/passthru.rs
new file mode 100644
index 0000000..b33880d
--- /dev/null
+++ b/examples/passthru.rs
@@ -0,0 +1,389 @@
+//! Author: Mike Hilgendorf
+//!
+//! Bare minimum plugin that copies input to output, doesn't
+//! save its own state, and doesn't have any parameters.
+#![allow(clippy::collapsible_if)]
+#![allow(clippy::missing_safety_doc)]
+use log::*;
+use std::{
+ os::raw::{c_char, c_short, c_void},
+ ptr::{copy_nonoverlapping, null_mut},
+};
+use vst3_com::{sys::GUID, IID};
+use vst3_sys::{
+ base::{
+ kInvalidArgument, kResultFalse, kResultOk, tresult, FIDString, IPluginBase, IPluginFactory,
+ TBool,
+ },
+ vst::{
+ AudioBusBuffers, BusDirection, BusDirections, BusFlags, BusInfo, IAudioPresentationLatency,
+ IAudioProcessor, IAutomationState, IComponent, IEditController, MediaTypes, ParameterInfo,
+ ProcessData, ProcessSetup, RoutingInfo, TChar,
+ },
+ VST3,
+};
+use widestring::U16CString;
+
+unsafe fn strcpy(src: &str, dst: *mut c_char) {
+ copy_nonoverlapping(src.as_ptr() as *const c_void as *const _, dst, src.len());
+}
+
+unsafe fn wstrcpy(src: &str, dst: *mut c_short) {
+ let src = U16CString::from_str(src).unwrap();
+ let mut src = src.into_vec();
+ src.push(0);
+ copy_nonoverlapping(src.as_ptr() as *const c_void as *const _, dst, src.len());
+}
+
+#[VST3(implements(
+ IComponent,
+ IPluginBase,
+ IEditController,
+ IAudioProcessor,
+ IAutomationState,
+ IAudioPresentationLatency
+))]
+pub struct PassthruPlugin {}
+pub struct PassthruController {}
+impl PassthruPlugin {
+ const CID: GUID = GUID {
+ data: [
+ 0x93, 0x68, 0x4f, 0x1a, 0x46, 0x11, 0x91, 0x01, 0x00, 0x00, 0xb4, 0x39, 0xe5, 0x64,
+ 0x8a, 0xda,
+ ],
+ };
+ pub fn new() -> Box {
+ PassthruPlugin::allocate()
+ }
+}
+
+#[VST3(implements(IPluginFactory))]
+pub struct Factory {}
+
+impl IEditController for PassthruPlugin {
+ unsafe fn set_component_state(&mut self, _state: *mut c_void) -> tresult {
+ info!("set_component_state");
+ kResultOk
+ }
+ unsafe fn set_state(&mut self, _state: *mut c_void) -> tresult {
+ info!("set_state");
+ kResultOk
+ }
+ unsafe fn get_state(&mut self, _state: *mut c_void) -> tresult {
+ info!("get_state");
+ kResultOk
+ }
+ unsafe fn get_parameter_count(&self) -> i32 {
+ info!("get_parameter_count");
+ 0
+ }
+ unsafe fn get_parameter_info(&self, _: i32, _: *mut ParameterInfo) -> tresult {
+ info!("get_parameter_info");
+ kResultFalse
+ }
+ unsafe fn get_param_string_by_value(
+ &self,
+ _id: u32,
+ _value_normalized: f64,
+ _string: *mut TChar,
+ ) -> tresult {
+ info!("get_param_string_by_value");
+ kResultFalse
+ }
+ unsafe fn get_param_value_by_string(
+ &self,
+ _id: u32,
+ _string: *mut TChar,
+ _value_normalized: *mut f64,
+ ) -> tresult {
+ info!("get_param_value_by_string");
+ kResultFalse
+ }
+ unsafe fn normalized_param_to_plain(&self, _id: u32, _value_normalized: f64) -> f64 {
+ info!("normalized_param_to_plain");
+ 0.0
+ }
+ unsafe fn plain_param_to_normalized(&self, _id: u32, _plain_value: f64) -> f64 {
+ info!("plain_param_to_normalized");
+ 0.0
+ }
+ unsafe fn get_param_normalized(&self, _id: u32) -> f64 {
+ info!("get_param_normalized");
+ 0.0
+ }
+ unsafe fn set_param_normalized(&mut self, _id: u32, _value: f64) -> tresult {
+ info!("set_param_normalized");
+ kResultOk
+ }
+ unsafe fn set_component_handler(&mut self, _handler: *mut c_void) -> tresult {
+ info!("set_component_handler");
+ kResultOk
+ }
+ unsafe fn create_view(&self, _name: FIDString) -> *mut c_void {
+ info!("Called: AGainController::create_view()");
+ null_mut()
+ }
+}
+impl IAudioProcessor for PassthruPlugin {
+ unsafe fn set_bus_arrangements(
+ &self,
+ _inputs: *mut u64,
+ _num_ins: i32,
+ _outputs: *mut u64,
+ _num_outs: i32,
+ ) -> i32 {
+ kResultFalse
+ }
+
+ unsafe fn get_bus_arrangements(&self, dir: i32, idx: i32, arr: *mut u64) -> i32 {
+ info!("get_bus(): dir: {}, idx: {}, arr: {:016b}", dir, idx, *arr);
+ let arr = &mut *arr;
+ if (*arr == 0x0) || (*arr == 0x1) || (*arr == 0x3) {
+ kResultOk
+ } else {
+ *arr = 0x03;
+ kResultOk
+ }
+ }
+
+ unsafe fn can_process_sample_size(&self, _symbolic_sample_size: i32) -> i32 {
+ kResultOk
+ }
+
+ unsafe fn get_latency_sample(&self) -> u32 {
+ 0
+ }
+ unsafe fn setup_processing(&mut self, _setup: *mut ProcessSetup) -> tresult {
+ kResultOk
+ }
+ unsafe fn set_processing(&self, _state: TBool) -> tresult {
+ kResultOk
+ }
+ unsafe fn process(&mut self, data: *mut ProcessData) -> tresult {
+ let data = &*data;
+ let num_samples = data.num_samples as usize;
+ if data.inputs.is_null() || data.outputs.is_null() {
+ return kResultOk;
+ }
+ let inputs: &mut AudioBusBuffers = &mut *data.inputs;
+ let outputs: &mut AudioBusBuffers = &mut *data.outputs;
+ let num_channels = inputs.num_channels as usize;
+ let input_ptr = std::slice::from_raw_parts(inputs.buffers, num_channels);
+ let output_ptr = std::slice::from_raw_parts(outputs.buffers, num_channels);
+ let sample_size = if data.symbolic_sample_size == 0 { 4 } else { 8 };
+ for (i, o) in input_ptr.iter().zip(output_ptr.iter()) {
+ copy_nonoverlapping(*i, *o, num_samples * sample_size);
+ }
+ kResultOk
+ }
+ unsafe fn get_tail_samples(&self) -> u32 {
+ 0
+ }
+}
+
+impl IAudioPresentationLatency for PassthruPlugin {
+ unsafe fn set_audio_presentation_latency_sample(
+ &self,
+ _dir: BusDirection,
+ _bus_idx: i32,
+ _latency_samples: u32,
+ ) -> tresult {
+ kResultOk
+ }
+}
+
+impl IAutomationState for PassthruPlugin {
+ unsafe fn set_automation_state(&self, _state: i32) -> tresult {
+ kResultOk
+ }
+}
+
+impl IPluginBase for PassthruPlugin {
+ unsafe fn initialize(&mut self, _host_context: *mut c_void) -> tresult {
+ kResultOk
+ }
+ unsafe fn terminate(&mut self) -> tresult {
+ kResultOk
+ }
+}
+
+impl IComponent for PassthruPlugin {
+ unsafe fn get_controller_class_id(&self, _tuid: *mut IID) -> tresult {
+ kResultOk
+ }
+
+ unsafe fn set_io_mode(&self, _mode: i32) -> tresult {
+ kResultOk
+ }
+
+ unsafe fn get_bus_count(&self, type_: i32, _dir: i32) -> i32 {
+ if type_ == MediaTypes::kAudio as i32 {
+ 1
+ } else {
+ 0
+ }
+ }
+
+ unsafe fn get_bus_info(&self, type_: i32, dir: i32, _idx: i32, info: *mut BusInfo) -> tresult {
+ if type_ == MediaTypes::kAudio as i32 {
+ let info = &mut *info;
+ if dir == BusDirections::kInput as i32 {
+ info.direction = dir;
+ info.bus_type = MediaTypes::kAudio as i32;
+ info.channel_count = 2;
+ info.flags = BusFlags::kDefaultActive.bits();
+ wstrcpy("Audio Input", info.name.as_mut_ptr());
+ } else {
+ info.direction = dir;
+ info.bus_type = MediaTypes::kAudio as i32;
+ info.channel_count = 2;
+ info.flags = BusFlags::kDefaultActive.bits();
+ wstrcpy("Audio Output", info.name.as_mut_ptr());
+ }
+ kResultOk
+ } else {
+ kInvalidArgument
+ }
+ }
+
+ unsafe fn get_routing_info(
+ &self,
+ _in_info: *mut RoutingInfo,
+ _out_info: *mut RoutingInfo,
+ ) -> i32 {
+ kResultFalse
+ }
+
+ unsafe fn activate_bus(&mut self, _type_: i32, _dir: i32, _idx: i32, _state: TBool) -> tresult {
+ kResultOk
+ }
+
+ unsafe fn set_active(&self, _state: TBool) -> tresult {
+ kResultOk
+ }
+
+ unsafe fn set_state(&mut self, _state: *mut c_void) -> tresult {
+ kResultOk
+ }
+
+ unsafe fn get_state(&mut self, _state: *mut c_void) -> tresult {
+ kResultOk
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Factory implementation
+
+impl Factory {
+ fn new() -> Box {
+ info!("instantiating factory...");
+ Self::allocate()
+ }
+}
+
+impl IPluginFactory for Factory {
+ unsafe fn get_factory_info(&self, info: *mut vst3_sys::base::PFactoryInfo) -> i32 {
+ let info = &mut *info;
+ strcpy("rust.audio", info.vendor.as_mut_ptr());
+ strcpy("https://rust.audio", info.url.as_mut_ptr());
+ strcpy("mailto://mike@hilgendorf.audio", info.email.as_mut_ptr());
+ info.flags = 8;
+ kResultOk
+ }
+
+ unsafe fn count_classes(&self) -> i32 {
+ 1
+ }
+ unsafe fn get_class_info(&self, idx: i32, info: *mut vst3_sys::base::PClassInfo) -> i32 {
+ match idx {
+ 0 => {
+ let info = &mut *info;
+ info.cardinality = 0x7FFF_FFFF;
+ info.cid = PassthruPlugin::CID;
+ strcpy("Audio Module Class", info.category.as_mut_ptr());
+ strcpy("Pass Through", info.name.as_mut_ptr());
+ }
+ _ => {
+ info!("invalid class info ID {}", idx);
+ return kInvalidArgument;
+ }
+ }
+ kResultOk
+ }
+ unsafe fn create_instance(
+ &self,
+ cid: *mut vst3_com::sys::GUID,
+ _riid: *mut vst3_com::sys::GUID,
+ obj: *mut *mut core::ffi::c_void,
+ ) -> i32 {
+ let iid = *cid;
+ match iid {
+ PassthruPlugin::CID => {
+ let ptr = Box::into_raw(PassthruPlugin::new()) as *mut c_void;
+ *obj = ptr;
+ kResultOk
+ }
+ _ => kResultFalse,
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+pub fn init() {
+ if let Err(e) = simple_logger::init() {
+ println!("{:?}", e);
+ }
+ info!("plugin library loaded");
+}
+
+#[no_mangle]
+#[allow(non_snake_case)]
+pub unsafe extern "system" fn GetPluginFactory() -> *mut c_void {
+ info!("calling plugin factory");
+ Box::into_raw(Factory::new()) as *mut c_void
+}
+
+#[cfg(target_os = "linux")]
+#[no_mangle]
+#[allow(non_snake_case)]
+pub extern "system" fn ModuleEntry(_: *mut c_void) -> bool {
+ init();
+ true
+}
+
+#[cfg(target_os = "linux")]
+#[no_mangle]
+#[allow(non_snake_case)]
+pub extern "system" fn ModuleExit() -> bool {
+ true
+}
+
+#[cfg(target_os = "macos")]
+#[no_mangle]
+#[allow(non_snake_case)]
+pub extern "system" fn bundleEntry() -> bool {
+ init();
+ true
+}
+
+#[cfg(target_os = "linux")]
+#[no_mangle]
+#[allow(non_snake_case)]
+pub extern "system" fn bundleExit() -> bool {
+ true
+}
+
+#[cfg(target_os = "windows")]
+#[no_mangle]
+#[allow(non_snake_case)]
+pub extern "system" fn InitDll() -> bool {
+ init();
+ true
+}
+
+#[cfg(target_os = "windows")]
+#[no_mangle]
+#[allow(non_snake_case)]
+pub extern "system" fn ExitDll() -> bool {
+ true
+}
diff --git a/examples/passthru/Cargo.toml b/examples/passthru/Cargo.toml
deleted file mode 100644
index 5c6526e..0000000
--- a/examples/passthru/Cargo.toml
+++ /dev/null
@@ -1,19 +0,0 @@
-[package]
-name = "passthru"
-version = "0.1.0"
-authors = ["Mike Hilgendorf "]
-edition = "2018"
-description = """
-Example/smoke test to verify the API bindings work, and to demonstrate
-how the APIs are intended to be used without macros.
-"""
-
-[lib]
-crate-type = ["cdylib"]
-
-[dependencies]
-vst3-sys = { path = "../../"}
-vst3-com = { path = "../../com" }
-log = "0.4"
-simple_logger = "1.6.0"
-lazy_static = "1.4.0"
\ No newline at end of file
diff --git a/examples/passthru/src/lib.rs b/examples/passthru/src/lib.rs
deleted file mode 100644
index bb420ee..0000000
--- a/examples/passthru/src/lib.rs
+++ /dev/null
@@ -1,292 +0,0 @@
-#![allow(unused_unsafe)]
-use lazy_static::lazy_static;
-use log::*;
-use std::os::raw::{c_char, c_void};
-use std::ptr::copy_nonoverlapping as memcpy;
-use std::sync::Mutex;
-use vst3_com::sys::GUID;
-use vst3_com::IID;
-use vst3_sys::base::{
- kInvalidArgument, kResultOk, tresult, IPluginBase, IPluginFactory, IUnknown, TBool,
-};
-use vst3_sys::vst::{
- BusDirection, BusDirections, BusFlags, BusInfo, IAudioPresentationLatency, IAudioProcessor,
- IAutomationState, IComponent, MediaTypes, ProcessData, ProcessSetup, RoutingInfo,
-};
-use vst3_sys::VST3;
-
-#[VST3(implements(
- IAudioProcessor,
- IAudioPresentationLatency,
- IAutomationState,
- IPluginBase
-))]
-pub struct PassthruPlugin {}
-pub struct PassthruController {}
-impl PassthruPlugin {
- const CID: GUID = GUID {
- data: [
- 0x93, 0x68, 0x4f, 0x1a, 0x46, 0x11, 0x91, 0x01, 0x0, 0, 0xb4, 0x39, 0xe5, 0x64, 0x8a,
- 0xda,
- ],
- };
- pub fn new() -> Box {
- PassthruPlugin::allocate()
- }
-}
-#[VST3(implements(IPluginFactory))]
-pub struct Factory {}
-
-impl IAudioProcessor for PassthruPlugin {
- unsafe fn set_bus_arrangements(
- &self,
- _inputs: *mut u64,
- _num_ins: i32,
- _outputs: *mut u64,
- _num_outs: i32,
- ) -> i32 {
- unimplemented!()
- }
-
- unsafe fn get_bus_arrangements(&self, _dir: i32, _index: i32, _arr: *mut u64) -> i32 {
- unimplemented!()
- }
-
- unsafe fn can_process_sample_size(&self, _symbolic_sample_size: i32) -> i32 {
- unimplemented!()
- }
-
- unsafe fn get_latency_sample(&self) -> u32 {
- 0
- }
- unsafe fn setup_processing(&mut self, _setup: *mut ProcessSetup) -> tresult {
- kResultOk
- }
- unsafe fn set_processing(&self, _state: TBool) -> tresult {
- kResultOk
- }
- unsafe fn process(&mut self, _data: *mut ProcessData) -> tresult {
- kResultOk
- }
- unsafe fn get_tail_samples(&self) -> u32 {
- 0
- }
-}
-
-impl IAudioPresentationLatency for PassthruPlugin {
- unsafe fn set_audio_presentation_latency_sample(
- &self,
- _dir: BusDirection,
- _bus_idx: i32,
- _latency_samples: u32,
- ) -> tresult {
- kResultOk
- }
-}
-
-impl IAutomationState for PassthruPlugin {
- unsafe fn set_automation_state(&self, _state: i32) -> tresult {
- kResultOk
- }
-}
-
-impl IPluginBase for PassthruPlugin {
- unsafe fn initialize(&mut self, _host_context: *mut c_void) -> tresult {
- kResultOk
- }
- unsafe fn terminate(&mut self) -> tresult {
- kResultOk
- }
-}
-
-impl IComponent for PassthruPlugin {
- unsafe fn get_controller_class_id(&self, _tuid: *mut IID) -> tresult {
- kResultOk
- }
-
- unsafe fn set_io_mode(&self, _mode: i32) -> tresult {
- kResultOk
- }
-
- unsafe fn get_bus_count(&self, type_: i32, _dir: i32) -> i32 {
- if type_ == MediaTypes::kAudio as i32 {
- 1
- } else {
- 0
- }
- }
-
- unsafe fn get_bus_info(&self, type_: i32, dir: i32, _idx: i32, info: *mut BusInfo) -> tresult {
- if type_ == MediaTypes::kAudio as i32 {
- let info = unsafe { &mut *info };
- if dir == BusDirections::kInput as i32 {
- info.direction = dir;
- info.bus_type = MediaTypes::kAudio as i32;
- info.channel_count = 2;
- info.flags = BusFlags::kDefaultActive.bits();
- } else {
- info.direction = dir;
- info.bus_type = MediaTypes::kAudio as i32;
- info.channel_count = 2;
- info.flags = BusFlags::kDefaultActive.bits();
- }
- kResultOk
- } else {
- kInvalidArgument
- }
- }
-
- unsafe fn get_routing_info(
- &self,
- _in_info: *mut RoutingInfo,
- _out_info: *mut RoutingInfo,
- ) -> i32 {
- unimplemented!()
- }
-
- unsafe fn activate_bus(&mut self, _type_: i32, _dir: i32, _idx: i32, _state: TBool) -> tresult {
- kResultOk
- }
-
- unsafe fn set_active(&self, _state: TBool) -> tresult {
- kResultOk
- }
-
- unsafe fn set_state(&mut self, _state: *mut c_void) -> tresult {
- kResultOk
- }
-
- unsafe fn get_state(&mut self, _state: *mut c_void) -> tresult {
- kResultOk
- }
-}
-
-//todo: IComponent
-//todo: IContextMenuTarget
-//todo: IEditController
-//todo: IEditController2
-//todo: IMidiMapping
-//todo: IEditControllerHostEditing
-//todo: IInterAppAudioConnectionNotification
-//todo: IInterAppAudioPresetManager
-//todo: IConnectionPoint
-//todo: IMidiLearn
-//todo: INoteExpressionController
-//todo: IKeyswitchController
-//todo: INoteExpressionPhysicalUIMapping
-//todo: IPrefetchableSupport
-//todo: IXmlRepresentationController
-//todo: IUnitInfo
-//todo: IProgramListData
-//todo: IUnitData
-//todo: IPlugView
-//todo: IPlugViewContentScaleSupport
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Factory implementation
-
-impl Factory {
- fn new() -> Box {
- info!("instantiating factory...");
- Self::allocate()
- }
-}
-
-unsafe fn strcpy(src: &str, dst: *mut c_char) {
- memcpy(src.as_ptr() as *const c_void as *const _, dst, src.len());
-}
-
-impl IPluginFactory for Factory {
- unsafe fn get_factory_info(&self, info: *mut vst3_sys::base::PFactoryInfo) -> i32 {
- let info = &mut *info;
- strcpy("rust.audio", info.vendor.as_mut_ptr());
- strcpy("https://rust.audio", info.url.as_mut_ptr());
- strcpy("mailto://mike@hilgendorf.audio", info.email.as_mut_ptr());
- info.flags = 8;
- kResultOk
- }
-
- unsafe fn count_classes(&self) -> i32 {
- 1
- }
- unsafe fn get_class_info(&self, idx: i32, info: *mut vst3_sys::base::PClassInfo) -> i32 {
- match idx {
- 0 => {
- let info = &mut *info;
- info.cardinality = 0x7FFFFFFF;
- info.cid = PassthruPlugin::CID;
- strcpy("Audio Module Class", info.category.as_mut_ptr());
- strcpy("Pass Through", info.name.as_mut_ptr());
- }
- _ => {
- info!("invalid class info ID {}", idx);
- return kInvalidArgument;
- }
- }
- kResultOk
- }
- unsafe fn create_instance(
- &self,
- cid: *mut vst3_com::sys::GUID,
- riid: *mut vst3_com::sys::GUID,
- ppv: *mut *mut core::ffi::c_void,
- ) -> i32 {
- //todo: figure out why this method fails in the validator
- let cid = *&*cid;
- let cmp = PassthruPlugin::CID;
-
- info!("creating instance of {:?}", cid);
- if cid == cmp {
- let instance = PassthruPlugin::new();
- instance.add_ref();
- let hr = instance.query_interface(riid, ppv);
- instance.release();
- core::mem::forget(instance);
- return hr;
- } else {
- warn!("CID not found");
- }
- kResultOk
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// entry point and wrapping code to satisfy the borrow checker
-// todo: cleanup singleton instance so this is less hacky
-
-struct FactoryWrapper {
- factory: Box,
-}
-unsafe impl Send for FactoryWrapper {}
-unsafe impl Sync for FactoryWrapper {}
-lazy_static! {
- static ref WRAPPER: Mutex = Mutex::new(FactoryWrapper {
- factory: Factory::new()
- });
-}
-
-#[no_mangle]
-#[allow(non_snake_case)]
-pub unsafe extern "system" fn GetPluginFactory() -> *mut c_void {
- info!("calling plugin factory");
- let factory = &mut *WRAPPER.lock().unwrap().factory;
- factory.add_ref();
- factory as *mut _ as *mut _
-}
-
-#[no_mangle]
-#[allow(non_snake_case)]
-pub extern "system" fn ModuleEntry(_: *mut c_void) -> bool {
- if let Err(e) = simple_logger::init() {
- println!("{:?}", e);
- }
- info!("Module entered");
- true
-}
-
-#[no_mangle]
-#[allow(non_snake_case)]
-pub extern "system" fn ModuleExit() -> bool {
- info!("Module exited");
- true
-}
diff --git a/osx-bundler.sh b/osx-bundler.sh
new file mode 100755
index 0000000..ddb7c1f
--- /dev/null
+++ b/osx-bundler.sh
@@ -0,0 +1,51 @@
+#!/bin/bash -e
+# Make sure we have the arguments we need
+if [[ -z $1 || -z $2 ]]; then
+ echo "Generates a macOS bundle from a compiled dylib file"
+ echo "Example:"
+ echo -e "\t$0 Plugin target/release/plugin.dylib"
+ echo -e "\tCreates a Plugin.vst3 bundle"
+else
+ # Make the bundle folder
+ mkdir -p "target/debug/$1.vst3/Contents/MacOS"
+
+ # Create the PkgInfo
+ echo "BNDL????" > "target/debug/$1.vst3/Contents/PkgInfo"
+
+ #build the Info.Plist
+ echo "
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ $1
+ CFBundleGetInfoString
+ vst3
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ com.rust-vst.$1
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $1
+ CFBundlePackageType
+ BNDL
+ CFBundleVersion
+ 1.0
+ CFBundleSignature
+ $((RANDOM % 9999))
+ CSResourcesFileMapped
+
+
+" > "target/debug/$1.vst3/Contents/Info.plist"
+
+ # move the provided library to the correct location
+ cp "$2" "target/debug/$1.vst3/Contents/MacOS/$1"
+
+ echo "Created bundle target/debug/$1.vst3"
+fi
+
+
diff --git a/readme.md b/readme.md
index 7c18095..14b7db2 100644
--- a/readme.md
+++ b/readme.md
@@ -4,6 +4,23 @@ A port of the VST3 API in pure Rust.
We do not distribute the SDK nor try and wrap it in clean abstractions, just port compatiable bindings to the API which is based on COM. The full SDK can be found at sdk.steinberg.net or cloned from github [here](https://github.com/steinbergmedia/vst3sdk).
+## Building Examples
+
+The examples can be built using [cargo-make](https://github.com/sagiegurari/cargo-make).
+
+```
+cargo install --force cargo-make
+```
+
+The two current examples are `again` and `passthru`.
+
+```
+cargo make again
+cargo make passthru
+```
+
+Provided is a script to package a vst3 plugin as a MacOS bundle, which requires an `Info.plist` file and `PkgInfo` to be included in the vst3 plugin directory.
+
## Completeness and Contributions
Currently this crate is missing definitions of some of the constants found in the SDK, and help covering them would be greatly appreciated. If you find something missing from the SDK please submit a PR to add it. You can also grep for `todo` to find gaps.