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 }