37ea86dc19796cd02450bab765496bfdaa3864e2
[ardour.git] / libs / fst / vstwin.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <windows.h>
4
5 #ifdef PLATFORM_WINDOWS
6
7 #include <ardourext/misc.h>
8 #include <ardourext/pthread.h>
9 static UINT_PTR idle_timer_id   = 0;
10
11 #else /* linux + wine */
12
13 #include <linux/limits.h> // PATH_MAX
14 #include <libgen.h> // basename
15 #include <winnt.h>
16 #include <wine/exception.h>
17 #include <pthread.h>
18 static int gui_quit = 0;
19
20 #endif
21
22 extern char * strdup (const char *);
23 #include <glib.h>
24 #include "fst.h"
25
26 struct ERect {
27         short top;
28         short left;
29         short bottom;
30         short right;
31 };
32
33 static pthread_mutex_t  plugin_mutex;
34 static VSTState*        fst_first        = NULL; /**< Head of linked list of all FSTs */
35 static int              host_initialized = 0;
36 static const char       magic[]          =  "FST Plugin State v002";
37
38
39 static LRESULT WINAPI
40 vstedit_wndproc (HWND w, UINT msg, WPARAM wp, LPARAM lp)
41 {
42         switch (msg) {
43                 case WM_KEYUP:
44                 case WM_KEYDOWN:
45                         break;
46
47                 case WM_CLOSE:
48                         /* we don't care about windows closing ...
49                          * WM_CLOSE is used for minimizing the window.
50                          * Our window has no frame so it shouldn't ever
51                          * get sent - but if it does, we don't want our
52                          * window to get minimized!
53                          */
54                         return 0;
55                         break;
56
57                 case WM_DESTROY:
58                 case WM_NCDESTROY:
59                         /* we don't care about windows being destroyed ... */
60                         return 0;
61                         break;
62
63                 default:
64                         break;
65         }
66
67         return DefWindowProcA (w, msg, wp, lp );
68 }
69
70
71 static void
72 maybe_set_program (VSTState* fst)
73 {
74         if (fst->want_program != -1) {
75                 if (fst->vst_version >= 2) {
76                         fst->plugin->dispatcher (fst->plugin, effBeginSetProgram, 0, 0, NULL, 0);
77                 }
78
79                 fst->plugin->dispatcher (fst->plugin, effSetProgram, 0, fst->want_program, NULL, 0);
80
81                 if (fst->vst_version >= 2) {
82                         fst->plugin->dispatcher (fst->plugin, effEndSetProgram, 0, 0, NULL, 0);
83                 }
84                 fst->want_program = -1;
85         }
86
87         if (fst->want_chunk == 1) {
88                 // XXX check
89                 // 24 == audioMasterGetAutomationState,
90                 // 48 == audioMasterGetChunkFile
91                 fst->plugin->dispatcher (fst->plugin, 24 /* effSetChunk */, 1, fst->wanted_chunk_size, fst->wanted_chunk, 0);
92                 fst->want_chunk = 0;
93         }
94 }
95
96 static VOID CALLBACK
97 idle_hands(
98                 HWND hwnd,        // handle to window for timer messages
99                 UINT message,     // WM_TIMER message
100                 UINT idTimer,     // timer identifier
101                 DWORD dwTime)     // current system time
102 {
103         VSTState* fst;
104
105         pthread_mutex_lock (&plugin_mutex);
106
107         for (fst = fst_first; fst; fst = fst->next) {
108                 if (fst->gui_shown) {
109                         // this seems insane, but some plugins will not draw their meters if you don't
110                         // call this every time.  Example Ambience by Magnus @ Smartelectron:x
111                         fst->plugin->dispatcher (fst->plugin, effEditIdle, 0, 0, NULL, 0);
112
113                         if (fst->wantIdle) {
114                                 fst->wantIdle = fst->plugin->dispatcher (fst->plugin, effIdle, 0, 0, NULL, 0);
115                         }
116                 }
117
118 #ifndef PLATFORM_WINDOWS /* linux + wine */
119                 /* Dispatch messages to send keypresses to the plugin */
120                 pthread_mutex_lock (&fst->lock);
121                 int i;
122
123                 for (i = 0; i < fst->n_pending_keys; ++i) {
124                         MSG msg;
125                         /* I'm not quite sure what is going on here; it seems
126                          * `special' keys must be delivered with WM_KEYDOWN,
127                          * but that alphanumerics etc. must use WM_CHAR or
128                          * they will be ignored.  Ours is not to reason why ...
129                          */
130                         if (fst->pending_keys[i].special != 0) {
131                                 msg.message = WM_KEYDOWN;
132                                 msg.wParam = fst->pending_keys[i].special;
133                         } else {
134                                 msg.message = WM_CHAR;
135                                 msg.wParam = fst->pending_keys[i].character;
136                         }
137                         msg.hwnd = GetFocus ();
138                         msg.lParam = 0;
139                         DispatchMessageA (&msg);
140                 }
141
142                 fst->n_pending_keys = 0;
143
144                 /* See comment for maybe_set_program call below */
145                 maybe_set_program (fst);
146                 fst->want_program = -1;
147                 fst->want_chunk = 0;
148                 /* If we don't have an editor window yet, we still need to
149                  * set up the program, otherwise when we load a plugin without
150                  * opening its window it will sound wrong.  However, it seems
151                  * that if you don't also load the program after opening the GUI,
152                  * the GUI does not reflect the program properly.  So we'll not
153                  * mark that we've done this (ie we won't set want_program to -1)
154                  * and so it will be done again if and when the GUI arrives.
155                  */
156                 if (fst->program_set_without_editor == 0) {
157                         maybe_set_program (fst);
158                         fst->program_set_without_editor = 1;
159                 }
160 #endif
161
162                 pthread_mutex_unlock (&fst->lock);
163         }
164
165         pthread_mutex_unlock (&plugin_mutex);
166 }
167
168 static void
169 fst_idle_timer_add_plugin (VSTState* fst)
170 {
171         pthread_mutex_lock (&plugin_mutex);
172
173         if (fst_first == NULL) {
174                 fst_first = fst;
175         } else {
176                 VSTState* p = fst_first;
177                 while (p->next) {
178                         p = p->next;
179                 }
180                 p->next = fst;
181         }
182
183         pthread_mutex_unlock (&plugin_mutex);
184 }
185
186 static void
187 fst_idle_timer_remove_plugin (VSTState* fst)
188 {
189         VSTState* p;
190         VSTState* prev;
191
192         pthread_mutex_lock (&plugin_mutex);
193
194         for (p = fst_first, prev = NULL; p; prev = p, p = p->next) {
195                 if (p == fst) {
196                         if (prev) {
197                                 prev->next = p->next;
198                         }
199                         break;
200                 }
201                 if (!p->next) {
202                         break;
203                 }
204         }
205
206         if (fst_first == fst) {
207                 fst_first = fst_first->next;
208         }
209
210         pthread_mutex_unlock (&plugin_mutex);
211 }
212
213 static VSTState*
214 fst_new (void)
215 {
216         VSTState* fst = (VSTState*) calloc (1, sizeof (VSTState));
217         pthread_mutex_init (&fst->lock, NULL);
218         pthread_cond_init (&fst->window_status_change, NULL); // unused ?? -> TODO check gtk2ardour
219         pthread_cond_init (&fst->plugin_dispatcher_called, NULL); // unused ??
220         fst->want_program = -1;
221         fst->want_chunk = 0;
222         fst->n_pending_keys = 0;
223         fst->has_editor = 0;
224 #ifdef PLATFORM_WINDOWS
225         fst->voffset = 36;
226 #else /* linux + wine */
227         fst->voffset = 24;
228 #endif
229         fst->program_set_without_editor = 0;
230         return fst;
231 }
232
233 static void
234 fst_delete (VSTState* fst)
235 {
236         if (fst) {
237                 free((void*)fst);
238                 fst = NULL;
239         }
240 }
241
242 static VSTHandle*
243 fst_handle_new (void)
244 {
245         VSTHandle* fst = (VSTHandle*) calloc (1, sizeof (VSTHandle));
246         return fst;
247 }
248
249 #ifndef PLATFORM_WINDOWS /* linux + wine */
250 static gboolean
251 g_idle_call (gpointer ignored) {
252         MSG msg;
253         if (PeekMessageA (&msg, NULL, 0, 0, 1)) {
254                 TranslateMessage (&msg);
255                 DispatchMessageA (&msg);
256         }
257         idle_hands(NULL, 0, 0, 0);
258         return gui_quit ? FALSE : TRUE;
259 }
260 #endif
261
262
263 int
264 fst_init (void* possible_hmodule)
265 {
266         if (host_initialized) return 0;
267         HMODULE hInst;
268
269         if (possible_hmodule) {
270 #ifdef PLATFORM_WINDOWS
271                 fst_error ("Error in fst_init(): (module handle is unnecessary for Win32 build)");
272                 return -1;
273 #else /* linux + wine */
274                 hInst = (HMODULE) possible_hmodule;
275 #endif
276         } else if ((hInst = GetModuleHandleA (NULL)) == NULL) {
277                 fst_error ("can't get module handle");
278                 return -1;
279         }
280
281         if (!hInst) {
282                 fst_error ("Cannot initialise VST host");
283                 return -1;
284         }
285
286         WNDCLASSEX wclass;
287
288         wclass.cbSize = sizeof(WNDCLASSEX);
289 #ifdef PLATFORM_WINDOWS
290         wclass.style = (CS_HREDRAW | CS_VREDRAW);
291         wclass.hIcon = NULL;
292         wclass.hCursor = LoadCursor(0, IDC_ARROW);
293 #else /* linux + wine */
294         wclass.style = 0;
295         wclass.hIcon = LoadIcon(hInst, "FST");
296         wclass.hCursor = LoadCursor(0, IDI_APPLICATION);
297 #endif
298         wclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
299         wclass.lpfnWndProc = vstedit_wndproc;
300         wclass.cbClsExtra = 0;
301         wclass.cbWndExtra = 0;
302         wclass.hInstance = hInst;
303         wclass.lpszMenuName = "MENU_FST";
304         wclass.lpszClassName = "FST";
305         wclass.hIconSm = 0;
306
307         pthread_mutex_init (&plugin_mutex, NULL);
308         host_initialized = -1;
309
310         if (!RegisterClassExA(&wclass)){
311                 fst_error ("Error in fst_init(): (class registration failed");
312                 return -1;
313         }
314 #ifndef PLATFORM_WINDOWS /* linux + wine */
315         gui_quit = 0;
316         g_idle_add (g_idle_call, NULL); // XXX too early ?
317         //g_timeout_add(40, g_idle_call, NULL);
318 #endif
319         return 0;
320 }
321
322 void
323 fst_exit (void)
324 {
325         if (!host_initialized) return;
326         VSTState* fst;
327         // If any plugins are still open at this point, close them!
328         while ((fst = fst_first))
329                 fst_close (fst);
330
331 #ifdef PLATFORM_WINDOWS
332         if (idle_timer_id != 0) {
333                 KillTimer(NULL, idle_timer_id);
334         }
335 #else /* linux + wine */
336         gui_quit = 1;
337         PostQuitMessage (0);
338 #endif
339
340         host_initialized = FALSE;
341         pthread_mutex_destroy (&plugin_mutex);
342 }
343
344
345 int
346 fst_run_editor (VSTState* fst, void* window_parent)
347 {
348         if (fst->windows_window == NULL) {
349                 HMODULE hInst;
350                 HWND window;
351                 struct ERect* er;
352
353                 if (!(fst->plugin->flags & effFlagsHasEditor)) {
354                         fst_error ("Plugin \"%s\" has no editor", fst->handle->name);
355                         return -1;
356                 }
357
358                 if ((hInst = GetModuleHandleA (NULL)) == NULL) {
359                         fst_error ("fst_create_editor() can't get module handle");
360                         return 1;
361                 }
362
363                 if ((window = CreateWindowExA (0, "FST", fst->handle->name,
364                                                 window_parent ? WS_CHILD : (WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX),
365                                                 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
366                                                 (HWND)window_parent, NULL,
367                                                 hInst,
368                                                 NULL) ) == NULL) {
369                         fst_error ("fst_create_editor() cannot create editor window");
370                         return 1;
371                 }
372
373                 if (!SetPropA (window, "fst_ptr", fst)) {
374                         fst_error ("fst_create_editor() cannot set fst_ptr on window");
375                 }
376
377                 fst->windows_window = window;
378
379                 if (window_parent) {
380                         // This is requiredv for some reason. Note the parent is set above when the window
381                         // is created. Without this extra call the actual plugin window will draw outside
382                         // of our plugin window.
383                         SetParent((HWND)fst->windows_window, (HWND)window_parent);
384                         fst->xid = 0;
385 #ifndef PLATFORM_WINDOWS /* linux + wine */
386                 } else {
387                         SetWindowPos (fst->windows_window, 0, 9999, 9999, 2, 2, 0);
388                         ShowWindow (fst->windows_window, SW_SHOWNA);
389                         fst->xid = (int) GetPropA (fst->windows_window, "__wine_x11_whole_window");
390 #endif
391                 }
392
393                 // This is the suggested order of calls.
394                 fst->plugin->dispatcher (fst->plugin, effEditGetRect, 0, 0, &er, 0 );
395                 fst->plugin->dispatcher (fst->plugin, effEditOpen, 0, 0, fst->windows_window, 0 );
396                 fst->plugin->dispatcher (fst->plugin, effEditGetRect, 0, 0, &er, 0 );
397
398                 fst->width =  er->right-er->left;
399                 fst->height =  er->bottom-er->top;
400
401
402                 fst->been_activated = TRUE;
403
404         }
405
406         if (fst->windows_window) {
407 #ifdef PLATFORM_WINDOWS
408                 if (idle_timer_id == 0) {
409                         // Init the idle timer if needed, so that the main window calls us.
410                         idle_timer_id = SetTimer(NULL, idle_timer_id, 50, (TIMERPROC) idle_hands);
411                 }
412 #endif
413
414                 fst_idle_timer_add_plugin (fst);
415         }
416
417         return fst->windows_window == NULL ? -1 : 0;
418 }
419
420 void
421 fst_destroy_editor (VSTState* fst)
422 {
423         if (fst->windows_window) {
424                 fprintf (stderr, "%s destroying edit window\n", fst->handle->name);
425
426                 fst_idle_timer_remove_plugin (fst);
427                 fst->plugin->dispatcher( fst->plugin, effEditClose, 0, 0, NULL, 0.0 );
428
429                 DestroyWindow ((HWND)(fst->windows_window));
430
431                 fst->windows_window = NULL;
432         }
433
434         fst->been_activated = FALSE;
435 }
436
437 void
438 fst_move_window_into_view (VSTState* fst)
439 {
440         if (fst->windows_window) {
441 #ifdef PLATFORM_WINDOWS
442                 SetWindowPos ((HWND)(fst->windows_window), 0, 0, fst->voffset, fst->width, fst->height, 0);
443 #else /* linux + wine */
444                 SetWindowPos ((HWND)(fst->windows_window), 0, 0, 0, fst->width, fst->height, 0);
445 #endif
446                 ShowWindow ((HWND)(fst->windows_window), SW_SHOWNA);
447         }
448 }
449
450 static HMODULE
451 fst_load_vst_library(const char * path)
452 {
453         char legalized_path[PATH_MAX];
454         strcpy (legalized_path, g_locale_from_utf8(path, -1, NULL, NULL, NULL));
455         return ( LoadLibraryA (legalized_path) );
456 }
457
458 VSTHandle *
459 fst_load (const char *path)
460 {
461         VSTHandle* fhandle = NULL;
462
463         if ((strlen(path)) && (NULL != (fhandle = fst_handle_new ())))
464         {
465                 char* period;
466                 fhandle->nameptr = strdup (path);
467                 fhandle->path = strdup (path);
468                 fhandle->name = basename(fhandle->nameptr);
469                 if ((period = strrchr (fhandle->name, '.'))) {
470                         *period = '\0';
471                 }
472
473                 // See if we can load the plugin DLL
474                 if ((fhandle->dll = (HMODULE)fst_load_vst_library (path)) == NULL) {
475                         fst_unload (&fhandle);
476                         return NULL;
477                 }
478
479                 fhandle->main_entry = (main_entry_t) GetProcAddress (fhandle->dll, "main");
480
481                 if (fhandle->main_entry == 0) {
482                         if ((fhandle->main_entry = (main_entry_t) GetProcAddress (fhandle->dll, "VSTPluginMain"))) {
483                                 fprintf(stderr, "VST >= 2.4 plugin '%s'\n", path);
484                                 //PBD::warning << path << _(": is a VST >= 2.4 - this plugin may or may not function correctly with this version of Ardour.") << endmsg;
485                         }
486                 }
487
488                 if (fhandle->main_entry == 0) {
489                         fst_unload (&fhandle);
490                         return NULL;
491                 }
492         }
493         return fhandle;
494 }
495
496 int
497 fst_unload (VSTHandle** fhandle)
498 {
499         if (!(*fhandle)) {
500                 return -1;
501         }
502
503         if ((*fhandle)->plugincnt) {
504                 return -1;
505         }
506
507         if ((*fhandle)->dll) {
508                 FreeLibrary ((HMODULE)(*fhandle)->dll);
509                 (*fhandle)->dll = NULL;
510         }
511
512         if ((*fhandle)->path) {
513                 free ((*fhandle)->path);
514                 (*fhandle)->path = NULL;
515         }
516
517         if ((*fhandle)->nameptr) {
518                 free ((*fhandle)->nameptr);
519                 (*fhandle)->nameptr = NULL;
520                 (*fhandle)->name = NULL;
521         }
522
523         free (*fhandle);
524         *fhandle = NULL;
525
526         return 0;
527 }
528
529 VSTState*
530 fst_instantiate (VSTHandle* fhandle, audioMasterCallback amc, void* userptr)
531 {
532         VSTState* fst = NULL;
533
534         if( fhandle == NULL ) {
535                 fst_error( "fst_instantiate(): (the handle was NULL)\n" );
536                 return NULL;
537         }
538
539         fst = fst_new ();
540
541         if ((fst->plugin = fhandle->main_entry (amc)) == NULL)  {
542                 fst_error ("fst_instantiate: %s could not be instantiated\n", fhandle->name);
543                 free (fst);
544                 return NULL;
545         }
546
547         fst->handle = fhandle;
548         fst->plugin->user = userptr;
549
550         if (fst->plugin->magic != kEffectMagic) {
551                 fst_error ("fst_instantiate: %s is not a vst plugin\n", fhandle->name);
552                 fst_close(fst);
553                 return NULL;
554         }
555
556         fst->plugin->dispatcher (fst->plugin, effOpen, 0, 0, 0, 0);
557         fst->vst_version = fst->plugin->dispatcher (fst->plugin, effGetVstVersion, 0, 0, 0, 0);
558
559         fst->handle->plugincnt++;
560         fst->wantIdle = 0;
561
562         return fst;
563 }
564
565 void
566 fst_close (VSTState* fst)
567 {
568         if (fst != NULL) {
569                 fst_destroy_editor (fst);
570
571                 if (fst->plugin) {
572                         fst->plugin->dispatcher (fst->plugin, effMainsChanged, 0, 0, NULL, 0);
573                         fst->plugin->dispatcher (fst->plugin, effClose, 0, 0, 0, 0);
574                         fst->plugin = NULL;
575                 }
576
577                 if (fst->handle) {
578                         if (fst->handle->plugincnt && --fst->handle->plugincnt == 0) {
579
580                                 fst->handle->main_entry = NULL;
581                                 fst_unload (&fst->handle);
582                         }
583                 }
584
585                 /* It might be good for this to be in it's own cleanup function
586                         since it will free the memory for the fst leaving the caller
587                         with an invalid pointer.  Caller beware */
588                 fst_delete(fst);
589         }
590 }
591
592 #if 0 // ?? who needs this, where?
593 float htonf (float v)
594 {
595         float result;
596         char * fin = (char*)&v;
597         char * fout = (char*)&result;
598         fout[0] = fin[3];
599         fout[1] = fin[2];
600         fout[2] = fin[1];
601         fout[3] = fin[0];
602         return result;
603 }
604 #endif