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: strlen, memset; 10 import dplug.core.sharedlib; 11 import dplug.core.nogc; 12 import dplug.core.vec; 13 import dplug.host.host; 14 import dplug.vst2; 15 16 nothrow @nogc: 17 18 alias VSTPluginMain_t = extern(C) void* function(void* fun); 19 20 /// Returns: a VST2 entry point, or `null` in case of errors. 21 VSTPluginMain_t getVST2EntryPoint(ref SharedLib lib) 22 { 23 void* result = null; 24 25 void tryEntryPoint(string name) 26 { 27 if (result != null) 28 return; 29 30 if (lib.hasSymbol(name)) 31 result = lib.loadSymbol(name); 32 else 33 result = null; 34 } 35 tryEntryPoint("VSTPluginMain"); 36 tryEntryPoint("main_macho"); 37 tryEntryPoint("main"); 38 39 if (result == null) 40 return null; // Did not find a VST entry point 41 else 42 return cast(VSTPluginMain_t)result; 43 } 44 45 version(VST2): 46 47 final class VST2PluginHost : IPluginHost 48 { 49 nothrow @nogc: 50 51 // Note: in case of error, it's OK the object will be partially constructed and 52 // its destructor can be called. 53 this(SharedLibHandle lib, bool* err) 54 { 55 *err = true; 56 _lib.initializeWithHandle(lib); 57 58 VSTPluginMain_t VSTPluginMain = getVST2EntryPoint(_lib); 59 if (VSTPluginMain is null) 60 return; 61 62 HostCallbackFunction hostFun = &hostCallback; 63 64 AEffect* aeffect = cast(AEffect*) VSTPluginMain(hostFun); 65 66 // various checks 67 if (aeffect.magic != 0x56737450) /* 'VstP' */ 68 return; // Wrong VST magic number 69 if (aeffect.dispatcher == null) 70 return; // aeffect.dispatcher is null 71 if (aeffect.setParameter == null) 72 return; // aeffect.setParameter is null 73 if (aeffect.getParameter == null) 74 return; // aeffect.getParameter is null 75 76 // aeffect passed those basic checks 77 _aeffect = aeffect; 78 _aeffect.resvd2 = cast(size_t) cast(void*) this; 79 80 _dispatcher = _aeffect.dispatcher; 81 82 // open plugin 83 _dispatcher(_aeffect, effOpen, 0, 0, null, 0.0f); 84 _parameterNames.reallocBuffer(33 * _aeffect.numParams); 85 86 // get initial latency 87 updateLatency(); 88 89 *err = false; 90 } 91 92 override void close() 93 { 94 // not used anymore (remove in Dplug v15), destructor does this instead 95 } 96 97 // This destructor must handle a partially constructed object! 98 ~this() 99 { 100 // close plugin 101 if (_aeffect !is null) 102 { 103 // Cannot close VST plugin while not suspended 104 // This is a programming error. 105 assert (_suspended); 106 107 // close plugin 108 _dispatcher(_aeffect, effClose, 0, 0, null, 0.0f); 109 110 // remove mapping 111 // TODO Is this safe though? What if the host is still calling audio processing? 112 _aeffect.resvd2 = 0; 113 114 _aeffect = null; 115 } 116 117 _lib.unload(); 118 } 119 120 override void setParameter(int paramIndex, float normalizedValue) 121 { 122 _aeffect.setParameter(_aeffect, paramIndex, normalizedValue); 123 } 124 125 override float getParameter(int paramIndex) 126 { 127 return _aeffect.getParameter(_aeffect, paramIndex); 128 } 129 130 override const(char)[] getParameterName(int paramIndex) 131 { 132 char* buf = &_parameterNames[33 * paramIndex]; 133 _dispatcher(_aeffect, effGetParamName, paramIndex, 0, buf, 0.0f); 134 return buf[0..strlen(buf)]; 135 } 136 137 override int getParameterCount() 138 { 139 return _aeffect.numParams; 140 } 141 142 override const(char)[] getVendorString() 143 { 144 _dispatcher(_aeffect, effGetVendorString, 0, 0, _vendorString.ptr, 0.0f); 145 return _vendorString[0..strlen(_vendorString.ptr)]; 146 } 147 148 override const(char)[] getEffectName() 149 { 150 _dispatcher(_aeffect, effGetEffectName, 0, 0, _effectName.ptr, 0.0f); 151 return _effectName[0..strlen(_effectName.ptr)]; 152 } 153 154 override const(char)[] getProductString() 155 { 156 _dispatcher(_aeffect, effGetProductString, 0, 0, _productString.ptr, 0.0f); 157 return _productString[0..strlen(_productString.ptr)]; 158 } 159 160 override void processAudioFloat(float** inputs, float** outputs, int samples) 161 { 162 assert (!_suspended); 163 _aeffect.processReplacing(_aeffect, inputs, outputs, samples); 164 _processedSamples += samples; 165 } 166 167 override void beginAudioProcessing() 168 { 169 _dispatcher(_aeffect, effMainsChanged, 0, 1, null, 0.0f); 170 _suspended = false; 171 updateLatency(); 172 } 173 174 override void endAudioProcessing() 175 { 176 _dispatcher(_aeffect, effMainsChanged, 0, 0, null, 0.0f); 177 _suspended = true; 178 } 179 180 override bool setIO(int numInputs, int numOutputs) 181 { 182 assert(numInputs <= 8 && numOutputs <= 8); 183 VstSpeakerArrangement pInputArr, pOutputArr; 184 memset(&pInputArr, 0, pInputArr.sizeof); 185 memset(&pOutputArr, 0, pOutputArr.sizeof); 186 pInputArr.type = kSpeakerArrEmpty; 187 pOutputArr.type = kSpeakerArrEmpty; 188 pInputArr.numChannels = numInputs; 189 pOutputArr.numChannels = numOutputs; 190 191 size_t value = cast(size_t)(&pInputArr); 192 void* ptr = cast(void*)(&pOutputArr); 193 _dispatcher(_aeffect, effSetSpeakerArrangement, 0, value, ptr, 0.0f); 194 195 // Dplug Issue #186: effSetSpeakerArrangement always says no 196 // so we return "yes" here, compounded bug 197 return true; 198 } 199 200 override void setSampleRate(float sampleRate) 201 { 202 assert(_suspended); // FUTURE: report success or error 203 _dispatcher(_aeffect, effSetSampleRate, 0, 0, null, sampleRate); 204 } 205 206 override void setMaxBufferSize(int samples) 207 { 208 assert(_suspended); // FUTURE: report success or error 209 _dispatcher(_aeffect, effSetBlockSize, 0, cast(VstIntPtr)samples, null, 0.0f); 210 } 211 212 override void loadPreset(int presetIndex) 213 { 214 _dispatcher(_aeffect, effSetProgram, 0, cast(ptrdiff_t)(presetIndex), null, 0.0f); 215 } 216 217 override void openUI(void* windowHandle) 218 { 219 _dispatcher(_aeffect, effEditOpen, 0, 0, windowHandle, 0.0f); 220 } 221 222 override void closeUI() 223 { 224 _dispatcher(_aeffect, effEditClose, 0, 0, null, 0.0f); 225 } 226 227 override int[2] getUISize() 228 { 229 ERect* rect; 230 _dispatcher(_aeffect, effEditGetRect, 0, 0, &rect, 0.0f); 231 int[2] size; 232 size[0] = rect.right - rect.left; 233 size[1] = rect.bottom - rect.top; 234 return size; 235 } 236 237 override const(ubyte)[] saveState() 238 { 239 if (_aeffect.flags && effFlagsProgramChunks) 240 { 241 ubyte* pChunk = null; 242 VstIntPtr size = _dispatcher(_aeffect, effGetChunk, 0 /* want a bank */, 0, &pChunk, 0.0f); 243 244 if (size == 0 || pChunk == null) 245 return null; // bug in client, effGetChunk returned an empty chunk 246 247 // Local copy 248 _lastStateChunkOutput.resize(size); 249 _lastStateChunkOutput[][0..size] = pChunk[0..size]; 250 251 return _lastStateChunkOutput[]; 252 } 253 else 254 return null; 255 256 } 257 258 override bool restoreState(const(ubyte)[] chunk) 259 { 260 if (chunk is null) 261 return false; 262 size_t size = chunk.length; 263 264 _lastStateChunkInput.resize(size); 265 _lastStateChunkInput[][0..size] = chunk[0..size]; 266 267 VstIntPtr result = _dispatcher(_aeffect, 268 effSetChunk, 269 0 /* want a bank */, 270 _lastStateChunkInput.length, 271 _lastStateChunkInput.ptr, 272 0.0f); 273 if (result != 1) 274 return false; // effSetChunk failed 275 else 276 return true; 277 } 278 279 override int getCurrentProgram() 280 { 281 return cast(int)( _dispatcher(_aeffect, effGetProgram, 0, 0, null, 0.0f) ); 282 } 283 284 override int getLatencySamples() 285 { 286 return _currentLatencySamples; 287 } 288 289 override double getTailSizeInSeconds() 290 { 291 double r = cast(double) _dispatcher(_aeffect, effGetTailSize, 0, 0, null, 0.0f); 292 return r; 293 } 294 295 private: 296 SharedLib _lib; 297 AEffect* _aeffect; 298 AEffectDispatcherProc _dispatcher; 299 long _processedSamples; 300 VstTimeInfo timeInfo; 301 bool _suspended = true; 302 int _currentLatencySamples; 303 304 char[] _parameterNames; // 33 x paramlength names. 305 char[65] _productString; 306 char[65] _vendorString; 307 char[65] _effectName; 308 309 // Using Vec to avoid allocating down. 310 Vec!ubyte _lastStateChunkOutput; 311 Vec!ubyte _lastStateChunkInput; 312 313 void updateLatency() 314 { 315 _currentLatencySamples = _aeffect.initialDelay; 316 } 317 } 318 319 extern(C) nothrow @nogc VstIntPtr hostCallback(AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) 320 { 321 import core.stdc.stdio; 322 323 // unimplemented stuff will printf 324 325 switch(opcode) 326 { 327 case audioMasterAutomate: printf("audioMasterAutomate\n"); return 0; 328 case audioMasterVersion: return 2400; 329 case audioMasterCurrentId: printf("audioMasterCurrentId\n"); return 0; 330 case audioMasterIdle: printf("audioMasterIdle\n"); return 0; 331 case DEPRECATED_audioMasterPinConnected: printf("DEPRECATED_audioMasterPinConnected\n"); return 0; 332 case DEPRECATED_audioMasterWantMidi: return 0; 333 case audioMasterGetTime: 334 { 335 VST2PluginHost phost = cast(VST2PluginHost) cast(void*) effect.resvd2; 336 if (!phost) 337 return 0; 338 339 phost.timeInfo.samplePos = phost._processedSamples; 340 phost.timeInfo.flags = 0; 341 342 return cast(VstIntPtr)(&phost.timeInfo); 343 } 344 case audioMasterProcessEvents: printf("audioMasterProcessEvents\n"); return 0; 345 case DEPRECATED_audioMasterSetTime: printf("DEPRECATED_audioMasterSetTime\n"); return 0; 346 case DEPRECATED_audioMasterTempoAt: printf("DEPRECATED_audioMasterTempoAt\n"); return 0; 347 case DEPRECATED_audioMasterGetNumAutomatableParameters: printf("DEPRECATED_audioMasterGetNumAutomatableParameters\n"); return 0; 348 case DEPRECATED_audioMasterGetParameterQuantization: printf("DEPRECATED_audioMasterGetParameterQuantization\n"); return 0; 349 case audioMasterIOChanged: printf("audioMasterIOChanged\n"); return 0; 350 case DEPRECATED_audioMasterNeedIdle: printf("DEPRECATED_audioMasterNeedIdle\n"); return 0; 351 case audioMasterSizeWindow: printf("audioMasterSizeWindow\n"); return 0; 352 case audioMasterGetSampleRate: printf("audioMasterGetSampleRate\n"); return 0; 353 case audioMasterGetBlockSize: printf("audioMasterGetBlockSize\n"); return 0; 354 case audioMasterGetInputLatency: printf("audioMasterGetInputLatency\n"); return 0; 355 case audioMasterGetOutputLatency: printf("audioMasterGetOutputLatency\n"); return 0; 356 case DEPRECATED_audioMasterGetPreviousPlug: printf("DEPRECATED_audioMasterGetPreviousPlug\n"); return 0; 357 case DEPRECATED_audioMasterGetNextPlug: printf("DEPRECATED_audioMasterGetNextPlug\n"); return 0; 358 case DEPRECATED_audioMasterWillReplaceOrAccumulate: printf("DEPRECATED_audioMasterWillReplaceOrAccumulate\n"); return 0; 359 360 case audioMasterGetCurrentProcessLevel: 361 return 2; /* kVstProcessLevelRealtime */ 362 363 case audioMasterGetAutomationState: printf("audioMasterGetAutomationState\n"); return 0; 364 case audioMasterOfflineStart: printf("audioMasterOfflineStart\n"); return 0; 365 case audioMasterOfflineRead: printf("audioMasterOfflineRead\n"); return 0; 366 case audioMasterOfflineWrite: printf("audioMasterOfflineWrite\n"); return 0; 367 case audioMasterOfflineGetCurrentPass: printf("audioMasterOfflineGetCurrentPass\n"); return 0; 368 case audioMasterOfflineGetCurrentMetaPass: printf("audioMasterOfflineGetCurrentMetaPass\n"); return 0; 369 case DEPRECATED_audioMasterSetOutputSampleRate: printf("DEPRECATED_audioMasterSetOutputSampleRate\n"); return 0; 370 case DEPRECATED_audioMasterGetOutputSpeakerArrangement: printf("DEPRECATED_audioMasterGetOutputSpeakerArrangement\n"); return 0; 371 372 case audioMasterGetVendorString: 373 case audioMasterGetProductString: 374 { 375 char* p = cast(char*)ptr; 376 if (p !is null) 377 stringNCopy(p, 64, "Dplug host"); 378 return 0; 379 } 380 381 case audioMasterGetVendorVersion: return 0x200; // 2.0 382 383 case audioMasterVendorSpecific: printf("audioMasterVendorSpecific\n"); return 0; 384 case DEPRECATED_audioMasterSetIcon: printf("DEPRECATED_audioMasterSetIcon\n"); return 0; 385 case audioMasterCanDo: printf("audioMasterCanDo\n"); return 0; 386 case audioMasterGetLanguage: printf("audioMasterGetLanguage\n"); return 0; 387 case DEPRECATED_audioMasterOpenWindow: printf("DEPRECATED_audioMasterOpenWindow\n"); return 0; 388 case DEPRECATED_audioMasterCloseWindow: printf("DEPRECATED_audioMasterCloseWindow\n"); return 0; 389 case audioMasterGetDirectory: printf("audioMasterGetDirectory\n"); return 0; 390 case audioMasterUpdateDisplay: printf("audioMasterUpdateDisplay\n"); return 0; 391 case audioMasterBeginEdit: printf("audioMasterBeginEdit\n"); return 0; 392 case audioMasterEndEdit: printf("audioMasterEndEdit\n"); return 0; 393 case audioMasterOpenFileSelector: printf("audioMasterOpenFileSelector\n"); return 0; 394 case audioMasterCloseFileSelector: printf("audioMasterCloseFileSelector\n"); return 0; 395 case DEPRECATED_audioMasterEditFile: printf("DEPRECATED_audioMasterEditFile\n"); return 0; 396 case DEPRECATED_audioMasterGetChunkFile: printf("DEPRECATED_audioMasterGetChunkFile\n"); return 0; 397 case DEPRECATED_audioMasterGetInputSpeakerArrangement: printf("DEPRECATED_audioMasterGetInputSpeakerArrangement\n"); return 0; 398 default: printf(" unknown opcode %d\n", opcode); return 0; 399 } 400 }