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 }