1 /** 2 * Copyright: Copyright Auburn Sounds 2015-2016 3 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 4 * Authors: Guillaume Piolat 5 */ 6 module dplug.host.vst; 7 8 import std.string; 9 10 import dplug.core.sharedlib; 11 import dplug.core.nogc; 12 import dplug.host.host; 13 import dplug.vst; 14 15 16 alias VSTPluginMain_t = extern(C) AEffect* function(HostCallbackFunction fun); 17 18 VSTPluginMain_t getVSTEntryPoint(SharedLib lib) 19 { 20 void* result = null; 21 22 void tryEntryPoint(string name) 23 { 24 if (result != null) 25 return; 26 try 27 { 28 result = lib.loadSymbol(name); 29 } 30 catch(Exception e) 31 { 32 result = null; 33 } 34 } 35 tryEntryPoint("VSTPluginMain"); 36 tryEntryPoint("main_macho"); 37 tryEntryPoint("main"); 38 39 if (result == null) 40 throw new Exception("Did not find a VST entry point"); 41 else 42 return cast(VSTPluginMain_t)result; 43 } 44 45 private __gshared VSTPluginHost[AEffect*] reverseMapping; 46 47 final class VSTPluginHost : IPluginHost 48 { 49 this(SharedLib lib) 50 { 51 _lib = lib; 52 53 VSTPluginMain_t VSTPluginMain = getVSTEntryPoint(lib); 54 55 _aeffect = VSTPluginMain(&hostCallback); 56 57 reverseMapping[_aeffect] = this; 58 59 // various checks 60 if (_aeffect.magic != kEffectMagic) 61 throw new Exception("Wrong VST magic number"); 62 if (_aeffect.dispatcher == null) 63 throw new Exception("aeffect.dispatcher is null"); 64 if (_aeffect.setParameter == null) 65 throw new Exception("aeffect.setParameter is null"); 66 if (_aeffect.getParameter == null) 67 throw new Exception("aeffect.getParameter is null"); 68 69 _dispatcher = _aeffect.dispatcher; 70 71 // open plugin 72 _dispatcher(_aeffect, effOpen, 0, 0, null, 0.0f); 73 } 74 75 override void setParameter(int paramIndex, float normalizedValue) 76 { 77 _aeffect.setParameter(_aeffect, paramIndex, normalizedValue); 78 } 79 80 override float getParameter(int paramIndex) 81 { 82 return _aeffect.getParameter(_aeffect, paramIndex); 83 } 84 85 override void close() 86 { 87 // close plugin 88 _dispatcher(_aeffect, effClose, 0, 0, null, 0.0f); 89 90 // TODO: disabled because this breaks the second time the DLL is unloaded on Windows 91 // unload dynlib 92 // Note: probably fixed now 93 // _lib.unload(); 94 } 95 96 override string getVendorString() 97 { 98 char[65] buf; 99 _dispatcher(_aeffect, effGetVendorString, 0, 0, buf.ptr, 0.0f); 100 return fromStringz(buf.ptr).idup; 101 } 102 103 override string getEffectName() 104 { 105 char[65] buf; 106 _dispatcher(_aeffect, effGetEffectName, 0, 0, buf.ptr, 0.0f); 107 return fromStringz(buf.ptr).idup; 108 } 109 110 override string getProductString() 111 { 112 char[65] buf; 113 _dispatcher(_aeffect, effGetProductString, 0, 0, buf.ptr, 0.0f); 114 return fromStringz(buf.ptr).idup; 115 } 116 117 override void processAudioFloat(float** inputs, float** outputs, int samples) 118 { 119 _aeffect.processReplacing(_aeffect, inputs, outputs, samples); 120 _processedSamples += samples; 121 } 122 123 override void setSampleRate(float sampleRate) 124 { 125 _dispatcher(_aeffect, effSetSampleRate, 0, 0, null, sampleRate); 126 } 127 128 override void setMaxBufferSize(int samples) 129 { 130 _dispatcher(_aeffect, effSetBlockSize, 0, cast(VstIntPtr)samples, null, 0.0f); 131 } 132 133 override void loadPreset(int presetIndex) 134 { 135 _dispatcher(_aeffect, effSetProgram, 0, cast(ptrdiff_t)(presetIndex), null, 0.0f); 136 } 137 138 override void openUI(void* windowHandle) 139 { 140 _dispatcher(_aeffect, effEditOpen, 0, 0, windowHandle, 0.0f); 141 } 142 143 override void closeUI() 144 { 145 _dispatcher(_aeffect, effEditClose, 0, 0, null, 0.0f); 146 } 147 148 override int[2] getUISize() 149 { 150 ERect* rect; 151 _dispatcher(_aeffect, effEditGetRect, 0, 0, &rect, 0.0f); 152 int[2] size; 153 size[0] = rect.right - rect.left; 154 size[1] = rect.bottom - rect.top; 155 return size; 156 } 157 158 override ubyte[] saveState() 159 { 160 if (_aeffect.flags && effFlagsProgramChunks) 161 { 162 ubyte* pChunk = null; 163 VstIntPtr size = _dispatcher(_aeffect, effGetChunk, 0 /* want a bank */, 0, &pChunk, 0.0f); 164 165 if (size == 0 || pChunk == null) 166 throw new Exception("effGetChunk returned an empty chunk"); 167 168 return pChunk[0..size].dup; 169 } 170 else 171 throw new Exception("This VST doesn't support chunks"); 172 173 } 174 175 override void restoreState(ubyte[] chunk) 176 { 177 VstIntPtr result = _dispatcher(_aeffect, effSetChunk, 0 /* want a bank */, chunk.length, chunk.ptr, 0.0f); 178 if (result != 1) 179 throw new Exception("effSetChunk failed"); 180 } 181 182 override int getCurrentProgram() 183 { 184 return cast(int)( _dispatcher(_aeffect, effGetProgram, 0, 0, null, 0.0f) ); 185 } 186 187 private: 188 SharedLib _lib; 189 AEffect* _aeffect; 190 AEffectDispatcherProc _dispatcher; 191 long _processedSamples; 192 VstTimeInfo timeInfo; 193 } 194 195 extern(C) nothrow @nogc VstIntPtr hostCallback(AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) 196 { 197 import core.stdc.stdio; 198 199 // unimplemented stuff will printf 200 201 switch(opcode) 202 { 203 case audioMasterAutomate: printf("audioMasterAutomate\n"); return 0; 204 case audioMasterVersion: return 2400; 205 case audioMasterCurrentId: printf("audioMasterCurrentId\n"); return 0; 206 case audioMasterIdle: printf("audioMasterIdle\n"); return 0; 207 case DEPRECATED_audioMasterPinConnected: printf("DEPRECATED_audioMasterPinConnected\n"); return 0; 208 case DEPRECATED_audioMasterWantMidi: printf("DEPRECATED_audioMasterWantMidi\n"); return 0; 209 case audioMasterGetTime: 210 { 211 VSTPluginHost* phost = effect in reverseMapping; 212 if (!phost) 213 return 0; 214 215 VSTPluginHost host = *phost; 216 host.timeInfo.samplePos = host._processedSamples; 217 host.timeInfo.flags = 0; 218 219 return cast(VstIntPtr)(&host.timeInfo); 220 } 221 case audioMasterProcessEvents: printf("audioMasterProcessEvents\n"); return 0; 222 case DEPRECATED_audioMasterSetTime: printf("DEPRECATED_audioMasterSetTime\n"); return 0; 223 case DEPRECATED_audioMasterTempoAt: printf("DEPRECATED_audioMasterTempoAt\n"); return 0; 224 case DEPRECATED_audioMasterGetNumAutomatableParameters: printf("DEPRECATED_audioMasterGetNumAutomatableParameters\n"); return 0; 225 case DEPRECATED_audioMasterGetParameterQuantization: printf("DEPRECATED_audioMasterGetParameterQuantization\n"); return 0; 226 case audioMasterIOChanged: printf("audioMasterIOChanged\n"); return 0; 227 case DEPRECATED_audioMasterNeedIdle: printf("DEPRECATED_audioMasterNeedIdle\n"); return 0; 228 case audioMasterSizeWindow: printf("audioMasterSizeWindow\n"); return 0; 229 case audioMasterGetSampleRate: printf("audioMasterGetSampleRate\n"); return 0; 230 case audioMasterGetBlockSize: printf("audioMasterGetBlockSize\n"); return 0; 231 case audioMasterGetInputLatency: printf("audioMasterGetInputLatency\n"); return 0; 232 case audioMasterGetOutputLatency: printf("audioMasterGetOutputLatency\n"); return 0; 233 case DEPRECATED_audioMasterGetPreviousPlug: printf("DEPRECATED_audioMasterGetPreviousPlug\n"); return 0; 234 case DEPRECATED_audioMasterGetNextPlug: printf("DEPRECATED_audioMasterGetNextPlug\n"); return 0; 235 case DEPRECATED_audioMasterWillReplaceOrAccumulate: printf("DEPRECATED_audioMasterWillReplaceOrAccumulate\n"); return 0; 236 case audioMasterGetCurrentProcessLevel: printf("audioMasterGetCurrentProcessLevel\n"); return 0; 237 case audioMasterGetAutomationState: printf("audioMasterGetAutomationState\n"); return 0; 238 case audioMasterOfflineStart: printf("audioMasterOfflineStart\n"); return 0; 239 case audioMasterOfflineRead: printf("audioMasterOfflineRead\n"); return 0; 240 case audioMasterOfflineWrite: printf("audioMasterOfflineWrite\n"); return 0; 241 case audioMasterOfflineGetCurrentPass: printf("audioMasterOfflineGetCurrentPass\n"); return 0; 242 case audioMasterOfflineGetCurrentMetaPass: printf("audioMasterOfflineGetCurrentMetaPass\n"); return 0; 243 case DEPRECATED_audioMasterSetOutputSampleRate: printf("DEPRECATED_audioMasterSetOutputSampleRate\n"); return 0; 244 case DEPRECATED_audioMasterGetOutputSpeakerArrangement: printf("DEPRECATED_audioMasterGetOutputSpeakerArrangement\n"); return 0; 245 246 case audioMasterGetVendorString: 247 case audioMasterGetProductString: 248 { 249 char* p = cast(char*)ptr; 250 if (p !is null) 251 stringNCopy(p, 64, "dplug host"); 252 return 0; 253 } 254 255 case audioMasterGetVendorVersion: return 0x200; // 2.0 256 257 case audioMasterVendorSpecific: printf("audioMasterVendorSpecific\n"); return 0; 258 case DEPRECATED_audioMasterSetIcon: printf("DEPRECATED_audioMasterSetIcon\n"); return 0; 259 case audioMasterCanDo: printf("audioMasterCanDo\n"); return 0; 260 case audioMasterGetLanguage: printf("audioMasterGetLanguage\n"); return 0; 261 case DEPRECATED_audioMasterOpenWindow: printf("DEPRECATED_audioMasterOpenWindow\n"); return 0; 262 case DEPRECATED_audioMasterCloseWindow: printf("DEPRECATED_audioMasterCloseWindow\n"); return 0; 263 case audioMasterGetDirectory: printf("audioMasterGetDirectory\n"); return 0; 264 case audioMasterUpdateDisplay: printf("audioMasterUpdateDisplay\n"); return 0; 265 case audioMasterBeginEdit: printf("audioMasterBeginEdit\n"); return 0; 266 case audioMasterEndEdit: printf("audioMasterEndEdit\n"); return 0; 267 case audioMasterOpenFileSelector: printf("audioMasterOpenFileSelector\n"); return 0; 268 case audioMasterCloseFileSelector: printf("audioMasterCloseFileSelector\n"); return 0; 269 case DEPRECATED_audioMasterEditFile: printf("DEPRECATED_audioMasterEditFile\n"); return 0; 270 case DEPRECATED_audioMasterGetChunkFile: printf("DEPRECATED_audioMasterGetChunkFile\n"); return 0; 271 case DEPRECATED_audioMasterGetInputSpeakerArrangement: printf("DEPRECATED_audioMasterGetInputSpeakerArrangement\n"); return 0; 272 default: printf(" unknown opcode %d\n", opcode); return 0; 273 } 274 }