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