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 // This source is part of the "Auburn Sounds (Guillaume Piolat) extension to the 
11 // Steinberg VST 3 Plug-in SDK".
12 //
13 // Details of that license can be found at: www.gnu.org/licenses/gpl-3.0.html
14 //
15 // Dual-licence:
16 // 
17 // The "Auburn Sounds (Guillaume Piolat) extension to the Steinberg VST 3 Plug-in
18 // SDK", hereby referred to as DPLUG:VST3, is a language translation of the VST3 
19 // SDK suitable for usage in Dplug. Any Licensee of a currently valid Steinberg 
20 // VST 3 Plug-In SDK Licensing Agreement (version 2.2.4 or ulterior, hereby referred
21 // to as the AGREEMENT), is granted by Auburn Sounds (Guillaume Piolat) a non-exclusive, 
22 // worldwide, nontransferable license during the term the AGREEMENT to use parts
23 // of DPLUG:VST3 not covered by the AGREEMENT, as if they were originally 
24 // inside the Licensed Software Developer Kit mentionned in the AGREEMENT. 
25 // Under this licence all conditions that apply to the Licensed Software Developer 
26 // Kit also apply to DPLUG:VST3.
27 //
28 //-----------------------------------------------------------------------------
29 module dplug.vst3.vst3main;
30 
31 version(VST3):
32 
33 nothrow @nogc:
34 
35 import core.stdc.stdio: snprintf;
36 
37 import dplug.core.runtime;
38 import dplug.core.nogc;
39 
40 import dplug.client.client;
41 import dplug.client.daw;
42 
43 import dplug.vst3.ipluginbase;
44 import dplug.vst3.ftypes;
45 import dplug.vst3.ivstaudioprocessor;
46 import dplug.vst3.client;
47 
48 template VST3EntryPoint(alias ClientClass)
49 {
50     // Those exports are optional, but could be useful in the future
51     enum entry_InitDll = `export extern(C) bool InitDll() nothrow @nogc { return true; }`;
52     enum entry_ExitDll = `export extern(C) bool ExitDll() nothrow @nogc { return true; }`;
53 
54     enum entry_GetPluginFactory =
55         "export extern(C) void* GetPluginFactory() nothrow @nogc" ~
56         "{" ~
57         "    return cast(void*)(GetPluginFactoryInternal!" ~ ClientClass.stringof ~ ");" ~
58         "}";
59 
60     // macOS has different "shared libraries" and "bundle"
61     // For Cubase, VST3 validator and Nuendo, the VST3 binary must be a macOS "bundle".
62     // Other hosts don't seem to care.
63     // This fake a macOS bundle with special entry points.
64     enum entry_bundleEntry = `export extern(C) bool bundleEntry(void*) nothrow @nogc { return true; }`;
65     enum entry_bundleExit = `export extern(C) bool bundleExit() nothrow @nogc { return true; }`;
66 
67     // Issue #433: on Linux, VST3 need entry points ModuleEntry and ModuleExit
68     version(linux)
69     {
70         enum entry_ModuleEntry = `export extern(C) bool ModuleEntry(void*) nothrow @nogc { return true; }`;
71         enum entry_ModuleExit = `export extern(C) bool ModuleExit(void*) nothrow @nogc { return true; }`;
72     }
73     else
74     {
75         enum entry_ModuleEntry = ``;
76         enum entry_ModuleExit = ``;
77     }
78 
79     const char[] VST3EntryPoint = entry_InitDll ~ entry_ExitDll 
80                                 ~ entry_GetPluginFactory 
81                                 ~ entry_bundleEntry ~ entry_bundleExit
82                                 ~ entry_ModuleEntry ~ entry_ModuleExit;
83 }
84 
85 IPluginFactory GetPluginFactoryInternal(ClientClass)()
86 {
87     ScopedForeignCallback!(false, true) scopedCallback;
88     scopedCallback.enter();
89 
90     if (!gPluginFactory)
91     {
92         // Create a client just for the purpose of creating the factory
93         ClientClass client = mallocNew!ClientClass();
94         scope(exit) client.destroyFree();
95 
96         auto vendorNameZ = CString(client.vendorName);
97 
98         string vendorEmail = client.getVendorSupportEmail();
99         if (!vendorEmail) vendorEmail = "support@example.com";
100 
101         string pluginHomepage = client.pluginHomepage();
102         if (!pluginHomepage) pluginHomepage = "https://google.com";
103 
104         auto pluginHomepageZ = CString(pluginHomepage);
105         auto vendorEmailZ = CString(vendorEmail);
106 
107         PFactoryInfo factoryInfo = PFactoryInfo(vendorNameZ,
108                                                 pluginHomepageZ,
109                                                 vendorEmailZ,
110                                                 PFactoryInfo.kUnicode);
111 
112         auto pluginFactory = mallocNew!CPluginFactory(factoryInfo);
113         gPluginFactory = pluginFactory;
114 
115         enum uint DPLUG_MAGIC  = 0xB20BA92;
116         enum uint DPLUG_MAGIC2 = 0xCE0B145;
117         char[4] vid = client.getVendorUniqueID();
118         char[4] pid = client.getPluginUniqueID();
119         TUID classId = INLINE_UID(DPLUG_MAGIC, DPLUG_MAGIC2, *cast(uint*)(vid.ptr), *cast(uint*)(pid.ptr));
120 
121         auto pluginNameZ = CString(client.pluginName());
122         char[64] versionString;
123         client.getPublicVersion().toVST3VersionString(versionString.ptr, 64);
124 
125         string vst3Category;
126         final switch(client.pluginCategory()) with (PluginCategory)
127         {
128             case effectAnalysisAndMetering: vst3Category = PlugType.kFxAnalyzer; break;
129             case effectDelay:               vst3Category = PlugType.kFxDelay; break;
130             case effectDistortion:          vst3Category = PlugType.kFxDistortion; break;
131             case effectDynamics:            vst3Category = PlugType.kFxDynamics; break;
132             case effectEQ:                  vst3Category = PlugType.kFxEQ; break;
133             case effectImaging:             vst3Category = PlugType.kFxSpatial; break;
134             case effectModulation:          vst3Category = PlugType.kFxModulation; break;
135             case effectPitch:               vst3Category = PlugType.kFxPitchShift; break;
136             case effectReverb:              vst3Category = PlugType.kFxReverb; break;
137             case effectOther:               vst3Category = PlugType.kFx; break;
138             case instrumentDrums:           vst3Category = PlugType.kInstrumentDrum; break;
139             case instrumentSampler:         vst3Category = PlugType.kInstrumentSampler; break;
140             case instrumentSynthesizer:     vst3Category = PlugType.kInstrumentSynth; break;
141             case instrumentOther:           vst3Category = PlugType.kInstrumentSynth; break;
142             case invalid:                   assert(false);
143         }
144 
145         PClassInfo2 componentClass = PClassInfo2(classId,
146                                                  PClassInfo.kManyInstances, // cardinality
147                                                  kVstAudioEffectClass.ptr,
148                                                  pluginNameZ,
149                                                  kSimpleModeSupported,
150                                                  vst3Category.ptr,
151                                                  vendorNameZ,
152                                                  versionString.ptr,
153                                                  kVstVersionString.ptr);
154         pluginFactory.registerClass(&componentClass, &(createVST3Client!ClientClass));
155     }
156     else
157         gPluginFactory.addRef();
158 
159     return gPluginFactory;
160 }
161 
162 // must return a IAudioProcessor
163 extern(C) FUnknown createVST3Client(ClientClass)(void* useless) nothrow @nogc
164 {
165     ScopedForeignCallback!(false, true) scopedCallback;
166     scopedCallback.enter();
167     ClientClass client = mallocNew!ClientClass();
168     VST3Client plugin = mallocNew!VST3Client(client);
169     return plugin;
170 }