1 /** 2 VST 2.4 host implementation. 3 4 Copyright: Auburn Sounds 2015-2016. 5 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 */ 7 module dplug.host.vst2; 8 9 import core.stdc.string; 10 11 import std.string; 12 import std.algorithm.mutation; 13 14 import dplug.core.sharedlib; 15 import dplug.core.nogc; 16 import dplug.host.host; 17 import dplug.vst2; 18 19 20 alias VSTPluginMain_t = extern(C) void* function(void* fun); 21 22 VSTPluginMain_t getVST2EntryPoint(ref SharedLib lib) 23 { 24 void* result = null; 25 26 void tryEntryPoint(string name) 27 { 28 if (result != null) 29 return; 30 31 if (lib.hasSymbol(name)) 32 result = lib.loadSymbol(name); 33 else 34 result = null; 35 } 36 tryEntryPoint("VSTPluginMain"); 37 tryEntryPoint("main_macho"); 38 tryEntryPoint("main"); 39 40 if (result == null) 41 throw new Exception("Did not find a VST entry point"); 42 else 43 return cast(VSTPluginMain_t)result; 44 } 45 46 version(VST2): 47 48 private __gshared VST2PluginHost[AEffect*] reverseMapping; 49 50 final class VST2PluginHost : IPluginHost 51 { 52 this(SharedLib lib) 53 { 54 _lib = move(lib); 55 56 VSTPluginMain_t VSTPluginMain = getVST2EntryPoint(_lib); 57 HostCallbackFunction hostFun = &hostCallback; 58 59 _aeffect = cast(AEffect*) VSTPluginMain(hostFun); 60 61 reverseMapping[_aeffect] = this; 62 63 // various checks 64 if (_aeffect.magic != 0x56737450) /* 'VstP' */ 65 throw new Exception("Wrong VST magic number"); 66 if (_aeffect.dispatcher == null) 67 throw new Exception("aeffect.dispatcher is null"); 68 if (_aeffect.setParameter == null) 69 throw new Exception("aeffect.setParameter is null"); 70 if (_aeffect.getParameter == null) 71 throw new Exception("aeffect.getParameter is null"); 72 73 _dispatcher = _aeffect.dispatcher; 74 75 // open plugin 76 _dispatcher(_aeffect, effOpen, 0, 0, null, 0.0f); 77 78 // get initial latency 79 updateLatency(); 80 } 81 82 override void setParameter(int paramIndex, float normalizedValue) 83 { 84 _aeffect.setParameter(_aeffect, paramIndex, normalizedValue); 85 } 86 87 override float getParameter(int paramIndex) 88 { 89 return _aeffect.getParameter(_aeffect, paramIndex); 90 } 91 92 override const(char)[] getParameterName(int paramIndex) 93 { 94 char[33] buf; 95 _dispatcher(_aeffect, effGetParamName, paramIndex, 0, buf.ptr, 0.0f); 96 return fromStringz(buf.ptr).idup; 97 } 98 99 override int getParameterCount() 100 { 101 return _aeffect.numParams; 102 } 103 104 override void close() 105 { 106 if (!_suspended) 107 throw new Exception("Cannot close VST plugin while not suspended"); 108 109 // close plugin 110 _dispatcher(_aeffect, effClose, 0, 0, null, 0.0f); 111 112 // TODO: disabled because this breaks the second time the DLL is unloaded on Windows 113 // unload dynlib 114 // Note: probably fixed now 115 // _lib.unload(); 116 } 117 118 override string getVendorString() 119 { 120 char[65] buf; 121 _dispatcher(_aeffect, effGetVendorString, 0, 0, buf.ptr, 0.0f); 122 return fromStringz(buf.ptr).idup; 123 } 124 125 override string getEffectName() 126 { 127 char[65] buf; 128 _dispatcher(_aeffect, effGetEffectName, 0, 0, buf.ptr, 0.0f); 129 return fromStringz(buf.ptr).idup; 130 } 131 132 override string getProductString() 133 { 134 char[65] buf; 135 _dispatcher(_aeffect, effGetProductString, 0, 0, buf.ptr, 0.0f); 136 return fromStringz(buf.ptr).idup; 137 } 138 139 override void processAudioFloat(float** inputs, float** outputs, int samples) 140 { 141 if (_suspended) 142 throw new Exception("Cannot process audio with VST plugin while suspended"); 143 _aeffect.processReplacing(_aeffect, inputs, outputs, samples); 144 _processedSamples += samples; 145 } 146 147 override void beginAudioProcessing() 148 { 149 _dispatcher(_aeffect, effMainsChanged, 0, 1, null, 0.0f); 150 _suspended = false; 151 updateLatency(); 152 } 153 154 override void endAudioProcessing() 155 { 156 _dispatcher(_aeffect, effMainsChanged, 0, 0, null, 0.0f); 157 _suspended = true; 158 } 159 160 override bool setIO(int numInputs, int numOutputs) 161 { 162 assert(numInputs <= 8 && numOutputs <= 8); 163 VstSpeakerArrangement pInputArr, pOutputArr; 164 memset(&pInputArr, 0, pInputArr.sizeof); 165 memset(&pOutputArr, 0, pOutputArr.sizeof); 166 pInputArr.type = kSpeakerArrEmpty; 167 pOutputArr.type = kSpeakerArrEmpty; 168 pInputArr.numChannels = numInputs; 169 pOutputArr.numChannels = numOutputs; 170 171 size_t value = cast(size_t)(&pInputArr); 172 void* ptr = cast(void*)(&pOutputArr); 173 _dispatcher(_aeffect, effSetSpeakerArrangement, 0, value, ptr, 0.0f); 174 175 // Dplug Issue #186: effSetSpeakerArrangement always says no 176 // so we return "yes" here, compunded bug 177 return true; 178 } 179 180 override void setSampleRate(float sampleRate) 181 { 182 if (!_suspended) 183 throw new Exception("Cannot set sample rate of VST plugin while not suspended"); 184 _dispatcher(_aeffect, effSetSampleRate, 0, 0, null, sampleRate); 185 } 186 187 override void setMaxBufferSize(int samples) 188 { 189 if (!_suspended) 190 throw new Exception("Cannot set block sizeof VST plugin while not suspended"); 191 _dispatcher(_aeffect, effSetBlockSize, 0, cast(VstIntPtr)samples, null, 0.0f); 192 } 193 194 override void loadPreset(int presetIndex) 195 { 196 _dispatcher(_aeffect, effSetProgram, 0, cast(ptrdiff_t)(presetIndex), null, 0.0f); 197 } 198 199 override void openUI(void* windowHandle) 200 { 201 _dispatcher(_aeffect, effEditOpen, 0, 0, windowHandle, 0.0f); 202 } 203 204 override void closeUI() 205 { 206 _dispatcher(_aeffect, effEditClose, 0, 0, null, 0.0f); 207 } 208 209 override int[2] getUISize() 210 { 211 ERect* rect; 212 _dispatcher(_aeffect, effEditGetRect, 0, 0, &rect, 0.0f); 213 int[2] size; 214 size[0] = rect.right - rect.left; 215 size[1] = rect.bottom - rect.top; 216 return size; 217 } 218 219 override ubyte[] saveState() 220 { 221 if (_aeffect.flags && effFlagsProgramChunks) 222 { 223 ubyte* pChunk = null; 224 VstIntPtr size = _dispatcher(_aeffect, effGetChunk, 0 /* want a bank */, 0, &pChunk, 0.0f); 225 226 if (size == 0 || pChunk == null) 227 throw new Exception("effGetChunk returned an empty chunk"); 228 229 return pChunk[0..size].dup; 230 } 231 else 232 throw new Exception("This VST doesn't support chunks"); 233 234 } 235 236 override void restoreState(ubyte[] chunk) 237 { 238 VstIntPtr result = _dispatcher(_aeffect, effSetChunk, 0 /* want a bank */, chunk.length, chunk.ptr, 0.0f); 239 if (result != 1) 240 throw new Exception("effSetChunk failed"); 241 } 242 243 override int getCurrentProgram() 244 { 245 return cast(int)( _dispatcher(_aeffect, effGetProgram, 0, 0, null, 0.0f) ); 246 } 247 248 override int getLatencySamples() 249 { 250 return _currentLatencySamples; 251 } 252 253 override double getTailSizeInSeconds() 254 { 255 double r = cast(double) _dispatcher(_aeffect, effGetTailSize, 0, 0, null, 0.0f); 256 return r; 257 } 258 259 private: 260 SharedLib _lib; 261 AEffect* _aeffect; 262 AEffectDispatcherProc _dispatcher; 263 long _processedSamples; 264 VstTimeInfo timeInfo; 265 bool _suspended = true; 266 int _currentLatencySamples; 267 268 void updateLatency() 269 { 270 _currentLatencySamples = _aeffect.initialDelay; 271 } 272 } 273 274 extern(C) nothrow @nogc VstIntPtr hostCallback(AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) 275 { 276 import core.stdc.stdio; 277 278 // unimplemented stuff will printf 279 280 switch(opcode) 281 { 282 case audioMasterAutomate: printf("audioMasterAutomate\n"); return 0; 283 case audioMasterVersion: return 2400; 284 case audioMasterCurrentId: printf("audioMasterCurrentId\n"); return 0; 285 case audioMasterIdle: printf("audioMasterIdle\n"); return 0; 286 case DEPRECATED_audioMasterPinConnected: printf("DEPRECATED_audioMasterPinConnected\n"); return 0; 287 case DEPRECATED_audioMasterWantMidi: return 0; 288 case audioMasterGetTime: 289 { 290 VST2PluginHost* phost = effect in reverseMapping; 291 if (!phost) 292 return 0; 293 294 VST2PluginHost host = *phost; 295 host.timeInfo.samplePos = host._processedSamples; 296 host.timeInfo.flags = 0; 297 298 return cast(VstIntPtr)(&host.timeInfo); 299 } 300 case audioMasterProcessEvents: printf("audioMasterProcessEvents\n"); return 0; 301 case DEPRECATED_audioMasterSetTime: printf("DEPRECATED_audioMasterSetTime\n"); return 0; 302 case DEPRECATED_audioMasterTempoAt: printf("DEPRECATED_audioMasterTempoAt\n"); return 0; 303 case DEPRECATED_audioMasterGetNumAutomatableParameters: printf("DEPRECATED_audioMasterGetNumAutomatableParameters\n"); return 0; 304 case DEPRECATED_audioMasterGetParameterQuantization: printf("DEPRECATED_audioMasterGetParameterQuantization\n"); return 0; 305 case audioMasterIOChanged: printf("audioMasterIOChanged\n"); return 0; 306 case DEPRECATED_audioMasterNeedIdle: printf("DEPRECATED_audioMasterNeedIdle\n"); return 0; 307 case audioMasterSizeWindow: printf("audioMasterSizeWindow\n"); return 0; 308 case audioMasterGetSampleRate: printf("audioMasterGetSampleRate\n"); return 0; 309 case audioMasterGetBlockSize: printf("audioMasterGetBlockSize\n"); return 0; 310 case audioMasterGetInputLatency: printf("audioMasterGetInputLatency\n"); return 0; 311 case audioMasterGetOutputLatency: printf("audioMasterGetOutputLatency\n"); return 0; 312 case DEPRECATED_audioMasterGetPreviousPlug: printf("DEPRECATED_audioMasterGetPreviousPlug\n"); return 0; 313 case DEPRECATED_audioMasterGetNextPlug: printf("DEPRECATED_audioMasterGetNextPlug\n"); return 0; 314 case DEPRECATED_audioMasterWillReplaceOrAccumulate: printf("DEPRECATED_audioMasterWillReplaceOrAccumulate\n"); return 0; 315 316 case audioMasterGetCurrentProcessLevel: 317 return 2; /* kVstProcessLevelRealtime */ 318 319 case audioMasterGetAutomationState: printf("audioMasterGetAutomationState\n"); return 0; 320 case audioMasterOfflineStart: printf("audioMasterOfflineStart\n"); return 0; 321 case audioMasterOfflineRead: printf("audioMasterOfflineRead\n"); return 0; 322 case audioMasterOfflineWrite: printf("audioMasterOfflineWrite\n"); return 0; 323 case audioMasterOfflineGetCurrentPass: printf("audioMasterOfflineGetCurrentPass\n"); return 0; 324 case audioMasterOfflineGetCurrentMetaPass: printf("audioMasterOfflineGetCurrentMetaPass\n"); return 0; 325 case DEPRECATED_audioMasterSetOutputSampleRate: printf("DEPRECATED_audioMasterSetOutputSampleRate\n"); return 0; 326 case DEPRECATED_audioMasterGetOutputSpeakerArrangement: printf("DEPRECATED_audioMasterGetOutputSpeakerArrangement\n"); return 0; 327 328 case audioMasterGetVendorString: 329 case audioMasterGetProductString: 330 { 331 char* p = cast(char*)ptr; 332 if (p !is null) 333 stringNCopy(p, 64, "Dplug host"); 334 return 0; 335 } 336 337 case audioMasterGetVendorVersion: return 0x200; // 2.0 338 339 case audioMasterVendorSpecific: printf("audioMasterVendorSpecific\n"); return 0; 340 case DEPRECATED_audioMasterSetIcon: printf("DEPRECATED_audioMasterSetIcon\n"); return 0; 341 case audioMasterCanDo: printf("audioMasterCanDo\n"); return 0; 342 case audioMasterGetLanguage: printf("audioMasterGetLanguage\n"); return 0; 343 case DEPRECATED_audioMasterOpenWindow: printf("DEPRECATED_audioMasterOpenWindow\n"); return 0; 344 case DEPRECATED_audioMasterCloseWindow: printf("DEPRECATED_audioMasterCloseWindow\n"); return 0; 345 case audioMasterGetDirectory: printf("audioMasterGetDirectory\n"); return 0; 346 case audioMasterUpdateDisplay: printf("audioMasterUpdateDisplay\n"); return 0; 347 case audioMasterBeginEdit: printf("audioMasterBeginEdit\n"); return 0; 348 case audioMasterEndEdit: printf("audioMasterEndEdit\n"); return 0; 349 case audioMasterOpenFileSelector: printf("audioMasterOpenFileSelector\n"); return 0; 350 case audioMasterCloseFileSelector: printf("audioMasterCloseFileSelector\n"); return 0; 351 case DEPRECATED_audioMasterEditFile: printf("DEPRECATED_audioMasterEditFile\n"); return 0; 352 case DEPRECATED_audioMasterGetChunkFile: printf("DEPRECATED_audioMasterGetChunkFile\n"); return 0; 353 case DEPRECATED_audioMasterGetInputSpeakerArrangement: printf("DEPRECATED_audioMasterGetInputSpeakerArrangement\n"); return 0; 354 default: printf(" unknown opcode %d\n", opcode); return 0; 355 } 356 }