1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
use std::ffi::c_void;
use std::marker::PhantomData;
use std::mem;
use vst3_sys::base::{kInvalidArgument, kResultOk, tresult};
use vst3_sys::base::{IPluginFactory, IPluginFactory2, IPluginFactory3, IUnknown};
use vst3_sys::VST3;

// Alias needed for the VST3 attribute macro
use vst3_sys as vst3_com;

use super::subcategories::Vst3SubCategory;
use super::util::u16strlcpy;
use super::wrapper::Wrapper;
use crate::prelude::Vst3Plugin;
use crate::wrapper::util::strlcpy;

/// The VST3 SDK version this is roughly based on. The bindings include some VST 3.7 things but not
/// everything, so we'll play it safe.
const VST3_SDK_VERSION: &str = "VST 3.6.14";

#[doc(hidden)]
#[VST3(implements(IPluginFactory, IPluginFactory2, IPluginFactory3))]
pub struct Factory<P: Vst3Plugin> {
    /// The type will be used for constructing plugin instances later.
    _phantom: PhantomData<P>,
}

impl<P: Vst3Plugin> Factory<P> {
    pub fn new() -> Box<Self> {
        Self::allocate(PhantomData::default())
    }
}

impl<P: Vst3Plugin> IPluginFactory for Factory<P> {
    unsafe fn get_factory_info(&self, info: *mut vst3_sys::base::PFactoryInfo) -> tresult {
        *info = mem::zeroed();

        let info = &mut *info;
        strlcpy(&mut info.vendor, P::VENDOR);
        strlcpy(&mut info.url, P::URL);
        strlcpy(&mut info.email, P::EMAIL);
        info.flags = vst3_sys::base::FactoryFlags::kUnicode as i32;

        kResultOk
    }

    unsafe fn count_classes(&self) -> i32 {
        // We don't do shell plugins, and good of an idea having separated components and edit
        // controllers in theory is, few software can use it, and doing that would make our simple
        // microframework a lot less simple
        1
    }

    unsafe fn get_class_info(&self, index: i32, info: *mut vst3_sys::base::PClassInfo) -> tresult {
        if index != 0 {
            return kInvalidArgument;
        }

        *info = mem::zeroed();

        let info = &mut *info;
        info.cid.data = P::PLATFORM_VST3_CLASS_ID;
        info.cardinality = vst3_sys::base::ClassCardinality::kManyInstances as i32;
        strlcpy(&mut info.category, "Audio Module Class");
        strlcpy(&mut info.name, P::NAME);

        kResultOk
    }

    unsafe fn create_instance(
        &self,
        cid: *const vst3_sys::IID,
        iid: *const vst3_sys::IID,
        obj: *mut *mut vst3_sys::c_void,
    ) -> tresult {
        check_null_ptr!(cid, obj);

        if (*cid).data != P::PLATFORM_VST3_CLASS_ID {
            return kInvalidArgument;
        }

        let wrapper = Wrapper::<P>::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);
        }

        result
    }
}

impl<P: Vst3Plugin> IPluginFactory2 for Factory<P> {
    unsafe fn get_class_info2(
        &self,
        index: i32,
        info: *mut vst3_sys::base::PClassInfo2,
    ) -> tresult {
        if index != 0 {
            return kInvalidArgument;
        }

        *info = mem::zeroed();

        let info = &mut *info;
        info.cid.data = P::PLATFORM_VST3_CLASS_ID;
        info.cardinality = vst3_sys::base::ClassCardinality::kManyInstances as i32;
        strlcpy(&mut info.category, "Audio Module Class");
        strlcpy(&mut info.name, P::NAME);
        info.class_flags = 1 << 1; // kSimpleModeSupported
        strlcpy(&mut info.subcategories, &make_subcategories_string::<P>());
        strlcpy(&mut info.vendor, P::VENDOR);
        strlcpy(&mut info.version, P::VERSION);
        strlcpy(&mut info.sdk_version, VST3_SDK_VERSION);

        kResultOk
    }
}

impl<P: Vst3Plugin> IPluginFactory3 for Factory<P> {
    unsafe fn get_class_info_unicode(
        &self,
        index: i32,
        info: *mut vst3_sys::base::PClassInfoW,
    ) -> tresult {
        if index != 0 {
            return kInvalidArgument;
        }

        *info = mem::zeroed();

        let info = &mut *info;
        info.cid.data = P::PLATFORM_VST3_CLASS_ID;
        info.cardinality = vst3_sys::base::ClassCardinality::kManyInstances as i32;
        strlcpy(&mut info.category, "Audio Module Class");
        u16strlcpy(&mut info.name, P::NAME);
        info.class_flags = 1 << 1; // kSimpleModeSupported
        strlcpy(&mut info.subcategories, &make_subcategories_string::<P>());
        u16strlcpy(&mut info.vendor, P::VENDOR);
        u16strlcpy(&mut info.version, P::VERSION);
        u16strlcpy(&mut info.sdk_version, VST3_SDK_VERSION);

        kResultOk
    }

    unsafe fn set_host_context(&self, _context: *mut c_void) -> tresult {
        // We don't need to do anything with this
        kResultOk
    }
}

fn make_subcategories_string<P: Vst3Plugin>() -> String {
    // No idea if any hosts do something with OnlyRT, but it's part of VST3's example categories
    // list. Plugins should not be adding this feature manually
    nih_debug_assert!(!P::VST3_SUBCATEGORIES.contains(&Vst3SubCategory::Custom("OnlyRT")));
    let subcategory_string = P::VST3_SUBCATEGORIES
        .iter()
        .map(Vst3SubCategory::as_str)
        .collect::<Vec<&str>>()
        .join("|");

    let subcategory_string = if P::HARD_REALTIME_ONLY {
        format!("{subcategory_string}|OnlyRT")
    } else {
        subcategory_string
    };
    nih_debug_assert!(subcategory_string.len() <= 127);

    subcategory_string
}