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 }