conformal_vst_wrapper/
factory.rs

1use super::edit_controller;
2use crate::{ClassCategory, enum_to_i32, enum_to_u32};
3use crate::{ClassID, Info};
4use vst3::Class;
5use vst3::ComWrapper;
6use vst3::Steinberg::IPluginFactoryTrait;
7use vst3::Steinberg::{FIDString, IPluginFactory2};
8use vst3::Steinberg::{IPluginBase, tresult};
9use vst3::Steinberg::{IPluginFactory, IPluginFactory2Trait};
10use vst3::com_scrape_types::Unknown;
11
12pub struct Factory {
13    classes: &'static [&'static dyn ClassCategory],
14    info: Info<'static>,
15}
16
17#[cfg(test)]
18#[path = "factory.tests.rs"]
19mod tests;
20
21const EC_TAG: &str = "EC";
22
23impl Factory {
24    pub fn new(classes: &'static [&'static dyn ClassCategory], info: Info<'static>) -> Factory {
25        assert!(info.vendor.len() < vst3::Steinberg::PFactoryInfo_::kNameSize as usize);
26        assert!(info.url.len() < vst3::Steinberg::PFactoryInfo_::kURLSize as usize);
27        assert!(info.email.len() < vst3::Steinberg::PFactoryInfo_::kEmailSize as usize);
28        assert!(info.version.len() < vst3::Steinberg::PClassInfo2_::kVersionSize as usize);
29
30        for class in classes {
31            assert!(
32                class.info().name.len()
33                    < vst3::Steinberg::PClassInfo_::kNameSize as usize - EC_TAG.len()
34            );
35        }
36        Factory { classes, info }
37    }
38}
39
40fn to_cstr<'a, T: Iterator<Item = &'a mut i8>>(s: &str, it: T) {
41    let s = s.as_bytes();
42    for (i, c) in it.enumerate() {
43        if i < s.len() {
44            *c = s[i] as i8;
45        } else {
46            *c = 0;
47        }
48    }
49}
50
51unsafe fn compare_fid(a: FIDString, b: ClassID) -> bool {
52    unsafe {
53        for i in 0..16 {
54            if *a.offset(i) != b[i as usize] as i8 {
55                return false;
56            }
57        }
58        true
59    }
60}
61
62unsafe fn to_iid(iid: FIDString) -> [u8; 16] {
63    unsafe {
64        let mut ret = [0; 16];
65        for i in 0isize..16 {
66            ret[i as usize] = *iid.offset(i) as u8;
67        }
68        ret
69    }
70}
71
72impl IPluginFactoryTrait for Factory {
73    unsafe fn countClasses(&self) -> vst3::Steinberg::int32 {
74        (self.classes.len() * 2).try_into().unwrap()
75    }
76
77    unsafe fn createInstance(
78        &self,
79        class_id: FIDString,
80        interface_id: FIDString,
81        obj: *mut *mut ::std::ffi::c_void,
82    ) -> tresult {
83        unsafe {
84            if obj.is_null() {
85                return vst3::Steinberg::kInvalidArgument;
86            }
87            for class in self.classes {
88                if compare_fid(class_id, class.info().edit_controller_cid) {
89                    let com_ptr = ComWrapper::new(edit_controller::create(
90                        class.create_parameter_model(),
91                        class.info().ui_initial_size,
92                        class.info().resizability,
93                        class.get_kind(),
94                    ))
95                    .to_com_ptr::<IPluginBase>()
96                    .unwrap();
97
98                    if let Some(i) =
99                        IPluginBase::query_interface(com_ptr.as_ptr(), &to_iid(interface_id))
100                    {
101                        std::ptr::write_unaligned(obj, i);
102                        return vst3::Steinberg::kResultOk;
103                    }
104                    return vst3::Steinberg::kNoInterface;
105                }
106                if compare_fid(class_id, class.info().cid) {
107                    let com_ptr = class.create_processor(class.info().edit_controller_cid);
108
109                    if let Some(i) =
110                        IPluginBase::query_interface(com_ptr.as_ptr(), &to_iid(interface_id))
111                    {
112                        std::ptr::write_unaligned(obj, i);
113                        return vst3::Steinberg::kResultOk;
114                    }
115                    return vst3::Steinberg::kNoInterface;
116                }
117            }
118            vst3::Steinberg::kInvalidArgument
119        }
120    }
121
122    unsafe fn getClassInfo(
123        &self,
124        index: vst3::Steinberg::int32,
125        info: *mut vst3::Steinberg::PClassInfo,
126    ) -> tresult {
127        unsafe {
128            if let Some(class) = &self.classes.get(index as usize / 2) {
129                let is_ec = index % 2 == 1;
130
131                (*info).cardinality =
132                    enum_to_i32(vst3::Steinberg::PClassInfo_::ClassCardinality_::kManyInstances)
133                        .unwrap();
134
135                if is_ec {
136                    (*info)
137                        .cid
138                        .iter_mut()
139                        .zip(class.info().edit_controller_cid.iter())
140                        .for_each(|(a, b)| *a = *b as i8);
141                    to_cstr("Component Controller Class", (*info).category.iter_mut());
142                    to_cstr(class.info().name, (*info).name.iter_mut());
143                    to_cstr(
144                        EC_TAG,
145                        (*info).name.iter_mut().skip(class.info().name.len()),
146                    );
147                } else {
148                    (*info)
149                        .cid
150                        .iter_mut()
151                        .zip(class.info().cid.iter())
152                        .for_each(|(a, b)| *a = *b as i8);
153                    to_cstr("Audio Module Class", (*info).category.iter_mut());
154                    to_cstr(class.info().name, (*info).name.iter_mut());
155                }
156                vst3::Steinberg::kResultOk
157            } else {
158                vst3::Steinberg::kInvalidArgument
159            }
160        }
161    }
162
163    unsafe fn getFactoryInfo(&self, info: *mut vst3::Steinberg::PFactoryInfo) -> tresult {
164        unsafe {
165            to_cstr(self.info.vendor, (*info).vendor.iter_mut());
166            to_cstr(self.info.url, (*info).url.iter_mut());
167            to_cstr(self.info.email, (*info).email.iter_mut());
168            (*info).flags =
169                enum_to_i32(vst3::Steinberg::PFactoryInfo_::FactoryFlags_::kUnicode).unwrap();
170            vst3::Steinberg::kResultOk
171        }
172    }
173}
174
175impl IPluginFactory2Trait for Factory {
176    unsafe fn getClassInfo2(
177        &self,
178        index: vst3::Steinberg::int32,
179        info: *mut vst3::Steinberg::PClassInfo2,
180    ) -> tresult {
181        unsafe {
182            if let Some(class) = &self.classes.get(index as usize / 2) {
183                let is_ec = index % 2 == 1;
184                (*info).cardinality =
185                    enum_to_i32(vst3::Steinberg::PClassInfo_::ClassCardinality_::kManyInstances)
186                        .unwrap();
187
188                if is_ec {
189                    (*info)
190                        .cid
191                        .iter_mut()
192                        .zip(class.info().edit_controller_cid.iter())
193                        .for_each(|(a, b)| *a = *b as i8);
194                    to_cstr("Component Controller Class", (*info).category.iter_mut());
195                    to_cstr(class.info().name, (*info).name.iter_mut());
196                    to_cstr(
197                        EC_TAG,
198                        (*info).name.iter_mut().skip(class.info().name.len()),
199                    );
200                } else {
201                    (*info)
202                        .cid
203                        .iter_mut()
204                        .zip(class.info().cid.iter())
205                        .for_each(|(a, b)| *a = *b as i8);
206                    to_cstr("Audio Module Class", (*info).category.iter_mut());
207                    to_cstr(class.info().name, (*info).name.iter_mut());
208                }
209                to_cstr(class.category_str(), (*info).subCategories.iter_mut());
210                (*info).classFlags =
211                    enum_to_u32(vst3::Steinberg::Vst::ComponentFlags_::kDistributable).unwrap();
212                to_cstr(
213                    std::ffi::CStr::from_ptr(vst3::Steinberg::Vst::SDKVersionString)
214                        .to_str()
215                        .unwrap(),
216                    (*info).sdkVersion.iter_mut(),
217                );
218                to_cstr(self.info.version, (*info).version.iter_mut());
219                to_cstr(self.info.vendor, (*info).vendor.iter_mut());
220                vst3::Steinberg::kResultOk
221            } else {
222                vst3::Steinberg::kInvalidArgument
223            }
224        }
225    }
226}
227
228impl Class for Factory {
229    type Interfaces = (IPluginFactory, IPluginFactory2);
230}