d86d3d5825c6261a2085fcf7c1b5068dbea9de54
[ardour.git] / libs / ardour / mac_vst_support.cc
1 /*
2  * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
3  * Copyright (C) 2012 Paul Davis
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #include <stdio.h>
21 #include <stdarg.h>
22 #include <stdlib.h>
23 #include <pthread.h>
24 #include <signal.h>
25 #include <dlfcn.h>
26 #include <string.h>
27 #include <time.h>
28 #include <unistd.h>
29 #include <pthread.h>
30
31 #include <glib.h>
32 #include "pbd/gstdio_compat.h"
33 #include <glibmm/miscutils.h>
34 #include <glibmm/fileutils.h>
35
36 #include "ardour/mac_vst_support.h"
37 #include "pbd/basename.h"
38 #include "pbd/error.h"
39
40 #include "pbd/i18n.h"
41
42 #include <Carbon/Carbon.h>
43
44 /*Simple error handler stuff for VSTFX*/
45
46 void mac_vst_error (const char *fmt, ...)
47 {
48         va_list ap;
49         char buffer[512];
50
51         va_start (ap, fmt);
52         vsnprintf (buffer, sizeof (buffer), fmt, ap);
53         mac_vst_error_callback (buffer);
54         va_end (ap);
55 }
56
57 /*default error handler callback*/
58
59 static void default_mac_vst_error_callback (const char *desc)
60 {
61         PBD::error << desc << endmsg;
62 }
63
64 void (*mac_vst_error_callback)(const char *desc) = &default_mac_vst_error_callback;
65
66 /* --- */
67
68 /*Create and return a pointer to a new VSTFX handle*/
69
70 static VSTHandle *
71 mac_vst_handle_new ()
72 {
73         VSTHandle* mac_vst = (VSTHandle *) calloc (1, sizeof (VSTHandle));
74         return mac_vst;
75 }
76
77 /*Create and return a pointer to a new mac_vst instance*/
78
79 static VSTState *
80 mac_vst_new ()
81 {
82         VSTState* mac_vst = (VSTState *) calloc (1, sizeof (VSTState));
83         mac_vst->init();
84         return mac_vst;
85 }
86
87 /*This loads up a plugin, given the path to its .vst bundle and
88  * finds its main entry point etc */
89
90 VSTHandle *
91 mac_vst_load (const char *path)
92 {
93         VSTHandle* fhandle;
94
95         /*Create a new handle we can use to reference the plugin*/
96
97         fhandle = mac_vst_handle_new ();
98
99         fhandle->dll = NULL;
100
101         CFURLRef url;
102         if (!(url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*)path, (CFIndex) strlen (path), true))) {
103                 return 0;
104         }
105
106         CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url);
107         CFRelease (url);
108
109         if (bundleRef == 0) {
110                 return 0;
111         }
112
113         if (!CFBundleLoadExecutable (bundleRef)) {
114                 CFRelease (bundleRef);
115                 return 0;
116         }
117
118         fhandle->name = strdup (path);
119         fhandle->dll = (void*) &bundleRef;
120
121         fhandle->main_entry = (main_entry_t)
122                 CFBundleGetFunctionPointerForName (bundleRef, CFSTR("main_macho"));
123
124         if (!fhandle->main_entry) {
125                 fhandle->main_entry = (main_entry_t)
126                         CFBundleGetFunctionPointerForName (bundleRef, CFSTR("VSTPluginMain"));
127         }
128
129         if (fhandle->main_entry == 0) {
130                 mac_vst_unload (fhandle);
131                 return 0;
132         }
133
134         fhandle->res_file_id = CFBundleOpenBundleResourceMap (bundleRef);
135
136         /*return the handle of the plugin*/
137         return fhandle;
138 }
139
140 /*This unloads a plugin*/
141
142 int
143 mac_vst_unload (VSTHandle* fhandle)
144 {
145         if (fhandle->plugincnt)
146         {
147                 /*Still have plugin instances - can't unload the library
148                 - actually dlclose keeps an instance count anyway*/
149
150                 return -1;
151         }
152
153         /*Valid plugin loaded?*/
154
155         if (fhandle->dll)
156         {
157                 CFBundleRef* bundleRefPtr = (CFBundleRef*) fhandle->dll;
158                 CFBundleCloseBundleResourceMap (*bundleRefPtr, (CFBundleRefNum)fhandle->res_file_id);
159                 CFRelease (*bundleRefPtr);
160                 fhandle->dll = 0;
161         }
162
163         if (fhandle->name)
164         {
165                 free (fhandle->name);
166         }
167
168         /*Don't need the plugin handle any more*/
169
170         free (fhandle);
171         return 0;
172 }
173
174 /*This instantiates a plugin*/
175
176 VSTState *
177 mac_vst_instantiate (VSTHandle* fhandle, audioMasterCallback amc, void* userptr)
178 {
179         VSTState* mac_vst = mac_vst_new ();
180
181         if (fhandle == 0)
182         {
183                 mac_vst_error ( "** ERROR ** VSTFX : The handle was 0\n" );
184                 free (mac_vst);
185                 return 0;
186         }
187
188         if ((mac_vst->plugin = fhandle->main_entry (amc)) == 0)
189         {
190                 mac_vst_error ("** ERROR ** VSTFX : %s could not be instantiated :(\n", fhandle->name);
191                 free (mac_vst);
192                 return 0;
193         }
194
195         mac_vst->handle = fhandle;
196         mac_vst->plugin->user = userptr;
197
198         if (mac_vst->plugin->magic != kEffectMagic)
199         {
200                 mac_vst_error ("** ERROR ** VSTFX : %s is not a VST plugin\n", fhandle->name);
201                 free (mac_vst);
202                 return 0;
203         }
204
205         mac_vst->plugin->dispatcher (mac_vst->plugin, effOpen, 0, 0, 0, 0);
206
207         /*May or May not need to 'switch the plugin on' here - unlikely
208         since FST doesn't and most plugins start up 'On' by default - I think this is the least of our worries*/
209
210         //mac_vst->plugin->dispatcher (mac_vst->plugin, effMainsChanged, 0, 1, 0, 0);
211
212         /* configure plugin to use Cocoa View */
213         mac_vst->plugin->dispatcher (mac_vst->plugin, effCanDo, 0, 0, const_cast<char*> ("hasCockosViewAsConfig"), 0.0f);
214
215         mac_vst->vst_version = mac_vst->plugin->dispatcher (mac_vst->plugin, effGetVstVersion, 0, 0, 0, 0);
216
217         mac_vst->handle->plugincnt++;
218         mac_vst->wantIdle = 0;
219
220         return mac_vst;
221 }
222
223 /*Close a mac_vst instance*/
224
225 void mac_vst_close (VSTState* mac_vst)
226 {
227         // assert that the GUI object is destoyed
228
229         if (mac_vst->plugin)
230         {
231                 mac_vst->plugin->dispatcher (mac_vst->plugin, effMainsChanged, 0, 0, 0, 0);
232
233                 /*Calling dispatcher with effClose will cause the plugin's destructor to
234                 be called, which will also remove the editor if it exists*/
235
236                 mac_vst->plugin->dispatcher (mac_vst->plugin, effClose, 0, 0, 0, 0);
237         }
238
239         if (mac_vst->handle->plugincnt)
240                         mac_vst->handle->plugincnt--;
241
242         /*mac_vst_unload will unload the dll if the instance count allows -
243         we need to do this because some plugins keep their own instance count
244         and (JUCE) manages the plugin UI in its own thread.  When the plugins
245         internal instance count reaches zero, JUCE stops the UI thread and won't
246         restart it until the next time the library is loaded.  If we don't unload
247         the lib JUCE will never restart*/
248
249
250         if (mac_vst->handle->plugincnt)
251         {
252                 return;
253         }
254
255         /*Valid plugin loaded - so we can unload it and 0 the pointer
256         to it.  We can't free the handle here because we don't know what else
257         might need it.  It should be / is freed when the plugin is deleted*/
258
259         if (mac_vst->handle->dll)
260         {
261                 dlclose (mac_vst->handle->dll); //dlclose keeps its own reference count
262                 mac_vst->handle->dll = 0;
263         }
264         free (mac_vst);
265 }
266
267 #if 0 // TODO wrap dispatch
268 intptr_t
269 mac_vst_dispatch (VSTState* mac_vst, int op, int idx, intptr_t val, void* ptr, float opt)
270 {
271         const ResFileRefNum old_resources = CurResFile();
272
273         if (mac_vst->handle->res_file_id) {
274                 UseResFile (mac_vst->handle->res_file_id);
275         }
276
277         mac_vst->plugin->dispatcher (mac_vst->plugin, op, idx, val, prt, opt);
278
279         const ResFileRefNum current_res = CurResFile();
280         if (current_res != old_resources) {
281                 mac_vst->handle->res_file_id = current_res;
282                 UseResFile (old_resources);
283         }
284 }
285 #endif