Skip to main content

nice_plug/wrapper/vst3/
factory.rs

1//! Utilities for building a VST3 factory.
2//!
3//! In order to support exporting multiple VST3 plugins from a single library a lot of functionality
4//! had to be moved to the `nice_export_vst3!()` macro. Because working in macro-land can be both
5//! frustrating and error prone, most code that does not specifically depend on all of the exposed
6//! plugin types was moved back to this module so it can be compiled and type checked as normal.
7
8use vst3::Steinberg::Vst::ComponentFlags_;
9use vst3::Steinberg::{
10    PClassInfo, PClassInfo_::ClassCardinality_, PClassInfo2, PClassInfoW, PFactoryInfo,
11    PFactoryInfo_::FactoryFlags_,
12};
13
14use super::subcategories::Vst3SubCategory;
15use crate::wrapper::util::strlcpy;
16use crate::wrapper::vst3::Vst3Plugin;
17use crate::wrapper::vst3::util::u16strlcpy;
18
19/// The VST3 SDK version this is roughly based on. The bindings include some VST 3.7 things but not
20/// everything, so we'll play it safe.
21pub const VST3_SDK_VERSION: &str = "VST 3.6.14";
22
23#[allow(clippy::unnecessary_cast)]
24const K_CLASS_CARDINALITY_MANY_INSTANCES: i32 = ClassCardinality_::kManyInstances as i32;
25#[allow(clippy::unnecessary_cast)]
26const K_FACTORY_FLAG_UNICODE: i32 = FactoryFlags_::kUnicode as i32;
27
28/// The information queried about a plugin through the `IPluginFactory*` methods. Stored in a
29/// separate struct so it can be type erased and stored in an array.
30#[derive(Debug)]
31pub struct PluginInfo {
32    pub cid: &'static [u8; 16],
33    pub name: &'static str,
34    pub subcategories: String,
35    pub vendor: &'static str,
36    pub version: &'static str,
37
38    // These are used for the factory's own info struct
39    pub url: &'static str,
40    pub email: &'static str,
41}
42
43impl PluginInfo {
44    pub fn for_plugin<P: Vst3Plugin>() -> PluginInfo {
45        PluginInfo {
46            cid: &P::PLATFORM_VST3_CLASS_ID,
47            name: P::NAME,
48            subcategories: make_subcategories_string::<P>(),
49            vendor: P::VENDOR,
50            version: P::VERSION,
51            url: P::URL,
52            email: P::EMAIL,
53        }
54    }
55
56    /// Fill a [`PFactoryInfo`] struct with the information from this library. Used in
57    /// `IPluginFactory`.
58    pub fn create_factory_info(&self) -> PFactoryInfo {
59        let mut info: PFactoryInfo = unsafe { std::mem::zeroed() };
60        strlcpy(&mut info.vendor, self.vendor);
61        strlcpy(&mut info.url, self.url);
62        strlcpy(&mut info.email, self.email);
63        info.flags = K_FACTORY_FLAG_UNICODE;
64
65        info
66    }
67
68    /// Fill a [`PClassInfo`] struct with the information from this library. Used in
69    /// `IPluginFactory`.
70    pub fn create_class_info(&self) -> PClassInfo {
71        let mut info: PClassInfo = unsafe { std::mem::zeroed() };
72        info.cid = self.cid.map(|b| b as std::ffi::c_char);
73        info.cardinality = K_CLASS_CARDINALITY_MANY_INSTANCES;
74        strlcpy(&mut info.category, "Audio Module Class");
75        strlcpy(&mut info.name, self.name);
76
77        info
78    }
79
80    /// Fill a [`PClassInfo2`] struct with the information from this library. Used in
81    /// `IPluginFactory2`.
82    pub fn create_class_info_2(&self) -> PClassInfo2 {
83        let mut info: PClassInfo2 = unsafe { std::mem::zeroed() };
84        info.cid = self.cid.map(|b| b as std::ffi::c_char);
85        info.cardinality = K_CLASS_CARDINALITY_MANY_INSTANCES;
86        strlcpy(&mut info.category, "Audio Module Class");
87        strlcpy(&mut info.name, self.name);
88        #[allow(clippy::unnecessary_cast)]
89        {
90            info.classFlags = ComponentFlags_::kSimpleModeSupported as u32;
91        }
92        strlcpy(&mut info.subCategories, &self.subcategories);
93        strlcpy(&mut info.vendor, self.vendor);
94        strlcpy(&mut info.version, self.version);
95        strlcpy(&mut info.sdkVersion, VST3_SDK_VERSION);
96
97        info
98    }
99
100    /// Fill a [`PClassInfoW`] struct with the information from this library. Used in
101    /// `IPluginFactory3`.
102    pub fn create_class_info_unicode(&self) -> PClassInfoW {
103        let mut info: PClassInfoW = unsafe { std::mem::zeroed() };
104        info.cid = self.cid.map(|b| b as std::ffi::c_char);
105        info.cardinality = K_CLASS_CARDINALITY_MANY_INSTANCES;
106        strlcpy(&mut info.category, "Audio Module Class");
107        u16strlcpy(&mut info.name, self.name);
108        #[allow(clippy::unnecessary_cast)]
109        {
110            info.classFlags = ComponentFlags_::kSimpleModeSupported as u32;
111        }
112        strlcpy(&mut info.subCategories, &self.subcategories);
113        u16strlcpy(&mut info.vendor, self.vendor);
114        u16strlcpy(&mut info.version, self.version);
115        u16strlcpy(&mut info.sdkVersion, VST3_SDK_VERSION);
116
117        info
118    }
119}
120
121/// Build a pipe separated subcategories string for a VST3 plugin.
122fn make_subcategories_string<P: Vst3Plugin>() -> String {
123    // No idea if any hosts do something with OnlyRT, but it's part of VST3's example categories
124    // list. Plugins should not be adding this feature manually
125    crate::nice_debug_assert!(!P::VST3_SUBCATEGORIES.contains(&Vst3SubCategory::Custom("OnlyRT")));
126    let subcategory_string = P::VST3_SUBCATEGORIES
127        .iter()
128        .map(Vst3SubCategory::as_str)
129        .collect::<Vec<&str>>()
130        .join("|");
131
132    let subcategory_string = if P::HARD_REALTIME_ONLY {
133        format!("{subcategory_string}|OnlyRT")
134    } else {
135        subcategory_string
136    };
137    crate::nice_debug_assert!(subcategory_string.len() <= 127);
138
139    subcategory_string
140}