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