diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2011-11-13 14:53:49 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2011-11-13 14:53:49 +0000 |
commit | 8e2858e555e98e2d45b4d37a305ae402f538e95f (patch) | |
tree | 9c33b72db0c570a47f44225a93967885d28992fb /gtk2_ardour/vstfxwin.cc | |
parent | f60c9d52dc4c3d59f6a269a0a1b435581135d408 (diff) |
move LXVST GUI stuff into gtk2_ardour, and fix X11 uselib stuff in wscripts
git-svn-id: svn://localhost/ardour2/branches/3.0@10565 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'gtk2_ardour/vstfxwin.cc')
-rwxr-xr-x | gtk2_ardour/vstfxwin.cc | 1399 |
1 files changed, 1399 insertions, 0 deletions
diff --git a/gtk2_ardour/vstfxwin.cc b/gtk2_ardour/vstfxwin.cc new file mode 100755 index 0000000000..a1a7d73909 --- /dev/null +++ b/gtk2_ardour/vstfxwin.cc @@ -0,0 +1,1399 @@ +/******************************************************************/ +/** VSTFX - An engine based on FST for handling linuxVST plugins **/ +/******************************************************************/ + +/*This is derived from the original FST (C code) with some tweaks*/ + + +/** EDITOR tab stops at 4 **/ + +#include <stdlib.h> +#include <stdio.h> +#include <jack/jack.h> +#include <jack/thread.h> +#include <libgen.h> + +#include <pthread.h> +#include <signal.h> +#include <glib.h> + +#include <ardour/vstfx.h> + +#include <X11/X.h> +#include <X11/Xlib.h> +#include <dlfcn.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <pthread.h> + +struct ERect{ + short top; + short left; + short bottom; + short right; +}; + +static pthread_mutex_t plugin_mutex; + +static VSTFX* vstfx_first = NULL; + +const char magic[] = "VSTFX Plugin State v002"; + +int gui_thread_id = 0; +static int gui_quit = 0; + +/*This will be our connection to X*/ + +Display* LXVST_XDisplay = NULL; + +/*The thread handle for the GUI event loop*/ + +pthread_t LXVST_gui_event_thread; + +#define DELAYED_WINDOW 1 + +/*Util functions to get the value of a property attached to an XWindow*/ + +bool LXVST_xerror; + +int TempErrorHandler(Display *display, XErrorEvent *e) +{ + LXVST_xerror = true; + + return 0; +} + +#ifdef LXVST_32BIT + +int getXWindowProperty(Window window, Atom atom) +{ + int result = 0; + int userSize; + unsigned long bytes; + unsigned long userCount; + unsigned char *data; + Atom userType; + LXVST_xerror = false; + + /*Use our own Xerror handler while we're in here - in an + attempt to stop the brain dead default Xerror behaviour of + qutting the entire application because of e.g. an invalid + window ID*/ + + XErrorHandler olderrorhandler = XSetErrorHandler(TempErrorHandler); + + XGetWindowProperty( LXVST_XDisplay, //The display + window, //The Window + atom, //The property + 0, //Offset into the data + 1, //Number of 32Bit chunks of data + false, //false = don't delete the property + AnyPropertyType, //Required property type mask + &userType, //Actual type returned + &userSize, //Actual format returned + &userCount, //Actual number of items stored in the returned data + &bytes, //Number of bytes remaining if a partial read + &data); //The actual data read + + if(LXVST_xerror == false && userCount == 1) + result = *(int*)data; + + XSetErrorHandler(olderrorhandler); + + /*Hopefully this will return zero if the property is not set*/ + + return result; +} + +#endif + +#ifdef LXVST_64BIT + +/********************************************************************/ +/* This is untested - have no 64Bit plugins which use this */ +/* system of passing an eventProc address */ +/********************************************************************/ + +long getXWindowProperty(Window window, Atom atom) +{ + long result = 0; + int userSize; + unsigned long bytes; + unsigned long userCount; + unsigned char *data; + Atom userType; + LXVST_xerror = false; + + /*Use our own Xerror handler while we're in here - in an + attempt to stop the brain dead default Xerror behaviour of + qutting the entire application because of e.g. an invalid + window ID*/ + + XErrorHandler olderrorhandler = XSetErrorHandler(TempErrorHandler); + + XGetWindowProperty( LXVST_XDisplay, + window, + atom, + 0, + 2, + false, + AnyPropertyType, + &userType, + &userSize, + &userCount, + &bytes, + &data); + + if(LXVST_xerror == false && userCount == 1) + result = *(long*)data; + + XSetErrorHandler(olderrorhandler); + + /*Hopefully this will return zero if the property is not set*/ + + return result; +} + +#endif + +/*The event handler - called from within the main GUI thread to +dispatch events to any VST UIs which have callbacks stuck to them*/ + +static void dispatch_x_events(XEvent* event, VSTFX* vstfx) +{ + /*Handle some of the Events we might be interested in*/ + + switch(event->type) + { + /*Configure event - when the window is resized or first drawn*/ + + case ConfigureNotify: + { + Window window = event->xconfigure.event; + + int width = event->xconfigure.width; + int height = event->xconfigure.height; + + /*If we get a config notify on the parent window XID then we need to see + if the size has been changed - some plugins re-size their UI window e.g. + when opening a preset manager (you might think that should be spawned as a new window...) */ + + /*if the size has changed, we flag this so that in lxvst_pluginui.cc we can make the + change to the GTK parent window in ardour, from its UI thread*/ + + if(window == (Window)(vstfx->window)) + { + if((width!=vstfx->width) || (height!=vstfx->height)) + { + vstfx->width = width; + vstfx->height = height; + vstfx->want_resize = 1; + + /*QUIRK : Loomer plugins not only resize the UI but throw it into some random + position at the same time. We need to re-position the window at the origin of + the parent window*/ + + if(vstfx->plugin_ui_window) + XMoveWindow(LXVST_XDisplay, vstfx->plugin_ui_window, 0, 0); + } + } + + break; + + } + + /*Reparent Notify - when the plugin UI is reparented into + our Host Window we will get an event here... probably... */ + + case ReparentNotify: + { + Window ParentWindow = event->xreparent.parent; + + /*If the ParentWindow matches the window for the vstfx instance then + the Child window must be the XID of the pluginUI window created by the + plugin, so we need to see if it has a callback stuck to it, and if so + set that up in the vstfx */ + + /***********************************************************/ + /* 64Bit --- This mechanism is not 64Bit compatible at the */ + /* present time */ + /***********************************************************/ + + if(ParentWindow == (Window)(vstfx->window)) + { + Window PluginUIWindowID = event->xreparent.window; + + vstfx->plugin_ui_window = PluginUIWindowID; +#ifdef LXVST_32BIT + int result = getXWindowProperty(PluginUIWindowID, XInternAtom(LXVST_XDisplay, "_XEventProc", false)); + + if(result == 0) + vstfx->eventProc = NULL; + else + vstfx->eventProc = (void (*) (void* event))result; +#endif +#ifdef LXVST_64BIT + long result = getXWindowProperty(PluginUIWindowID, XInternAtom(LXVST_XDisplay, "_XEventProc", false)); + + if(result == 0) + vstfx->eventProc = NULL; + else + vstfx->eventProc = (void (*) (void* event))result; +#endif + } + break; + } + + case ClientMessage: + { + Window window = event->xany.window; + Atom message_type = event->xclient.message_type; + + /*The only client message we are interested in is to signal + that the plugin parent window is now valid and can be passed + to effEditOpen when the editor is launched*/ + + if(window == (Window)(vstfx->window)) + { + char* message = XGetAtomName(LXVST_XDisplay, message_type); + + if(strcmp(message,"LaunchEditor") == 0) + { + + if(event->xclient.data.l[0] == 0x0FEEDBAC) + vstfx_launch_editor(vstfx); + } + + XFree(message); + } + break; + } + + default: + break; + } + + /* Some VSTs built with toolkits e.g. JUCE will manager their own UI + autonomously in the plugin, running the UI in its own thread, so once + we have created a parent window for the plugin, its UI takes care of + itself.*/ + + /*Other types register a callback as an Xwindow property on the plugin + UI window after they create it. If that is the case, we need to call it + here, passing the XEvent into it*/ + + if(vstfx->eventProc == NULL) + return; + + vstfx->eventProc((void*)event); +} + + +/*Create and return a pointer to a new vstfx instance*/ + +static VSTFX* vstfx_new () +{ + VSTFX* vstfx = (VSTFX*) calloc (1, sizeof (VSTFX)); + + /*Mutexes*/ + + pthread_mutex_init (&vstfx->lock, NULL); + pthread_cond_init (&vstfx->window_status_change, NULL); + pthread_cond_init (&vstfx->plugin_dispatcher_called, NULL); + pthread_cond_init (&vstfx->window_created, NULL); + + /*Safe values*/ + + vstfx->want_program = -1; + vstfx->want_chunk = 0; + vstfx->current_program = -1; + vstfx->n_pending_keys = 0; + vstfx->has_editor = 0; + vstfx->program_set_without_editor = 0; + vstfx->window = 0; + vstfx->plugin_ui_window = 0; + vstfx->eventProc = NULL; + vstfx->extra_data = NULL; + vstfx->want_resize = 0; + + return vstfx; +} + +/*Create and return a pointer to a new VSTFX handle*/ + +static VSTFXHandle* vstfx_handle_new() +{ + VSTFXHandle* vstfx = (VSTFXHandle*)calloc(1, sizeof (VSTFXHandle)); + return vstfx; +} + +/** This is the main gui event loop for the plugin, we also need to pass +any Xevents to all the UI callbacks plugins 'may' have registered on their +windows, that is if they don't manage their own UIs **/ + +void* gui_event_loop (void* ptr) +{ + + VSTFX* vstfx; + int LXVST_sched_event_timer = 0; + int LXVST_sched_timer_interval = 50; //ms + XEvent event; + + /*The 'Forever' loop - runs the plugin UIs etc - based on the FST gui event loop*/ + + while (!gui_quit) + { + /* handle window creation requests, destroy requests, + and run idle callbacks */ + + /*Look at the XEvent queue - if there are any XEvents we need to handle them, + including passing them to all the plugin (eventProcs) we are currently managing*/ + + if(LXVST_XDisplay) + { + /*See if there are any events in the queue*/ + + int num_events = XPending(LXVST_XDisplay); + + /*process them if there are any*/ + + while(num_events) + { + XNextEvent(LXVST_XDisplay, &event); + + /*Call dispatch events, with the event, for each plugin in the linked list*/ + + for (vstfx = vstfx_first; vstfx; vstfx = vstfx->next) + { + pthread_mutex_lock(&vstfx->lock); + + dispatch_x_events(&event, vstfx); + + pthread_mutex_unlock(&vstfx->lock); + } + + num_events--; + } + } + + /*We don't want to use all the CPU.. */ + + usleep(1000); + + LXVST_sched_event_timer++; + + LXVST_sched_event_timer = LXVST_sched_event_timer & 0x00FFFFFF; + + /*See if its time for us to do a scheduled event pass on all the plugins*/ + + if((LXVST_sched_timer_interval!=0) && (!(LXVST_sched_event_timer% LXVST_sched_timer_interval))) + { + pthread_mutex_lock (&plugin_mutex); + +again: + /*Parse through the linked list of plugins*/ + + for (vstfx = vstfx_first; vstfx; vstfx = vstfx->next) + { + pthread_mutex_lock (&vstfx->lock); + + /*Window scheduled for destruction*/ + + if (vstfx->destroy) + { + if (vstfx->window) + { + vstfx->plugin->dispatcher( vstfx->plugin, effEditClose, 0, 0, NULL, 0.0 ); + + XDestroyWindow (LXVST_XDisplay, vstfx->window); + vstfx->window = 0; //FIXME - probably safe to assume we never have an XID of 0 but not explicitly true + vstfx->destroy = FALSE; + } + + vstfx_event_loop_remove_plugin (vstfx); + vstfx->been_activated = FALSE; + pthread_cond_signal (&vstfx->window_status_change); + pthread_mutex_unlock (&vstfx->lock); + + goto again; + } + + /*Window does not yet exist - scheduled for creation*/ + + if (vstfx->window == 0) //FIXME - probably safe to assume 0 is not a valid XID but not explicitly true + { + if (vstfx_create_editor (vstfx)) + { + vstfx_error ("** ERROR ** VSTFX : Cannot create editor for plugin %s", vstfx->handle->name); + vstfx_event_loop_remove_plugin (vstfx); + pthread_cond_signal (&vstfx->window_status_change); + pthread_mutex_unlock (&vstfx->lock); + goto again; + } + else + { + /* condition/unlock: it was signalled & unlocked in fst_create_editor() */ + } + } + + /*Scheduled for setting a new program*/ + + if (vstfx->want_program != -1 ) + { + if (vstfx->vst_version >= 2) + { + vstfx->plugin->dispatcher (vstfx->plugin, 67 /* effBeginSetProgram */, 0, 0, NULL, 0); + } + + vstfx->plugin->dispatcher (vstfx->plugin, effSetProgram, 0, vstfx->want_program, NULL, 0); + + if (vstfx->vst_version >= 2) + { + vstfx->plugin->dispatcher (vstfx->plugin, 68 /* effEndSetProgram */, 0, 0, NULL, 0); + } + + /* did it work? */ + + vstfx->current_program = vstfx->plugin->dispatcher (vstfx->plugin, 3, /* effGetProgram */ 0, 0, NULL, 0); + vstfx->want_program = -1; + } + + /*scheduled call to dispatcher*/ + + if(vstfx->dispatcher_wantcall) + { + vstfx->dispatcher_retval = vstfx->plugin->dispatcher( vstfx->plugin, + vstfx->dispatcher_opcode, + vstfx->dispatcher_index, + vstfx->dispatcher_val, + vstfx->dispatcher_ptr, + vstfx->dispatcher_opt ); + vstfx->dispatcher_wantcall = 0; + pthread_cond_signal (&vstfx->plugin_dispatcher_called); + } + + /*Call the editor Idle function in the plugin*/ + + vstfx->plugin->dispatcher (vstfx->plugin, effEditIdle, 0, 0, NULL, 0); + + if(vstfx->wantIdle) + vstfx->plugin->dispatcher (vstfx->plugin, 53, 0, 0, NULL, 0); + + pthread_mutex_unlock (&vstfx->lock); + } + pthread_mutex_unlock (&plugin_mutex); + } + } + + /*Drop out to here if we set gui_quit to 1 */ + + return NULL; +} + +/*The VSTFX Init function - this needs to be called before the VSTFX engine +can be accessed, it gets the UI thread running, opens a connection to X etc +normally started in globals.cc*/ + +int vstfx_init (void* ptr) +{ + + int thread_create_result; + + pthread_attr_t thread_attributes; + + /*Init the attribs to defaults*/ + + pthread_attr_init(&thread_attributes); + + /*Make sure the thread is joinable - this should be the default anyway - + so we can join to it on vstfx_exit*/ + + pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_JOINABLE); + + + /*This is where we need to open a connection to X, and start the GUI thread*/ + + /*Open our connection to X - all linuxVST plugin UIs handled by the LXVST engine + will talk to X down this connection - X cannot handle multi-threaded access via + the same Display* */ + + if(LXVST_XDisplay==NULL) + LXVST_XDisplay = XOpenDisplay(NULL); //We might be able to make this open a specific screen etc + + /*Drop out and report the error if we fail to connect to X */ + + if(LXVST_XDisplay==NULL) + { + vstfx_error ("** ERROR ** VSTFX: Failed opening connection to X"); + + return -1; + } + + /*We have a connection to X - so start the gui event loop*/ + + /*Create the thread - use default attrs for now, don't think we need anything special*/ + + thread_create_result = pthread_create(&LXVST_gui_event_thread, NULL, gui_event_loop, NULL); + + if(thread_create_result!=0) + { + /*There was a problem starting the GUI event thread*/ + + vstfx_error ("** ERROR ** VSTFX: Failed starting GUI event thread"); + + XCloseDisplay(LXVST_XDisplay); + + return -1; + } + + return 0; +} + +/*The vstfx Quit function*/ + +void vstfx_exit() +{ + gui_quit = 1; + + /*We need to pthread_join the gui_thread here so + we know when it has stopped*/ + + pthread_join(LXVST_gui_event_thread, NULL); +} + +/*Adds a new plugin (VSTFX) instance to the linked list*/ + +int vstfx_run_editor (VSTFX* vstfx) +{ + pthread_mutex_lock (&plugin_mutex); + + /*Add the new VSTFX instance to the linked list*/ + + if (vstfx_first == NULL) + { + vstfx_first = vstfx; + } + else + { + VSTFX* p = vstfx_first; + + while (p->next) + { + p = p->next; + } + p->next = vstfx; + + /*Mark the new end of the list*/ + + vstfx->next = NULL; + } + + pthread_mutex_unlock (&plugin_mutex); + + /* wait for the plugin editor window to be created (or not) */ + + pthread_mutex_lock (&vstfx->lock); + + if (!vstfx->window) + { + pthread_cond_wait (&vstfx->window_status_change, &vstfx->lock); + } + + pthread_mutex_unlock (&vstfx->lock); + + if (!vstfx->window) + { + return -1; + } + + return 0; +} + +/*Set up a call to the plugins 'dispatcher' function*/ + +int vstfx_call_dispatcher (VSTFX *vstfx, int opcode, int index, int val, void *ptr, float opt) +{ + pthread_mutex_lock (&vstfx->lock); + + /*Set up the opcode and parameters*/ + + vstfx->dispatcher_opcode = opcode; + vstfx->dispatcher_index = index; + vstfx->dispatcher_val = val; + vstfx->dispatcher_ptr = ptr; + vstfx->dispatcher_opt = opt; + + /*Signal that we want the call to happen*/ + + vstfx->dispatcher_wantcall = 1; + + /*Wait for the call to happen*/ + + pthread_cond_wait (&vstfx->plugin_dispatcher_called, &vstfx->lock); + pthread_mutex_unlock (&vstfx->lock); + + /*Return the result*/ + + return vstfx->dispatcher_retval; +} + +/*Creates an editor for the plugin - normally called from within the gui event loop +after run_editor has added the plugin (editor) to the linked list*/ + +int vstfx_create_editor (VSTFX* vstfx) +{ + Window parent_window; + + int x_size = 1; + int y_size = 1; + + /* Note: vstfx->lock is held while this function is called */ + + if (!(vstfx->plugin->flags & effFlagsHasEditor)) + { + vstfx_error ("** ERROR ** VSTFX: Plugin \"%s\" has no editor", vstfx->handle->name); + return -1; + } + + + /*Create an XWindow for the plugin to inhabit*/ + + parent_window = XCreateSimpleWindow(LXVST_XDisplay, + DefaultRootWindow(LXVST_XDisplay), + 0, + 0, + x_size, + y_size, + 0, + 0, + 0); + + /*Select the events we are interested in receiving - we need Substructure notify so that + if the plugin resizes its window - e.g. Loomer Manifold then we get a message*/ + + XSelectInput(LXVST_XDisplay, + parent_window, + SubstructureNotifyMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | ExposureMask); + + vstfx->window = parent_window; + + vstfx->xid = parent_window; //vstfx->xid will be referenced to connect to GTK UI in ardour later + + /*Because the plugin may be operating on a different Display* to us, and therefore + the two event queues can be asynchronous, although we have created the window on + our display, we can't guarantee it exists in the server yet, which will + cause BadWindow crashes if the plugin tries to use it. + + It would be nice to use CreateNotify events here, but they don't get + through on all window managers, so instead we pass a client message + into out queue, after the XCreateWindow. When this message pops out + in our event handler, it will trigger the second stage of plugin + Editor instantiation, and by then the Window should be valid...*/ + + XClientMessageEvent event; + + /*Create an atom to identify our message (only if it doesn't already exist)*/ + + Atom WindowActiveAtom = XInternAtom(LXVST_XDisplay, "LaunchEditor", false); + + event.type = ClientMessage; + event.send_event = true; + event.window = parent_window; + event.message_type = WindowActiveAtom; + + event.format = 32; //Data format + event.data.l[0] = 0x0FEEDBAC; //Something we can recognize later + + /*Push the event into the queue on our Display*/ + + XSendEvent(LXVST_XDisplay, parent_window, FALSE, NoEventMask, (XEvent*)&event); + + /*Unlock - and we are done for the first part of staring the Editor...*/ + + pthread_mutex_unlock (&vstfx->lock); + + return 0; +} + +int vstfx_launch_editor(VSTFX* vstfx) +{ + /*This is the second stage of launching the editor (see vstfx_create editor) + we get called here in response to receiving the ClientMessage on our Window, + therefore it's about as safe (as can be) to assume that the Window we created + is now valid in the XServer and can be passed to the plugin in effEditOpen + without generating BadWindow errors when the plugin reparents itself into our + parent window*/ + + if(vstfx->been_activated) + return 0; + + Window parent_window; + struct ERect* er; + + int x_size = 1; + int y_size = 1; + + parent_window = vstfx->window; + + /*Open the editor - Bah! we have to pass the int windowID as a void pointer - yuck + it gets cast back to an int as the parent window XID in the plugin - and we have to pass the + Display* as a long */ + + /**************************************************************/ + /* 64Bit --- parent window is an int passed as a void* so */ + /* that should be ok for 64Bit machines */ + /* */ + /* Display is passed in as a long - ok on arch's where sizeof */ + /* long = 8 */ + /* */ + /* Most linux VST plugins open a connection to X on their own */ + /* Display anyway so it may not matter */ + /* */ + /* linuxDSP VSTs don't use the host Display* at all */ + /**************************************************************/ + + vstfx->plugin->dispatcher (vstfx->plugin, effEditOpen, 0, (long)LXVST_XDisplay, (void*)(parent_window), 0 ); + + /*QUIRK - some plugins need a slight delay after opening the editor before you can + ask the window size or they might return zero - specifically discoDSP */ + + usleep(100000); + + /*Now we can find out how big the parent window should be (and try) to resize it*/ + + vstfx->plugin->dispatcher (vstfx->plugin, effEditGetRect, 0, 0, &er, 0 ); + + x_size = er->right - er->left; + y_size = er->bottom - er->top; + + vstfx->width = x_size; + vstfx->height = y_size; + + XResizeWindow(LXVST_XDisplay, parent_window, x_size, y_size); + + XFlush (LXVST_XDisplay); + + /*Not sure if we need to map the window or if the plugin will do it for us + it should be ok because XReparentWindow generates a Map event*/ + + /*mark the editor as activated - mainly so that vstfx_get_XID + will know it is valid*/ + + vstfx->been_activated = TRUE; + + pthread_cond_signal (&vstfx->window_status_change); + return 0; +} + +/*May not be needed in the XLib version*/ + +void vstfx_move_window_into_view (VSTFX* vstfx) +{ + + /*This is probably the equivalent of Mapping an XWindow + but we most likely don't need it because the window + will be Mapped by XReparentWindow*/ + +} + +/*Destroy the editor window*/ + +void vstfx_destroy_editor (VSTFX* vstfx) +{ + pthread_mutex_lock (&vstfx->lock); + if (vstfx->window) + { + vstfx->destroy = TRUE; + pthread_cond_wait (&vstfx->window_status_change, &vstfx->lock); + } + pthread_mutex_unlock (&vstfx->lock); +} + +/*Remove a vstfx instance from the linked list parsed by the +event loop*/ + +void vstfx_event_loop_remove_plugin (VSTFX* vstfx) +{ + /*This only ever gets called from within our GUI thread + so we don't need to lock here - if we did there would be + a deadlock anyway*/ + + VSTFX* p; + VSTFX* prev; + + for(p = vstfx_first, prev = NULL; p; prev = p, p = p->next) + { + if(p == vstfx) + { + if(prev) + { + prev->next = p->next; + break; + } + } + } + + if (vstfx_first == vstfx) + vstfx_first = vstfx_first->next; +} + +/*This loads the plugin shared library*/ + +void* vstfx_load_vst_library(const char* path) +{ + void* dll; + char* full_path; + char* envdup; + char* lxvst_path; + size_t len1; + size_t len2; + + /*Try and load the shared library pointed to by the path - + NOTE: You have to give RTLD_LAZY or RTLD_NOW to dlopen or + you get some occasional failures to load - dlerror reports + invalid arguments*/ + + if ((dll = dlopen (path, RTLD_LOCAL | RTLD_LAZY)) != NULL) + return dll; + + /*We didn't find the library so try and get the path specified in the + env variable LXVST_PATH*/ + + envdup = getenv ("LXVST_PATH"); + + /*Path not specified - not much more we can do*/ + + if (envdup == NULL) + return NULL; + + /*Copy the path into envdup*/ + + envdup = strdup (envdup); + + if (envdup == NULL) + return NULL; + + len2 = strlen(path); + + /*Try all the possibilities in the path - deliminated by : */ + + lxvst_path = strtok (envdup, ":"); + + while (lxvst_path != NULL) + { + vstfx_error ("\"%s\"", lxvst_path); + len1 = strlen(lxvst_path); + + full_path = (char*)malloc(len1 + 1 + len2 + 1); + memcpy(full_path, lxvst_path, len1); + full_path[len1] = '/'; + memcpy(full_path + len1 + 1, path, len2); + full_path[len1 + 1 + len2] = '\0'; + + /*Try and load the library*/ + + if ((dll = dlopen(full_path, RTLD_LOCAL | RTLD_LAZY)) != NULL) + { + /*Succeeded */ + break; + } + + /*Try again*/ + + lxvst_path = strtok (NULL, ":"); + } + + /*Free the path*/ + + free(envdup); + + return dll; +} + +/*This loads up a plugin, given the path to its .so file and + finds its main entry point etc*/ + +VSTFXHandle* vstfx_load (const char *path) +{ + char* buf = NULL; + VSTFXHandle* fhandle; + int i; + + /*Create a new handle we can use to reference the plugin*/ + + fhandle = vstfx_handle_new(); + + /*See if we have .so appended to the path - if not we need to make sure it is added*/ + + if (strstr (path, ".so") == NULL) + { + + /*Append the .so to the path - Make sure the path has enough space*/ + + buf = (char *)malloc(strlen(path) + 4); //The .so and a terminating zero + + sprintf (buf, "%s.so", path); + + fhandle->nameptr = strdup (path); + + } + else + { + /*We already have .so appened to the filename*/ + + buf = strdup(path); + + fhandle->nameptr = strdup (path); + } + + /*Use basename to shorten the path and then strip off the .so - the old VST problem, + we don't know anything about its name until we load and instantiate the plugin + which we don't want to do at this point*/ + + for(i=0; i < (int)strlen(fhandle->nameptr); i++) + { + if(fhandle->nameptr[i] == '.') + fhandle->nameptr[i] = 0; + } + + + fhandle->name = basename (fhandle->nameptr); + + /*call load_vstfx_library to actually load the .so into memory*/ + + if ((fhandle->dll = vstfx_load_vst_library (buf)) == NULL) + { + vstfx_unload (fhandle); + + free(buf); + + return NULL; + } + + /*Find the main entry point into the plugin*/ + + if ((fhandle->main_entry = (main_entry_t) dlsym(fhandle->dll, "main")) == NULL) + { + /*If it can't be found, unload the plugin and return a NULL handle*/ + + vstfx_unload (fhandle); + + free(buf); + + return NULL; + } + + free(buf); + + /*return the handle of the plugin*/ + + return fhandle; +} + +/*This unloads a plugin*/ + +int vstfx_unload (VSTFXHandle* fhandle) +{ + if (fhandle->plugincnt) + { + /*Still have plugin instances - can't unload the library + - actually dlclose keeps an instance count anyway*/ + + return -1; + } + + /*Valid plugin loaded?*/ + + if (fhandle->dll) + { + dlclose(fhandle->dll); + fhandle->dll = NULL; + } + + if (fhandle->nameptr) + { + free (fhandle->nameptr); + fhandle->name = NULL; + } + + /*Don't need the plugin handle any more*/ + + free (fhandle); + return 0; +} + +/*This instantiates a plugin*/ + +VSTFX* vstfx_instantiate (VSTFXHandle* fhandle, audioMasterCallback amc, void* userptr) +{ + VSTFX* vstfx = vstfx_new (); + + if(fhandle == NULL) + { + vstfx_error( "** ERROR ** VSTFX : The handle was NULL\n" ); + return NULL; + } + + if ((vstfx->plugin = fhandle->main_entry (amc)) == NULL) + { + vstfx_error ("** ERROR ** VSTFX : %s could not be instantiated :(\n", fhandle->name); + free (vstfx); + return NULL; + } + + vstfx->handle = fhandle; + vstfx->plugin->user = userptr; + + if (vstfx->plugin->magic != kEffectMagic) + { + vstfx_error ("** ERROR ** VSTFX : %s is not a VST plugin\n", fhandle->name); + free (vstfx); + return NULL; + } + + vstfx->plugin->dispatcher (vstfx->plugin, effOpen, 0, 0, 0, 0); + + /*May or May not need to 'switch the plugin on' here - unlikely + since FST doesn't and most plugins start up 'On' by default - I think this is the least of our worries*/ + + //vstfx->plugin->dispatcher (vstfx->plugin, effMainsChanged, 0, 1, NULL, 0); + + vstfx->vst_version = vstfx->plugin->dispatcher (vstfx->plugin, effGetVstVersion, 0, 0, 0, 0); + + vstfx->handle->plugincnt++; + vstfx->wantIdle = 0; + + return vstfx; +} + +/*Close a vstfx instance*/ + +void vstfx_close (VSTFX* vstfx) +{ + vstfx_destroy_editor(vstfx); + + if(vstfx->plugin) + { + vstfx->plugin->dispatcher (vstfx->plugin, effMainsChanged, 0, 0, NULL, 0); + + /*Calling dispatcher with effClose will cause the plugin's destructor to + be called, which will also remove the editor if it exists*/ + + vstfx->plugin->dispatcher (vstfx->plugin, effClose, 0, 0, 0, 0); + } + + if (vstfx->handle->plugincnt) + vstfx->handle->plugincnt--; + + /*vstfx_unload will unload the dll if the instance count allows - + we need to do this because some plugins keep their own instance count + and (JUCE) manages the plugin UI in its own thread. When the plugins + internal instance count reaches zero, JUCE stops the UI thread and won't + restart it until the next time the library is loaded. If we don't unload + the lib JUCE will never restart*/ + + + if (vstfx->handle->plugincnt) + { + return; + } + + /*Valid plugin loaded - so we can unload it and NULL the pointer + to it. We can't free the handle here because we don't know what else + might need it. It should be / is freed when the plugin is deleted*/ + + if (vstfx->handle->dll) + { + dlclose(vstfx->handle->dll); //dlclose keeps its own reference count + vstfx->handle->dll = NULL; + } +} + + +/*Get the XID of the plugin editor window*/ + +int vstfx_get_XID (VSTFX* vstfx) +{ + int id; + + /*Wait for the lock to become free - otherwise + the window might be in the process of being + created and we get bad Window errors when trying + to embed it in the GTK UI*/ + + pthread_mutex_lock(&vstfx->lock); + + /*The Window may be scheduled for creation + but not actually created by the gui_event_loop yet - + + spin here until it has been activated. Possible + deadlock if the window never gets activated but + should not be called here if the window doesn't + exist or will never exist*/ + + while(!(vstfx->been_activated)) + usleep(1000); + + id = vstfx->xid; + + pthread_mutex_unlock(&vstfx->lock); + + /*Finally it might be safe to return the ID - + problems will arise if we return either a zero ID + and GTK tries to socket it or if we return an ID + which hasn't yet become real to the server*/ + + return id; +} + +float htonf (float v) +{ + float result; + char * fin = (char*)&v; + char * fout = (char*)&result; + fout[0] = fin[3]; + fout[1] = fin[2]; + fout[2] = fin[1]; + fout[3] = fin[0]; + return result; +} + + +/*load_state and save_state do not appear to be needed (yet) in ardour +- untested at the moment, these are just replicas of the fst code*/ + + +#if 0 +int vstfx_load_state (VSTFX* vstfx, char * filename) +{ + FILE* f = fopen (filename, "rb"); + if(f) + { + char testMagic[sizeof (magic)]; + fread (&testMagic, sizeof (magic), 1, f); + if (strcmp (testMagic, magic)) + { + printf ("File corrupt\n"); + return FALSE; + } + + char productString[64]; + char vendorString[64]; + char effectName[64]; + char testString[64]; + unsigned length; + int success; + + fread (&length, sizeof (unsigned), 1, f); + length = htonl (length); + fread (productString, length, 1, f); + productString[length] = 0; + printf ("Product string: %s\n", productString); + + success = vstfx_call_dispatcher(vstfx, effGetProductString, 0, 0, testString, 0); + + if (success == 1) + { + if (strcmp (testString, productString) != 0) + { + printf ("Product string mismatch! Plugin has: %s\n", testString); + fclose (f); + return FALSE; + } + } + else if (length != 0) + { + printf ("Product string mismatch! Plugin has none.\n", testString); + fclose (f); + return FALSE; + } + + fread (&length, sizeof (unsigned), 1, f); + length = htonl (length); + fread (effectName, length, 1, f); + effectName[length] = 0; + printf ("Effect name: %s\n", effectName); + + success = vstfx_call_dispatcher(vstfx, effGetEffectName, 0, 0, testString, 0); + + if(success == 1) + { + if(strcmp(testString, effectName)!= 0) + { + printf ("Effect name mismatch! Plugin has: %s\n", testString); + fclose (f); + return FALSE; + } + } + else if(length != 0) + { + printf ("Effect name mismatch! Plugin has none.\n", testString); + fclose (f); + return FALSE; + } + + fread (&length, sizeof (unsigned), 1, f); + length = htonl (length); + fread (vendorString, length, 1, f); + vendorString[length] = 0; + + printf ("Vendor string: %s\n", vendorString); + + success = vstfx_call_dispatcher(vstfx, effGetVendorString, 0, 0, testString, 0); + if(success == 1) + { + if (strcmp(testString, vendorString)!= 0) + { + printf ("Vendor string mismatch! Plugin has: %s\n", testString); + fclose (f); + return FALSE; + } + } + else if(length != 0) + { + printf ("Vendor string mismatch! Plugin has none.\n", testString); + fclose (f); + return FALSE; + } + + int numParam; + unsigned i; + fread (&numParam, sizeof (int), 1, f); + numParam = htonl (numParam); + + for (i = 0; i < numParam; ++i) + { + float val; + fread (&val, sizeof (float), 1, f); + val = htonf (val); + + pthread_mutex_lock(&vstfx->lock ); + vstfx->plugin->setParameter(vstfx->plugin, i, val); + pthread_mutex_unlock(&vstfx->lock ); + } + + int bytelen; + + fread (&bytelen, sizeof (int), 1, f); + bytelen = htonl (bytelen); + + if (bytelen) + { + char * buf = malloc (bytelen); + fread (buf, bytelen, 1, f); + + vstfx_call_dispatcher(vstfx, 24, 0, bytelen, buf, 0); + free (buf); + } + } + else + { + printf ("Could not open state file\n"); + return FALSE; + } + return TRUE; + +} +#endif + +int vstfx_save_state (VSTFX* vstfx, char * filename) +{ + FILE* f = fopen (filename, "wb"); + if (f) + { + int bytelen; + int numParams = vstfx->plugin->numParams; + int i; + char productString[64]; + char effectName[64]; + char vendorString[64]; + int success; + + /* write header */ + + fprintf(f, "<plugin_state>\n"); + + success = vstfx_call_dispatcher(vstfx, effGetProductString, 0, 0, productString, 0); + + if(success == 1) + { + fprintf (f, " <check field=\"productString\" value=\"%s\"/>\n", productString); + } + else + { + printf ("No product string\n"); + } + + success = vstfx_call_dispatcher(vstfx, effGetEffectName, 0, 0, effectName, 0); + + if(success == 1) + { + fprintf (f, " <check field=\"effectName\" value=\"%s\"/>\n", effectName); + printf ("Effect name: %s\n", effectName); + } + else + { + printf ("No effect name\n"); + } + + success = vstfx_call_dispatcher(vstfx, effGetVendorString, 0, 0, vendorString, 0); + + if( success == 1 ) + { + fprintf (f, " <check field=\"vendorString\" value=\"%s\"/>\n", vendorString); + printf ("Vendor string: %s\n", vendorString); + } + else + { + printf ("No vendor string\n"); + } + + + if(vstfx->plugin->flags & 32 ) + { + numParams = 0; + } + + for(i=0; i < numParams; i++) + { + float val; + + pthread_mutex_lock( &vstfx->lock ); + val = vstfx->plugin->getParameter(vstfx->plugin, i ); + pthread_mutex_unlock( &vstfx->lock ); + fprintf( f, " <param index=\"%d\" value=\"%f\"/>\n", i, val ); + } + + if(vstfx->plugin->flags & 32 ) + { + printf( "getting chunk...\n" ); + void * chunk; + bytelen = vstfx_call_dispatcher(vstfx, 23, 0, 0, &chunk, 0 ); + printf( "got tha chunk..\n" ); + if( bytelen ) + { + if( bytelen < 0 ) + { + printf( "Chunke len < 0 !!! Not saving chunk.\n" ); + } + else + { + //char *encoded = g_base64_encode( chunk, bytelen ); + //fprintf( f, " <chunk size=\"%d\">\n %s\n </chunk>\n", bytelen, encoded ); + //g_free( encoded ); + } + } + } + + fprintf( f, "</plugin_state>\n" ); + fclose( f ); + } + else + { + printf ("Could not open state file\n"); + return FALSE; + } + return TRUE; +} + |