Mac VST-2.x support
[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
84         /*Mutexes*/
85         pthread_mutex_init (&mac_vst->lock, 0);
86         pthread_cond_init (&mac_vst->window_status_change, 0); // XXX  unused
87         pthread_cond_init (&mac_vst->plugin_dispatcher_called, 0); // XXX unused
88         pthread_cond_init (&mac_vst->window_created, 0); // XXX unused
89
90         /*Safe values*/
91         mac_vst->want_program = -1;
92         mac_vst->want_chunk = 0;
93         mac_vst->n_pending_keys = 0;
94         mac_vst->has_editor = 0;
95         mac_vst->program_set_without_editor = 0;
96         mac_vst->linux_window = 0;
97         mac_vst->linux_plugin_ui_window = 0;
98         mac_vst->eventProc = 0;
99         mac_vst->extra_data = 0;
100         mac_vst->want_resize = 0;
101
102         return mac_vst;
103 }
104
105 /*This loads up a plugin, given the path to its .vst bundle and
106  * finds its main entry point etc */
107
108 VSTHandle *
109 mac_vst_load (const char *path)
110 {
111         VSTHandle* fhandle;
112
113         /*Create a new handle we can use to reference the plugin*/
114
115         fhandle = mac_vst_handle_new ();
116
117         fhandle->dll = NULL;
118
119         CFURLRef url;
120         if (!(url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*)path, (CFIndex) strlen (path), true))) {
121                 return 0;
122         }
123
124         CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url);
125         CFRelease (url);
126
127         if (bundleRef == 0) {
128                 return 0;
129         }
130
131         if (!CFBundleLoadExecutable (bundleRef)) {
132                 CFRelease (bundleRef);
133                 return 0;
134         }
135
136         fhandle->name = strdup (path);
137         fhandle->dll = (void*) &bundleRef;
138
139         fhandle->main_entry = (main_entry_t)
140                 CFBundleGetFunctionPointerForName (bundleRef, CFSTR("main_macho"));
141
142         if (!fhandle->main_entry) {
143                 fhandle->main_entry = (main_entry_t)
144                         CFBundleGetFunctionPointerForName (bundleRef, CFSTR("VSTPluginMain"));
145         }
146
147         if (fhandle->main_entry == 0) {
148                 mac_vst_unload (fhandle);
149                 return 0;
150         }
151
152         fhandle->res_file_id = CFBundleOpenBundleResourceMap (bundleRef);
153
154         /*return the handle of the plugin*/
155         return fhandle;
156 }
157
158 /*This unloads a plugin*/
159
160 int
161 mac_vst_unload (VSTHandle* fhandle)
162 {
163         if (fhandle->plugincnt)
164         {
165                 /*Still have plugin instances - can't unload the library
166                 - actually dlclose keeps an instance count anyway*/
167
168                 return -1;
169         }
170
171         /*Valid plugin loaded?*/
172
173         if (fhandle->dll)
174         {
175                 CFBundleRef* bundleRefPtr = (CFBundleRef*) fhandle->dll;
176                 CFBundleCloseBundleResourceMap (*bundleRefPtr, (CFBundleRefNum)fhandle->res_file_id);
177                 CFRelease (*bundleRefPtr);
178                 fhandle->dll = 0;
179         }
180
181         if (fhandle->name)
182         {
183                 free (fhandle->name);
184         }
185
186         /*Don't need the plugin handle any more*/
187
188         free (fhandle);
189         return 0;
190 }
191
192 /*This instantiates a plugin*/
193
194 VSTState *
195 mac_vst_instantiate (VSTHandle* fhandle, audioMasterCallback amc, void* userptr)
196 {
197         VSTState* mac_vst = mac_vst_new ();
198
199         if (fhandle == 0)
200         {
201                 mac_vst_error ( "** ERROR ** VSTFX : The handle was 0\n" );
202                 free (mac_vst);
203                 return 0;
204         }
205
206         if ((mac_vst->plugin = fhandle->main_entry (amc)) == 0)
207         {
208                 mac_vst_error ("** ERROR ** VSTFX : %s could not be instantiated :(\n", fhandle->name);
209                 free (mac_vst);
210                 return 0;
211         }
212
213         mac_vst->handle = fhandle;
214         mac_vst->plugin->user = userptr;
215
216         if (mac_vst->plugin->magic != kEffectMagic)
217         {
218                 mac_vst_error ("** ERROR ** VSTFX : %s is not a VST plugin\n", fhandle->name);
219                 free (mac_vst);
220                 return 0;
221         }
222
223         mac_vst->plugin->dispatcher (mac_vst->plugin, effOpen, 0, 0, 0, 0);
224
225         /*May or May not need to 'switch the plugin on' here - unlikely
226         since FST doesn't and most plugins start up 'On' by default - I think this is the least of our worries*/
227
228         //mac_vst->plugin->dispatcher (mac_vst->plugin, effMainsChanged, 0, 1, 0, 0);
229
230         /* configure plugin to use Cocoa View */
231         mac_vst->plugin->dispatcher (mac_vst->plugin, effCanDo, 0, 0, const_cast<char*> ("hasCockosViewAsConfig"), 0.0f);
232
233         mac_vst->vst_version = mac_vst->plugin->dispatcher (mac_vst->plugin, effGetVstVersion, 0, 0, 0, 0);
234
235         mac_vst->handle->plugincnt++;
236         mac_vst->wantIdle = 0;
237
238         return mac_vst;
239 }
240
241 /*Close a mac_vst instance*/
242
243 void mac_vst_close (VSTState* mac_vst)
244 {
245         // assert that the GUI object is destoyed
246
247         if (mac_vst->plugin)
248         {
249                 mac_vst->plugin->dispatcher (mac_vst->plugin, effMainsChanged, 0, 0, 0, 0);
250
251                 /*Calling dispatcher with effClose will cause the plugin's destructor to
252                 be called, which will also remove the editor if it exists*/
253
254                 mac_vst->plugin->dispatcher (mac_vst->plugin, effClose, 0, 0, 0, 0);
255         }
256
257         if (mac_vst->handle->plugincnt)
258                         mac_vst->handle->plugincnt--;
259
260         /*mac_vst_unload will unload the dll if the instance count allows -
261         we need to do this because some plugins keep their own instance count
262         and (JUCE) manages the plugin UI in its own thread.  When the plugins
263         internal instance count reaches zero, JUCE stops the UI thread and won't
264         restart it until the next time the library is loaded.  If we don't unload
265         the lib JUCE will never restart*/
266
267
268         if (mac_vst->handle->plugincnt)
269         {
270                 return;
271         }
272
273         /*Valid plugin loaded - so we can unload it and 0 the pointer
274         to it.  We can't free the handle here because we don't know what else
275         might need it.  It should be / is freed when the plugin is deleted*/
276
277         if (mac_vst->handle->dll)
278         {
279                 dlclose (mac_vst->handle->dll); //dlclose keeps its own reference count
280                 mac_vst->handle->dll = 0;
281         }
282         free (mac_vst);
283 }
284
285 #if 0 // TODO wrap dispatch
286 intptr_t
287 mac_vst_dispatch (VSTState* mac_vst, int op, int idx, intptr_t val, void* ptr, float opt)
288 {
289         const ResFileRefNum old_resources = CurResFile();
290
291         if (mac_vst->handle->res_file_id) {
292                 UseResFile (mac_vst->handle->res_file_id);
293         }
294
295         mac_vst->plugin->dispatcher (mac_vst->plugin, op, idx, val, prt, opt);
296
297         const ResFileRefNum current_res = CurResFile();
298         if (current_res != old_resources) {
299                 mac_vst->handle->res_file_id = current_res;
300                 UseResFile (old_resources);
301         }
302 }
303 #endif