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 }