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 }