1 //-----------------------------------------------------------------------------
2 // LICENSE
3 // (c) 2018, Steinberg Media Technologies GmbH, All Rights Reserved
4 // (c) 2018, Guillaume Piolat (contact@auburnsounds.com)
5 //-----------------------------------------------------------------------------
6 //
7 // This Software Development Kit is licensed under the terms of the General
8 // Public License (GPL) Version 3.
9 //
10 // Details of that license can be found at: www.gnu.org/licenses/gpl-3.0.html
11 //-----------------------------------------------------------------------------
12 module dplug.vst3.vst3main;
13 
14 version(VST3):
15 
16 nothrow @nogc:
17 
18 import core.stdc.stdio: snprintf;
19 
20 import dplug.core.runtime;
21 import dplug.core.nogc;
22 
23 import dplug.client.client;
24 import dplug.client.daw;
25 
26 import dplug.vst3.ipluginbase;
27 import dplug.vst3.ftypes;
28 import dplug.vst3.ivstaudioprocessor;
29 import dplug.vst3.client;
30 
31 template VST3EntryPoint(alias ClientClass)
32 {
33     // Those exports are optional, but could be useful in the future
34     enum entry_InitDll = `export extern(C) bool InitDll() nothrow @nogc { return true; }`;
35     enum entry_ExitDll = `export extern(C) bool ExitDll() nothrow @nogc { return true; }`;
36 
37     enum entry_GetPluginFactory =
38         "export extern(C) void* GetPluginFactory() nothrow @nogc" ~
39         "{" ~
40         "    return cast(void*)(GetPluginFactoryInternal!" ~ ClientClass.stringof ~ ");" ~
41         "}";
42 
43     // macOS has different "shared libraries" and "bundle"
44     // For Cubase, VST3 validator and Nuendo, the VST3 binary must be a macOS "bundle".
45     // Other hosts don't seem to care.
46     // This fake a macOS bundle with special entry points.
47     enum entry_bundleEntry = `export extern(C) bool bundleEntry(void*) nothrow @nogc { return true; }`;
48     enum entry_bundleExit = `export extern(C) bool bundleExit() nothrow @nogc { return true; }`;
49 
50     // Issue #433: on Linux, VST3 need entry points ModuleEntry and ModuleExit
51     version(linux)
52     {
53         enum entry_ModuleEntry = `export extern(C) bool ModuleEntry(void*) nothrow @nogc { return true; }`;
54         enum entry_ModuleExit = `export extern(C) bool ModuleExit(void*) nothrow @nogc { return true; }`;
55     }
56     else
57     {
58         enum entry_ModuleEntry = ``;
59         enum entry_ModuleExit = ``;
60     }
61 
62     const char[] VST3EntryPoint = entry_InitDll ~ entry_ExitDll 
63                                 ~ entry_GetPluginFactory 
64                                 ~ entry_bundleEntry ~ entry_bundleExit
65                                 ~ entry_ModuleEntry ~ entry_ModuleExit;
66 }
67 
68 IPluginFactory GetPluginFactoryInternal(ClientClass)()
69 {
70     ScopedForeignCallback!(false, true) scopedCallback;
71     scopedCallback.enter();
72 
73     if (!gPluginFactory)
74     {
75         // Create a client just for the purpose of creating the factory
76         ClientClass client = mallocNew!ClientClass();
77         scope(exit) client.destroyFree();
78 
79         auto vendorNameZ = CString(client.vendorName);
80 
81         string vendorEmail = client.getVendorSupportEmail();
82         if (!vendorEmail) vendorEmail = "support@example.com";
83 
84         string pluginHomepage = client.pluginHomepage();
85         if (!pluginHomepage) pluginHomepage = "https://google.com";
86 
87         auto pluginHomepageZ = CString(pluginHomepage);
88         auto vendorEmailZ = CString(vendorEmail);
89 
90         PFactoryInfo factoryInfo = PFactoryInfo(vendorNameZ,
91                                                 pluginHomepageZ,
92                                                 vendorEmailZ,
93                                                 PFactoryInfo.kUnicode);
94 
95         auto pluginFactory = mallocNew!CPluginFactory(factoryInfo);
96         gPluginFactory = pluginFactory;
97 
98         enum uint DPLUG_MAGIC  = 0xB20BA92;
99         enum uint DPLUG_MAGIC2 = 0xCE0B145;
100         char[4] vid = client.getVendorUniqueID();
101         char[4] pid = client.getPluginUniqueID();
102         TUID classId = INLINE_UID(DPLUG_MAGIC, DPLUG_MAGIC2, *cast(uint*)(vid.ptr), *cast(uint*)(pid.ptr));
103 
104         auto pluginNameZ = CString(client.pluginName());
105         char[64] versionString;
106         client.getPublicVersion().toVST3VersionString(versionString.ptr, 64);
107 
108         string vst3Category;
109         final switch(client.pluginCategory()) with (PluginCategory)
110         {
111             case effectAnalysisAndMetering: vst3Category = PlugType.kFxAnalyzer; break;
112             case effectDelay:               vst3Category = PlugType.kFxDelay; break;
113             case effectDistortion:          vst3Category = PlugType.kFxDistortion; break;
114             case effectDynamics:            vst3Category = PlugType.kFxDynamics; break;
115             case effectEQ:                  vst3Category = PlugType.kFxEQ; break;
116             case effectImaging:             vst3Category = PlugType.kFxSpatial; break;
117             case effectModulation:          vst3Category = PlugType.kFxModulation; break;
118             case effectPitch:               vst3Category = PlugType.kFxPitchShift; break;
119             case effectReverb:              vst3Category = PlugType.kFxReverb; break;
120             case effectOther:               vst3Category = PlugType.kFx; break;
121             case instrumentDrums:           vst3Category = PlugType.kInstrumentDrum; break;
122             case instrumentSampler:         vst3Category = PlugType.kInstrumentSampler; break;
123             case instrumentSynthesizer:     vst3Category = PlugType.kInstrumentSynth; break;
124             case instrumentOther:           vst3Category = PlugType.kInstrumentSynth; break;
125             case invalid:                   assert(false);
126         }
127 
128         PClassInfo2 componentClass = PClassInfo2(classId,
129                                                  PClassInfo.kManyInstances, // cardinality
130                                                  kVstAudioEffectClass.ptr,
131                                                  pluginNameZ,
132                                                  kSimpleModeSupported,
133                                                  vst3Category.ptr,
134                                                  vendorNameZ,
135                                                  versionString.ptr,
136                                                  kVstVersionString.ptr);
137         pluginFactory.registerClass(&componentClass, &(createVST3Client!ClientClass));
138     }
139     else
140         gPluginFactory.addRef();
141 
142     return gPluginFactory;
143 }
144 
145 // must return a IAudioProcessor
146 extern(C) FUnknown createVST3Client(ClientClass)(void* useless) nothrow @nogc
147 {
148     ScopedForeignCallback!(false, true) scopedCallback;
149     scopedCallback.enter();
150     ClientClass client = mallocNew!ClientClass();
151     VST3Client plugin = mallocNew!VST3Client(client);
152     return plugin;
153 }