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 // This destructor must handle a partially constructed object! 93 ~this() 94 { 95 // close plugin 96 if (_aeffect !is null) 97 { 98 // Cannot close VST plugin while not suspended 99 // This is a programming error. 100 assert (_suspended); 101 102 // close plugin 103 _dispatcher(_aeffect, effClose, 0, 0, null, 0.0f); 104 105 // remove mapping 106 // TODO Is this safe though? What if the host is still calling audio processing? 107 _aeffect.resvd2 = 0; 108 109 _aeffect = null; 110 } 111 112 _lib.unload(); 113 } 114 115 override void setParameter(int paramIndex, float normalizedValue) 116 { 117 _aeffect.setParameter(_aeffect, paramIndex, normalizedValue); 118 } 119 120 override float getParameter(int paramIndex) 121 { 122 return _aeffect.getParameter(_aeffect, paramIndex); 123 } 124 125 override const(char)[] getParameterName(int paramIndex) 126 { 127 char* buf = &_parameterNames[33 * paramIndex]; 128 _dispatcher(_aeffect, effGetParamName, paramIndex, 0, buf, 0.0f); 129 return buf[0..strlen(buf)]; 130 } 131 132 override int getParameterCount() 133 { 134 return _aeffect.numParams; 135 } 136 137 override const(char)[] getVendorString() 138 { 139 _dispatcher(_aeffect, effGetVendorString, 0, 0, _vendorString.ptr, 0.0f); 140 return _vendorString[0..strlen(_vendorString.ptr)]; 141 } 142 143 override const(char)[] getEffectName() 144 { 145 _dispatcher(_aeffect, effGetEffectName, 0, 0, _effectName.ptr, 0.0f); 146 return _effectName[0..strlen(_effectName.ptr)]; 147 } 148 149 override const(char)[] getProductString() 150 { 151 _dispatcher(_aeffect, effGetProductString, 0, 0, _productString.ptr, 0.0f); 152 return _productString[0..strlen(_productString.ptr)]; 153 } 154 155 override void processAudioFloat(float** inputs, float** outputs, int samples) 156 { 157 assert (!_suspended); 158 _aeffect.processReplacing(_aeffect, inputs, outputs, samples); 159 _processedSamples += samples; 160 } 161 162 override void beginAudioProcessing() 163 { 164 _dispatcher(_aeffect, effMainsChanged, 0, 1, null, 0.0f); 165 _suspended = false; 166 updateLatency(); 167 } 168 169 override void endAudioProcessing() 170 { 171 _dispatcher(_aeffect, effMainsChanged, 0, 0, null, 0.0f); 172 _suspended = true; 173 } 174 175 override bool setIO(int numInputs, int numOutputs) 176 { 177 assert(numInputs <= 8 && numOutputs <= 8); 178 VstSpeakerArrangement pInputArr, pOutputArr; 179 memset(&pInputArr, 0, pInputArr.sizeof); 180 memset(&pOutputArr, 0, pOutputArr.sizeof); 181 pInputArr.type = kSpeakerArrEmpty; 182 pOutputArr.type = kSpeakerArrEmpty; 183 pInputArr.numChannels = numInputs; 184 pOutputArr.numChannels = numOutputs; 185 186 size_t value = cast(size_t)(&pInputArr); 187 void* ptr = cast(void*)(&pOutputArr); 188 _dispatcher(_aeffect, effSetSpeakerArrangement, 0, value, ptr, 0.0f); 189 190 // Dplug Issue #186: effSetSpeakerArrangement always says no 191 // so we return "yes" here, compounded bug 192 return true; 193 } 194 195 override void setSampleRate(float sampleRate) 196 { 197 assert(_suspended); // FUTURE: report success or error 198 _dispatcher(_aeffect, effSetSampleRate, 0, 0, null, sampleRate); 199 } 200 201 override void setMaxBufferSize(int samples) 202 { 203 assert(_suspended); // FUTURE: report success or error 204 _dispatcher(_aeffect, effSetBlockSize, 0, cast(VstIntPtr)samples, null, 0.0f); 205 } 206 207 override void loadPreset(int presetIndex) 208 { 209 _dispatcher(_aeffect, effSetProgram, 0, cast(ptrdiff_t)(presetIndex), null, 0.0f); 210 } 211 212 override void openUI(void* windowHandle) 213 { 214 _dispatcher(_aeffect, effEditOpen, 0, 0, windowHandle, 0.0f); 215 } 216 217 override void closeUI() 218 { 219 _dispatcher(_aeffect, effEditClose, 0, 0, null, 0.0f); 220 } 221 222 override int[2] getUISize() 223 { 224 ERect* rect; 225 _dispatcher(_aeffect, effEditGetRect, 0, 0, &rect, 0.0f); 226 int[2] size; 227 size[0] = rect.right - rect.left; 228 size[1] = rect.bottom - rect.top; 229 return size; 230 } 231 232 override const(ubyte)[] saveState() 233 { 234 if (_aeffect.flags && effFlagsProgramChunks) 235 { 236 ubyte* pChunk = null; 237 VstIntPtr size = _dispatcher(_aeffect, effGetChunk, 0 /* want a bank */, 0, &pChunk, 0.0f); 238 239 if (size == 0 || pChunk == null) 240 return null; // bug in client, effGetChunk returned an empty chunk 241 242 // Local copy 243 _lastStateChunkOutput.resize(size); 244 _lastStateChunkOutput[][0..size] = pChunk[0..size]; 245 246 return _lastStateChunkOutput[]; 247 } 248 else 249 return null; 250 251 } 252 253 override bool restoreState(const(ubyte)[] chunk) 254 { 255 if (chunk is null) 256 return false; 257 size_t size = chunk.length; 258 259 _lastStateChunkInput.resize(size); 260 _lastStateChunkInput[][0..size] = chunk[0..size]; 261 262 VstIntPtr result = _dispatcher(_aeffect, 263 effSetChunk, 264 0 /* want a bank */, 265 _lastStateChunkInput.length, 266 _lastStateChunkInput.ptr, 267 0.0f); 268 if (result != 1) 269 return false; // effSetChunk failed 270 else 271 return true; 272 } 273 274 override int getCurrentProgram() 275 { 276 return cast(int)( _dispatcher(_aeffect, effGetProgram, 0, 0, null, 0.0f) ); 277 } 278 279 override int getLatencySamples() 280 { 281 return _currentLatencySamples; 282 } 283 284 override double getTailSizeInSeconds() 285 { 286 double r = cast(double) _dispatcher(_aeffect, effGetTailSize, 0, 0, null, 0.0f); 287 return r; 288 } 289 290 private: 291 SharedLib _lib; 292 AEffect* _aeffect; 293 AEffectDispatcherProc _dispatcher; 294 long _processedSamples; 295 VstTimeInfo timeInfo; 296 bool _suspended = true; 297 int _currentLatencySamples; 298 299 char[] _parameterNames; // 33 x paramlength names. 300 char[65] _productString; 301 char[65] _vendorString; 302 char[65] _effectName; 303 304 // Using Vec to avoid allocating down. 305 Vec!ubyte _lastStateChunkOutput; 306 Vec!ubyte _lastStateChunkInput; 307 308 void updateLatency() 309 { 310 _currentLatencySamples = _aeffect.initialDelay; 311 } 312 } 313 314 extern(C) nothrow @nogc VstIntPtr hostCallback(AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) 315 { 316 import core.stdc.stdio; 317 318 // unimplemented stuff will printf 319 320 switch(opcode) 321 { 322 case audioMasterAutomate: printf("audioMasterAutomate\n"); return 0; 323 case audioMasterVersion: return 2400; 324 case audioMasterCurrentId: printf("audioMasterCurrentId\n"); return 0; 325 case audioMasterIdle: printf("audioMasterIdle\n"); return 0; 326 case DEPRECATED_audioMasterPinConnected: printf("DEPRECATED_audioMasterPinConnected\n"); return 0; 327 case DEPRECATED_audioMasterWantMidi: return 0; 328 case audioMasterGetTime: 329 { 330 VST2PluginHost phost = cast(VST2PluginHost) cast(void*) effect.resvd2; 331 if (!phost) 332 return 0; 333 334 phost.timeInfo.samplePos = phost._processedSamples; 335 phost.timeInfo.flags = 0; 336 337 return cast(VstIntPtr)(&phost.timeInfo); 338 } 339 case audioMasterProcessEvents: printf("audioMasterProcessEvents\n"); return 0; 340 case DEPRECATED_audioMasterSetTime: printf("DEPRECATED_audioMasterSetTime\n"); return 0; 341 case DEPRECATED_audioMasterTempoAt: printf("DEPRECATED_audioMasterTempoAt\n"); return 0; 342 case DEPRECATED_audioMasterGetNumAutomatableParameters: printf("DEPRECATED_audioMasterGetNumAutomatableParameters\n"); return 0; 343 case DEPRECATED_audioMasterGetParameterQuantization: printf("DEPRECATED_audioMasterGetParameterQuantization\n"); return 0; 344 case audioMasterIOChanged: printf("audioMasterIOChanged\n"); return 0; 345 case DEPRECATED_audioMasterNeedIdle: printf("DEPRECATED_audioMasterNeedIdle\n"); return 0; 346 case audioMasterSizeWindow: printf("audioMasterSizeWindow\n"); return 0; 347 case audioMasterGetSampleRate: printf("audioMasterGetSampleRate\n"); return 0; 348 case audioMasterGetBlockSize: printf("audioMasterGetBlockSize\n"); return 0; 349 case audioMasterGetInputLatency: printf("audioMasterGetInputLatency\n"); return 0; 350 case audioMasterGetOutputLatency: printf("audioMasterGetOutputLatency\n"); return 0; 351 case DEPRECATED_audioMasterGetPreviousPlug: printf("DEPRECATED_audioMasterGetPreviousPlug\n"); return 0; 352 case DEPRECATED_audioMasterGetNextPlug: printf("DEPRECATED_audioMasterGetNextPlug\n"); return 0; 353 case DEPRECATED_audioMasterWillReplaceOrAccumulate: printf("DEPRECATED_audioMasterWillReplaceOrAccumulate\n"); return 0; 354 355 case audioMasterGetCurrentProcessLevel: 356 return 2; /* kVstProcessLevelRealtime */ 357 358 case audioMasterGetAutomationState: printf("audioMasterGetAutomationState\n"); return 0; 359 case audioMasterOfflineStart: printf("audioMasterOfflineStart\n"); return 0; 360 case audioMasterOfflineRead: printf("audioMasterOfflineRead\n"); return 0; 361 case audioMasterOfflineWrite: printf("audioMasterOfflineWrite\n"); return 0; 362 case audioMasterOfflineGetCurrentPass: printf("audioMasterOfflineGetCurrentPass\n"); return 0; 363 case audioMasterOfflineGetCurrentMetaPass: printf("audioMasterOfflineGetCurrentMetaPass\n"); return 0; 364 case DEPRECATED_audioMasterSetOutputSampleRate: printf("DEPRECATED_audioMasterSetOutputSampleRate\n"); return 0; 365 case DEPRECATED_audioMasterGetOutputSpeakerArrangement: printf("DEPRECATED_audioMasterGetOutputSpeakerArrangement\n"); return 0; 366 367 case audioMasterGetVendorString: 368 case audioMasterGetProductString: 369 { 370 char* p = cast(char*)ptr; 371 if (p !is null) 372 stringNCopy(p, 64, "Dplug host"); 373 return 0; 374 } 375 376 case audioMasterGetVendorVersion: return 0x200; // 2.0 377 378 case audioMasterVendorSpecific: printf("audioMasterVendorSpecific\n"); return 0; 379 case DEPRECATED_audioMasterSetIcon: printf("DEPRECATED_audioMasterSetIcon\n"); return 0; 380 case audioMasterCanDo: printf("audioMasterCanDo\n"); return 0; 381 case audioMasterGetLanguage: printf("audioMasterGetLanguage\n"); return 0; 382 case DEPRECATED_audioMasterOpenWindow: printf("DEPRECATED_audioMasterOpenWindow\n"); return 0; 383 case DEPRECATED_audioMasterCloseWindow: printf("DEPRECATED_audioMasterCloseWindow\n"); return 0; 384 case audioMasterGetDirectory: printf("audioMasterGetDirectory\n"); return 0; 385 case audioMasterUpdateDisplay: printf("audioMasterUpdateDisplay\n"); return 0; 386 case audioMasterBeginEdit: printf("audioMasterBeginEdit\n"); return 0; 387 case audioMasterEndEdit: printf("audioMasterEndEdit\n"); return 0; 388 case audioMasterOpenFileSelector: printf("audioMasterOpenFileSelector\n"); return 0; 389 case audioMasterCloseFileSelector: printf("audioMasterCloseFileSelector\n"); return 0; 390 case DEPRECATED_audioMasterEditFile: printf("DEPRECATED_audioMasterEditFile\n"); return 0; 391 case DEPRECATED_audioMasterGetChunkFile: printf("DEPRECATED_audioMasterGetChunkFile\n"); return 0; 392 case DEPRECATED_audioMasterGetInputSpeakerArrangement: printf("DEPRECATED_audioMasterGetInputSpeakerArrangement\n"); return 0; 393 default: printf(" unknown opcode %d\n", opcode); return 0; 394 } 395 }