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