From: Carl Hetherington Date: Wed, 23 Nov 2011 19:31:04 +0000 (+0000) Subject: Rename linux VST files to be more friendly. X-Git-Tag: 3.0-beta2~350 X-Git-Url: https://main.carlh.net/gitweb/?p=ardour.git;a=commitdiff_plain;h=7c87036ee3ba9dc4cc8b6f60c5a9e6dd399b1285 Rename linux VST files to be more friendly. git-svn-id: svn://localhost/ardour2/branches/3.0@10808 d708f5d6-7413-0410-9779-e7cbd77b26cf --- diff --git a/gtk2_ardour/linux_vst_gui_support.cc b/gtk2_ardour/linux_vst_gui_support.cc new file mode 100644 index 0000000000..161b6495c1 --- /dev/null +++ b/gtk2_ardour/linux_vst_gui_support.cc @@ -0,0 +1,759 @@ +/******************************************************************/ +/** 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 +#include +#include +#include +#include + +#include +#include +#include + +#include "ardour/linux_vst_support.h" + +#include +#include +#include +#include +#include +#include +#include + +struct ERect{ + short top; + short left; + short bottom; + short right; +}; + +static pthread_mutex_t plugin_mutex; + +static VSTState * 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; + +/*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, VSTState* 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->linux_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->linux_plugin_ui_window) { + XMoveWindow (LXVST_XDisplay, vstfx->linux_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->linux_window)) { + + Window PluginUIWindowID = event->xreparent.window; + + vstfx->linux_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->linux_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); +} + +static void +maybe_set_program (VSTState* vstfx) +{ + 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); + } + + vstfx->want_program = -1; + } + + if (vstfx->want_chunk == 1) { + vstfx->plugin->dispatcher (vstfx->plugin, 24 /* effSetChunk */, 1, vstfx->wanted_chunk_size, vstfx->wanted_chunk, 0); + vstfx->want_chunk = 0; + } +} + +/** 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) +{ + VSTState* 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->linux_window) { + vstfx->plugin->dispatcher (vstfx->plugin, effEditClose, 0, 0, NULL, 0.0); + + XDestroyWindow (LXVST_XDisplay, vstfx->linux_window); + /* FIXME - probably safe to assume we never have an XID of 0 but not explicitly true */ + vstfx->linux_window = 0; + 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*/ + + /* FIXME - probably safe to assume 0 is not a valid XID but not explicitly true */ + if (vstfx->linux_window == 0) { + 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() */ + } + } + + maybe_set_program (vstfx); + vstfx->want_program = -1; + vstfx->want_chunk = 0; + + /*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 (VSTState* vstfx) +{ + pthread_mutex_lock (&plugin_mutex); + + /* Add the new VSTFX instance to the linked list */ + + if (vstfx_first == NULL) { + vstfx_first = vstfx; + } else { + VSTState* 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->linux_window) { + pthread_cond_wait (&vstfx->window_status_change, &vstfx->lock); + } + + pthread_mutex_unlock (&vstfx->lock); + + if (!vstfx->linux_window) { + return -1; + } + + return 0; +} + + +/*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 (VSTState* 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->linux_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 (VSTState* 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->linux_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; +} + +/** Destroy the editor window */ +void +vstfx_destroy_editor (VSTState* vstfx) +{ + pthread_mutex_lock (&vstfx->lock); + if (vstfx->linux_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 (VSTState* 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 + */ + + VSTState* p; + VSTState* 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; + } +} + diff --git a/gtk2_ardour/lxvst_plugin_ui.cc b/gtk2_ardour/lxvst_plugin_ui.cc index 31bb6c879b..5d86f428c2 100644 --- a/gtk2_ardour/lxvst_plugin_ui.cc +++ b/gtk2_ardour/lxvst_plugin_ui.cc @@ -18,7 +18,7 @@ */ #include "ardour/lxvst_plugin.h" -#include "ardour/vstfx.h" +#include "ardour/linux_vst_support.h" #include "lxvst_plugin_ui.h" #include "ardour_ui.h" #include diff --git a/gtk2_ardour/vstfxwin.cc b/gtk2_ardour/vstfxwin.cc deleted file mode 100755 index 0687d06b71..0000000000 --- a/gtk2_ardour/vstfxwin.cc +++ /dev/null @@ -1,759 +0,0 @@ -/******************************************************************/ -/** 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 -#include -#include -#include -#include - -#include -#include -#include - -#include "ardour/vstfx.h" - -#include -#include -#include -#include -#include -#include -#include - -struct ERect{ - short top; - short left; - short bottom; - short right; -}; - -static pthread_mutex_t plugin_mutex; - -static VSTState * 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; - -/*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, VSTState* 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->linux_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->linux_plugin_ui_window) { - XMoveWindow (LXVST_XDisplay, vstfx->linux_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->linux_window)) { - - Window PluginUIWindowID = event->xreparent.window; - - vstfx->linux_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->linux_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); -} - -static void -maybe_set_program (VSTState* vstfx) -{ - 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); - } - - vstfx->want_program = -1; - } - - if (vstfx->want_chunk == 1) { - vstfx->plugin->dispatcher (vstfx->plugin, 24 /* effSetChunk */, 1, vstfx->wanted_chunk_size, vstfx->wanted_chunk, 0); - vstfx->want_chunk = 0; - } -} - -/** 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) -{ - VSTState* 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->linux_window) { - vstfx->plugin->dispatcher (vstfx->plugin, effEditClose, 0, 0, NULL, 0.0); - - XDestroyWindow (LXVST_XDisplay, vstfx->linux_window); - /* FIXME - probably safe to assume we never have an XID of 0 but not explicitly true */ - vstfx->linux_window = 0; - 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*/ - - /* FIXME - probably safe to assume 0 is not a valid XID but not explicitly true */ - if (vstfx->linux_window == 0) { - 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() */ - } - } - - maybe_set_program (vstfx); - vstfx->want_program = -1; - vstfx->want_chunk = 0; - - /*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 (VSTState* vstfx) -{ - pthread_mutex_lock (&plugin_mutex); - - /* Add the new VSTFX instance to the linked list */ - - if (vstfx_first == NULL) { - vstfx_first = vstfx; - } else { - VSTState* 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->linux_window) { - pthread_cond_wait (&vstfx->window_status_change, &vstfx->lock); - } - - pthread_mutex_unlock (&vstfx->lock); - - if (!vstfx->linux_window) { - return -1; - } - - return 0; -} - - -/*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 (VSTState* 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->linux_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 (VSTState* 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->linux_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; -} - -/** Destroy the editor window */ -void -vstfx_destroy_editor (VSTState* vstfx) -{ - pthread_mutex_lock (&vstfx->lock); - if (vstfx->linux_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 (VSTState* 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 - */ - - VSTState* p; - VSTState* 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; - } -} - diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index 65e1da5186..3a5a301a13 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -399,7 +399,7 @@ def build(bld): obj.uselib += ' X11 ' if bld.is_defined('LXVST_SUPPORT'): - obj.source += [ 'vstfxwin.cc', 'lxvst_plugin_ui.cc' ] + obj.source += [ 'linux_vst_gui_support.cc', 'lxvst_plugin_ui.cc' ] obj.defines += [ 'LXVST_SUPPORT' ] obj.uselib += ' X11 ' diff --git a/libs/ardour/ardour/vstfx.h b/libs/ardour/ardour/vstfx.h deleted file mode 100755 index 31f8bafe51..0000000000 --- a/libs/ardour/ardour/vstfx.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef __vstfx_h__ -#define __vstfx_h__ - -#include -#include -#include -#include - -#include "ardour/vst_types.h" - -/******************************************************************************************/ -/*VSTFX - an engine to manage native linux VST plugins - derived from FST for Windows VSTs*/ -/******************************************************************************************/ - -extern void (*vstfx_error_callback)(const char *msg); - -void vstfx_set_error_function (void (*func)(const char *)); - -void vstfx_error (const char *fmt, ...); - -/*API to vstfx*/ - -extern int vstfx_launch_editor (VSTState *); -extern int vstfx_init (void *); -extern void vstfx_exit (); -extern VSTHandle * vstfx_load (const char*); -extern int vstfx_unload (VSTHandle *); -extern VSTState * vstfx_instantiate (VSTHandle *, audioMasterCallback, void *); -extern void vstfx_close (VSTState*); - -extern int vstfx_create_editor (VSTState *); -extern int vstfx_run_editor (VSTState *); -extern void vstfx_destroy_editor (VSTState *); - -extern VSTInfo * vstfx_get_info (char *); -extern void vstfx_free_info (VSTInfo *); -extern void vstfx_event_loop_remove_plugin (VSTState *); -extern int vstfx_call_dispatcher (VSTState *, int, int, int, void *, float); - -/** Load a plugin state from a file.**/ - -extern int vstfx_load_state (VSTState* vstfx, char * filename); - -/** Save a plugin state to a file.**/ - -extern bool vstfx_save_state (VSTState* vstfx, char * filename); - - -#endif /* __vstfx_h__ */ diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index e07437ce9d..5a2a2addd6 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -34,7 +34,7 @@ #endif #ifdef LXVST_SUPPORT -#include "ardour/vstfx.h" +#include "ardour/linux_vst_support.h" #endif #ifdef AUDIOUNIT_SUPPORT diff --git a/libs/ardour/linux_vst_info_file.cc b/libs/ardour/linux_vst_info_file.cc new file mode 100644 index 0000000000..58954c7261 --- /dev/null +++ b/libs/ardour/linux_vst_info_file.cc @@ -0,0 +1,423 @@ +/***********************************************************/ +/*vstfx infofile - module to manage info files */ +/*containing cached information about a plugin. e.g. its */ +/*name, creator etc etc */ +/***********************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "ardour/linux_vst_support.h" + +#define MAX_STRING_LEN 256 + +static char* read_string(FILE *fp) +{ + char buf[MAX_STRING_LEN]; + + if (!fgets( buf, MAX_STRING_LEN, fp )) { + return 0; + } + + if(strlen(buf) < MAX_STRING_LEN) { + if (strlen(buf)) { + buf[strlen(buf)-1] = 0; + } + return strdup(buf); + } else { + return 0; + } +} + +static VSTInfo * +load_vstfx_info_file (FILE* fp) +{ + VSTInfo *info; + int i; + + if ((info = (VSTInfo*) malloc (sizeof (VSTInfo))) == 0) { + return 0; + } + + if((info->name = read_string(fp)) == 0) goto error; + if((info->creator = read_string(fp)) == 0) goto error; + if(1 != fscanf(fp, "%d\n", &info->UniqueID)) goto error; + if((info->Category = read_string(fp)) == 0) goto error; + if(1 != fscanf(fp, "%d\n", &info->numInputs)) goto error; + if(1 != fscanf(fp, "%d\n", &info->numOutputs)) goto error; + if(1 != fscanf(fp, "%d\n", &info->numParams)) goto error; + if(1 != fscanf(fp, "%d\n", &info->wantMidi)) goto error; + if(1 != fscanf(fp, "%d\n", &info->hasEditor)) goto error; + if(1 != fscanf(fp, "%d\n", &info->canProcessReplacing)) goto error; + + if((info->ParamNames = (char **) malloc(sizeof(char*)*info->numParams)) == 0) { + goto error; + } + + for (i=0; inumParams; i++) { + if((info->ParamNames[i] = read_string(fp)) == 0) goto error; + } + + if ((info->ParamLabels = (char **) malloc(sizeof(char*)*info->numParams)) == 0) { + goto error; + } + + for (i=0; i < info->numParams; i++) { + if((info->ParamLabels[i] = read_string(fp)) == 0) goto error; + } + + return info; + + error: + free( info ); + return 0; +} + +static int +save_vstfx_info_file (VSTInfo *info, FILE* fp) +{ + int i; + + if (info == 0) { + vstfx_error("** ERROR ** VSTFXinfofile : info ptr is 0\n"); + return -1; + } + + if (fp == 0) { + vstfx_error("** ERROR ** VSTFXinfofile : file ptr is 0\n"); + return -1; + } + + fprintf( fp, "%s\n", info->name ); + fprintf( fp, "%s\n", info->creator ); + fprintf( fp, "%d\n", info->UniqueID ); + fprintf( fp, "%s\n", info->Category ); + fprintf( fp, "%d\n", info->numInputs ); + fprintf( fp, "%d\n", info->numOutputs ); + fprintf( fp, "%d\n", info->numParams ); + fprintf( fp, "%d\n", info->wantMidi ); + fprintf( fp, "%d\n", info->hasEditor ); + fprintf( fp, "%d\n", info->canProcessReplacing ); + + for (i=0; i < info->numParams; i++) { + fprintf(fp, "%s\n", info->ParamNames[i]); + } + + for (i=0; i < info->numParams; i++) { + fprintf(fp, "%s\n", info->ParamLabels[i]); + } + + return 0; +} + +static char* vstfx_infofile_stat (char *dllpath, struct stat* statbuf, int personal) +{ + char* path; + char* dir_path; + char* basename; + char* base; + size_t blen; + + if (strstr (dllpath, ".so" ) == 0) { + return 0; + } + + if (personal) { + dir_path = g_build_filename (g_get_home_dir(), ".fst", NULL); + } else { + dir_path = g_path_get_dirname (dllpath); + } + + base = g_path_get_basename (dllpath); + blen = strlen (base) + 2; // null char and '.' + basename = (char*) g_malloc (blen); + snprintf (basename, blen, ".%s.fsi", base); + g_free (base); + + path = g_build_filename (dir_path, basename, NULL); + + g_free (dir_path); + g_free (basename); + + + if (g_file_test (path, GFileTest (G_FILE_TEST_EXISTS|G_FILE_TEST_IS_REGULAR))) { + + /* info file exists in same location as the shared object, so + check if its current and up to date + */ + + + struct stat dllstat; + + if (stat (dllpath, &dllstat) == 0) { + if (stat(path, statbuf) == 0) { + if (dllstat.st_mtime <= statbuf->st_mtime) { + /* plugin is older than info file */ + return path; + } + } + } + } + + g_free (path); + + return 0; +} + + +static FILE* vstfx_infofile_for_read (char* dllpath) +{ + struct stat own_statbuf; + struct stat sys_statbuf; + char *own_info; + char *sys_info; + + own_info = vstfx_infofile_stat (dllpath, &own_statbuf, 1); + sys_info = vstfx_infofile_stat (dllpath, &sys_statbuf, 0); + + if (own_info) { + if (sys_info) { + if (own_statbuf.st_mtime <= sys_statbuf.st_mtime) { + /* system info file is newer, use it */ + return fopen (sys_info, "r"); + } + } else { + return fopen (own_info, "r"); + } + } + + return 0; +} + +static FILE* vstfx_infofile_create (char* dllpath, int personal) +{ + char* path; + char* dir_path; + char* basename; + char* base; + size_t blen; + + if (strstr (dllpath, ".so" ) == 0) { + return 0; + } + + if (personal) { + dir_path = g_build_filename (g_get_home_dir(), ".fst", NULL); + + /* if the directory doesn't yet exist, try to create it */ + + if (!g_file_test (dir_path, G_FILE_TEST_IS_DIR)) { + if (g_mkdir (dir_path, 0700)) { + return 0; + } + } + + } else { + dir_path = g_path_get_dirname (dllpath); + } + + base = g_path_get_basename (dllpath); + blen = strlen (base) + 2; // null char and '.' + basename = (char*) g_malloc (blen); + snprintf (basename, blen, ".%s.fsi", base); + g_free (base); + + path = g_build_filename (dir_path, basename, NULL); + + g_free (dir_path); + g_free (basename); + + FILE* f = fopen (path, "w"); + g_free (path); + + return f; +} + +static FILE* vstfx_infofile_for_write (char* dllpath) +{ + FILE* f; + + if ((f = vstfx_infofile_create (dllpath, 0)) == 0) { + f = vstfx_infofile_create (dllpath, 1); + } + + return f; +} + +static +int vstfx_can_midi (VSTState* vstfx) +{ + AEffect *plugin = vstfx->plugin; + + int vst_version = plugin->dispatcher (plugin, effGetVstVersion, 0, 0, 0, 0.0f); + + if (vst_version >= 2) + { + /* should we send it VST events (i.e. MIDI) */ + + if ((plugin->flags & effFlagsIsSynth) || (plugin->dispatcher (plugin, effCanDo, 0, 0,(void*) "receiveVstEvents", 0.0f) > 0)) + return -1; + } + return false; +} + +static VSTInfo * +vstfx_info_from_plugin (VSTState* vstfx) +{ + VSTInfo* info = (VSTInfo*) malloc (sizeof (VSTInfo)); + + AEffect *plugin; + int i; + + /*We need to init the creator because some plugins + fail to implement getVendorString, and so won't stuff the + string with any name*/ + + char creator[65] = "Unknown\0"; + + if(!vstfx) + { + vstfx_error( "** ERROR ** VSTFXinfofile : vstfx ptr is 0\n" ); + return 0; + } + + if(!info) + return 0; + + plugin = vstfx->plugin; + + info->name = strdup(vstfx->handle->name ); + + /*If the plugin doesn't bother to implement GetVendorString we will + have pre-stuffed the string with 'Unkown' */ + + plugin->dispatcher (plugin, effGetVendorString, 0, 0, creator, 0); + + /*Some plugins DO implement GetVendorString, but DON'T put a name in it + so if its just a zero length string we replace it with 'Unknown' */ + + if (strlen(creator) == 0) { + info->creator = strdup("Unknown"); + } else { + info->creator = strdup (creator); + } + + info->UniqueID = plugin->uniqueID; + + info->Category = strdup("None"); // FIXME: + info->numInputs = plugin->numInputs; + info->numOutputs = plugin->numOutputs; + info->numParams = plugin->numParams; + info->wantMidi = vstfx_can_midi(vstfx); + info->hasEditor = plugin->flags & effFlagsHasEditor ? true : false; + info->canProcessReplacing = plugin->flags & effFlagsCanReplacing ? true : false; + info->ParamNames = (char **) malloc(sizeof(char*)*info->numParams); + info->ParamLabels = (char **) malloc(sizeof(char*)*info->numParams); + + for(i=0; i < info->numParams; i++) { + char name[64]; + char label[64]; + + /*Not all plugins give parameters labels as well as names*/ + + strcpy(name, "No Name"); + strcpy(label, "No Label"); + + plugin->dispatcher (plugin, effGetParamName, i, 0, name, 0); + info->ParamNames[i] = strdup(name); + + //NOTE: 'effGetParamLabel' is no longer defined in vestige headers + //plugin->dispatcher (plugin, effGetParamLabel, i, 0, label, 0); + info->ParamLabels[i] = strdup(label); + } + return info; +} + +/* A simple 'dummy' audiomaster callback which should be ok, +we will only be instantiating the plugin in order to get its info*/ + +static intptr_t +simple_master_callback (AEffect *, int32_t opcode, int32_t, intptr_t, void *, float) +{ + if (opcode == audioMasterVersion) + return 2; + else + return 0; +} + +/*Try to get plugin info - first by looking for a .fsi cache of the +data, and if that doesn't exist, load the plugin, get its data and +then cache it for future ref*/ + +VSTInfo * +vstfx_get_info (char* dllpath) +{ + FILE* infofile; + VSTHandle* h; + VSTState* vstfx; + VSTInfo* info; + + if ((infofile = vstfx_infofile_for_read (dllpath)) != 0) { + VSTInfo *info; + info = load_vstfx_info_file (infofile); + fclose (infofile); + return info; + } + + if(!(h = vstfx_load(dllpath))) + return 0; + + if(!(vstfx = vstfx_instantiate(h, simple_master_callback, 0))) { + vstfx_unload(h); + vstfx_error( "** ERROR ** VSTFXinfofile : Instantiate failed\n" ); + return 0; + } + + infofile = vstfx_infofile_for_write (dllpath); + + if(!infofile) { + vstfx_close(vstfx); + vstfx_unload(h); + vstfx_error("cannot create new FST info file for plugin"); + return 0; + } + + info = vstfx_info_from_plugin(vstfx); + + save_vstfx_info_file(info, infofile); + fclose (infofile); + + vstfx_close(vstfx); + vstfx_unload(h); + + return info; +} + +void +vstfx_free_info (VSTInfo *info) +{ + for (int i = 0; i < info->numParams; i++) { + free (info->ParamNames[i]); + free (info->ParamLabels[i]); + } + + free (info->name); + free (info->creator); + free (info->Category); + free (info); +} + + diff --git a/libs/ardour/linux_vst_support.cc b/libs/ardour/linux_vst_support.cc new file mode 100644 index 0000000000..e6f2735351 --- /dev/null +++ b/libs/ardour/linux_vst_support.cc @@ -0,0 +1,487 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ardour/linux_vst_support.h" +#include "pbd/error.h" + +/***********************************************************/ +/* VSTFX - A set of modules for managing linux VST plugins */ +/* vstfx.cc, vstfxwin.cc and vstfxinfofile.cc */ +/***********************************************************/ + +/*Simple error handler stuff for VSTFX*/ + +void vstfx_error (const char *fmt, ...) +{ + va_list ap; + char buffer[512]; + + va_start (ap, fmt); + vsnprintf (buffer, sizeof(buffer), fmt, ap); + vstfx_error_callback (buffer); + va_end (ap); +} + +/*default error handler callback*/ + +void default_vstfx_error_callback (const char *desc) +{ + PBD::error << desc << endmsg; +} + +void (*vstfx_error_callback)(const char *desc) = &default_vstfx_error_callback; + +/* --- */ + +/*Create and return a pointer to a new VSTFX handle*/ + +VSTHandle * +vstfx_handle_new () +{ + VSTHandle* vstfx = (VSTHandle *) calloc (1, sizeof (VSTHandle)); + return vstfx; +} + +/*Create and return a pointer to a new vstfx instance*/ + +VSTState * +vstfx_new () +{ + VSTState* vstfx = (VSTState *) calloc (1, sizeof (VSTState)); + + /*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->n_pending_keys = 0; + vstfx->has_editor = 0; + vstfx->program_set_without_editor = 0; + vstfx->linux_window = 0; + vstfx->linux_plugin_ui_window = 0; + vstfx->eventProc = NULL; + vstfx->extra_data = NULL; + vstfx->want_resize = 0; + + return vstfx; +} + +/*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*/ + +VSTHandle * +vstfx_load (const char *path) +{ + char* buf = NULL; + VSTHandle* 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 (VSTHandle* 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*/ + +VSTState * +vstfx_instantiate (VSTHandle* fhandle, audioMasterCallback amc, void* userptr) +{ + VSTState* 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 (VSTState* 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; + } +} + + +bool +vstfx_save_state (VSTState* 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, "\n"); + + success = vstfx_call_dispatcher(vstfx, effGetProductString, 0, 0, productString, 0); + + if(success == 1) + { + fprintf (f, " \n", productString); + } + else + { + printf ("No product string\n"); + } + + success = vstfx_call_dispatcher(vstfx, effGetEffectName, 0, 0, effectName, 0); + + if(success == 1) + { + fprintf (f, " \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, " \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, " \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, " \n %s\n \n", bytelen, encoded ); + //g_free( encoded ); + } + } + } + + fprintf( f, "\n" ); + fclose( f ); + } + else + { + printf ("Could not open state file\n"); + return false; + } + return true; +} + +/*Set up a call to the plugins 'dispatcher' function*/ + +int vstfx_call_dispatcher (VSTState* 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; +} diff --git a/libs/ardour/lxvst_plugin.cc b/libs/ardour/lxvst_plugin.cc index f14d052fd4..6e30e0c443 100755 --- a/libs/ardour/lxvst_plugin.cc +++ b/libs/ardour/lxvst_plugin.cc @@ -17,7 +17,7 @@ */ -#include "ardour/vstfx.h" +#include "ardour/linux_vst_support.h" #include "ardour/session.h" #include "ardour/lxvst_plugin.h" diff --git a/libs/ardour/plugin_manager.cc b/libs/ardour/plugin_manager.cc index 0cfbdd17eb..54a130ec9c 100644 --- a/libs/ardour/plugin_manager.cc +++ b/libs/ardour/plugin_manager.cc @@ -31,14 +31,14 @@ #include #ifdef WINDOWS_VST_SUPPORT -#include +#include "fst.h" #include "pbd/basename.h" #include #endif // WINDOWS_VST_SUPPORT #ifdef LXVST_SUPPORT -#include -#include +#include "ardour/linux_vst_support.h" +#include "pbd/basename.h" #include #endif //LXVST_SUPPORT diff --git a/libs/ardour/test/dummy_lxvst.cc b/libs/ardour/test/dummy_lxvst.cc index 1c8b63e466..9f93b48c74 100644 --- a/libs/ardour/test/dummy_lxvst.cc +++ b/libs/ardour/test/dummy_lxvst.cc @@ -1,6 +1,6 @@ /* Dummy LXVST methods so that libardour can be linked against the test code */ -#include "ardour/vstfx.h" +#include "ardour/linux_vst_support.h" int vstfx_init (void* ptr) diff --git a/libs/ardour/vstfx.cc b/libs/ardour/vstfx.cc deleted file mode 100755 index 5046afb737..0000000000 --- a/libs/ardour/vstfx.cc +++ /dev/null @@ -1,487 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ardour/vstfx.h" -#include "pbd/error.h" - -/***********************************************************/ -/* VSTFX - A set of modules for managing linux VST plugins */ -/* vstfx.cc, vstfxwin.cc and vstfxinfofile.cc */ -/***********************************************************/ - -/*Simple error handler stuff for VSTFX*/ - -void vstfx_error (const char *fmt, ...) -{ - va_list ap; - char buffer[512]; - - va_start (ap, fmt); - vsnprintf (buffer, sizeof(buffer), fmt, ap); - vstfx_error_callback (buffer); - va_end (ap); -} - -/*default error handler callback*/ - -void default_vstfx_error_callback (const char *desc) -{ - PBD::error << desc << endmsg; -} - -void (*vstfx_error_callback)(const char *desc) = &default_vstfx_error_callback; - -/* --- */ - -/*Create and return a pointer to a new VSTFX handle*/ - -VSTHandle * -vstfx_handle_new () -{ - VSTHandle* vstfx = (VSTHandle *) calloc (1, sizeof (VSTHandle)); - return vstfx; -} - -/*Create and return a pointer to a new vstfx instance*/ - -VSTState * -vstfx_new () -{ - VSTState* vstfx = (VSTState *) calloc (1, sizeof (VSTState)); - - /*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->n_pending_keys = 0; - vstfx->has_editor = 0; - vstfx->program_set_without_editor = 0; - vstfx->linux_window = 0; - vstfx->linux_plugin_ui_window = 0; - vstfx->eventProc = NULL; - vstfx->extra_data = NULL; - vstfx->want_resize = 0; - - return vstfx; -} - -/*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*/ - -VSTHandle * -vstfx_load (const char *path) -{ - char* buf = NULL; - VSTHandle* 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 (VSTHandle* 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*/ - -VSTState * -vstfx_instantiate (VSTHandle* fhandle, audioMasterCallback amc, void* userptr) -{ - VSTState* 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 (VSTState* 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; - } -} - - -bool -vstfx_save_state (VSTState* 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, "\n"); - - success = vstfx_call_dispatcher(vstfx, effGetProductString, 0, 0, productString, 0); - - if(success == 1) - { - fprintf (f, " \n", productString); - } - else - { - printf ("No product string\n"); - } - - success = vstfx_call_dispatcher(vstfx, effGetEffectName, 0, 0, effectName, 0); - - if(success == 1) - { - fprintf (f, " \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, " \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, " \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, " \n %s\n \n", bytelen, encoded ); - //g_free( encoded ); - } - } - } - - fprintf( f, "\n" ); - fclose( f ); - } - else - { - printf ("Could not open state file\n"); - return false; - } - return true; -} - -/*Set up a call to the plugins 'dispatcher' function*/ - -int vstfx_call_dispatcher (VSTState* 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; -} diff --git a/libs/ardour/vstfxinfofile.cc b/libs/ardour/vstfxinfofile.cc deleted file mode 100755 index 070c646dbe..0000000000 --- a/libs/ardour/vstfxinfofile.cc +++ /dev/null @@ -1,423 +0,0 @@ -/***********************************************************/ -/*vstfx infofile - module to manage info files */ -/*containing cached information about a plugin. e.g. its */ -/*name, creator etc etc */ -/***********************************************************/ - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include "ardour/vstfx.h" - -#define MAX_STRING_LEN 256 - -static char* read_string(FILE *fp) -{ - char buf[MAX_STRING_LEN]; - - if (!fgets( buf, MAX_STRING_LEN, fp )) { - return 0; - } - - if(strlen(buf) < MAX_STRING_LEN) { - if (strlen(buf)) { - buf[strlen(buf)-1] = 0; - } - return strdup(buf); - } else { - return 0; - } -} - -static VSTInfo * -load_vstfx_info_file (FILE* fp) -{ - VSTInfo *info; - int i; - - if ((info = (VSTInfo*) malloc (sizeof (VSTInfo))) == 0) { - return 0; - } - - if((info->name = read_string(fp)) == 0) goto error; - if((info->creator = read_string(fp)) == 0) goto error; - if(1 != fscanf(fp, "%d\n", &info->UniqueID)) goto error; - if((info->Category = read_string(fp)) == 0) goto error; - if(1 != fscanf(fp, "%d\n", &info->numInputs)) goto error; - if(1 != fscanf(fp, "%d\n", &info->numOutputs)) goto error; - if(1 != fscanf(fp, "%d\n", &info->numParams)) goto error; - if(1 != fscanf(fp, "%d\n", &info->wantMidi)) goto error; - if(1 != fscanf(fp, "%d\n", &info->hasEditor)) goto error; - if(1 != fscanf(fp, "%d\n", &info->canProcessReplacing)) goto error; - - if((info->ParamNames = (char **) malloc(sizeof(char*)*info->numParams)) == 0) { - goto error; - } - - for (i=0; inumParams; i++) { - if((info->ParamNames[i] = read_string(fp)) == 0) goto error; - } - - if ((info->ParamLabels = (char **) malloc(sizeof(char*)*info->numParams)) == 0) { - goto error; - } - - for (i=0; i < info->numParams; i++) { - if((info->ParamLabels[i] = read_string(fp)) == 0) goto error; - } - - return info; - - error: - free( info ); - return 0; -} - -static int -save_vstfx_info_file (VSTInfo *info, FILE* fp) -{ - int i; - - if (info == 0) { - vstfx_error("** ERROR ** VSTFXinfofile : info ptr is 0\n"); - return -1; - } - - if (fp == 0) { - vstfx_error("** ERROR ** VSTFXinfofile : file ptr is 0\n"); - return -1; - } - - fprintf( fp, "%s\n", info->name ); - fprintf( fp, "%s\n", info->creator ); - fprintf( fp, "%d\n", info->UniqueID ); - fprintf( fp, "%s\n", info->Category ); - fprintf( fp, "%d\n", info->numInputs ); - fprintf( fp, "%d\n", info->numOutputs ); - fprintf( fp, "%d\n", info->numParams ); - fprintf( fp, "%d\n", info->wantMidi ); - fprintf( fp, "%d\n", info->hasEditor ); - fprintf( fp, "%d\n", info->canProcessReplacing ); - - for (i=0; i < info->numParams; i++) { - fprintf(fp, "%s\n", info->ParamNames[i]); - } - - for (i=0; i < info->numParams; i++) { - fprintf(fp, "%s\n", info->ParamLabels[i]); - } - - return 0; -} - -static char* vstfx_infofile_stat (char *dllpath, struct stat* statbuf, int personal) -{ - char* path; - char* dir_path; - char* basename; - char* base; - size_t blen; - - if (strstr (dllpath, ".so" ) == 0) { - return 0; - } - - if (personal) { - dir_path = g_build_filename (g_get_home_dir(), ".fst", NULL); - } else { - dir_path = g_path_get_dirname (dllpath); - } - - base = g_path_get_basename (dllpath); - blen = strlen (base) + 2; // null char and '.' - basename = (char*) g_malloc (blen); - snprintf (basename, blen, ".%s.fsi", base); - g_free (base); - - path = g_build_filename (dir_path, basename, NULL); - - g_free (dir_path); - g_free (basename); - - - if (g_file_test (path, GFileTest (G_FILE_TEST_EXISTS|G_FILE_TEST_IS_REGULAR))) { - - /* info file exists in same location as the shared object, so - check if its current and up to date - */ - - - struct stat dllstat; - - if (stat (dllpath, &dllstat) == 0) { - if (stat(path, statbuf) == 0) { - if (dllstat.st_mtime <= statbuf->st_mtime) { - /* plugin is older than info file */ - return path; - } - } - } - } - - g_free (path); - - return 0; -} - - -static FILE* vstfx_infofile_for_read (char* dllpath) -{ - struct stat own_statbuf; - struct stat sys_statbuf; - char *own_info; - char *sys_info; - - own_info = vstfx_infofile_stat (dllpath, &own_statbuf, 1); - sys_info = vstfx_infofile_stat (dllpath, &sys_statbuf, 0); - - if (own_info) { - if (sys_info) { - if (own_statbuf.st_mtime <= sys_statbuf.st_mtime) { - /* system info file is newer, use it */ - return fopen (sys_info, "r"); - } - } else { - return fopen (own_info, "r"); - } - } - - return 0; -} - -static FILE* vstfx_infofile_create (char* dllpath, int personal) -{ - char* path; - char* dir_path; - char* basename; - char* base; - size_t blen; - - if (strstr (dllpath, ".so" ) == 0) { - return 0; - } - - if (personal) { - dir_path = g_build_filename (g_get_home_dir(), ".fst", NULL); - - /* if the directory doesn't yet exist, try to create it */ - - if (!g_file_test (dir_path, G_FILE_TEST_IS_DIR)) { - if (g_mkdir (dir_path, 0700)) { - return 0; - } - } - - } else { - dir_path = g_path_get_dirname (dllpath); - } - - base = g_path_get_basename (dllpath); - blen = strlen (base) + 2; // null char and '.' - basename = (char*) g_malloc (blen); - snprintf (basename, blen, ".%s.fsi", base); - g_free (base); - - path = g_build_filename (dir_path, basename, NULL); - - g_free (dir_path); - g_free (basename); - - FILE* f = fopen (path, "w"); - g_free (path); - - return f; -} - -static FILE* vstfx_infofile_for_write (char* dllpath) -{ - FILE* f; - - if ((f = vstfx_infofile_create (dllpath, 0)) == 0) { - f = vstfx_infofile_create (dllpath, 1); - } - - return f; -} - -static -int vstfx_can_midi (VSTState* vstfx) -{ - AEffect *plugin = vstfx->plugin; - - int vst_version = plugin->dispatcher (plugin, effGetVstVersion, 0, 0, 0, 0.0f); - - if (vst_version >= 2) - { - /* should we send it VST events (i.e. MIDI) */ - - if ((plugin->flags & effFlagsIsSynth) || (plugin->dispatcher (plugin, effCanDo, 0, 0,(void*) "receiveVstEvents", 0.0f) > 0)) - return -1; - } - return false; -} - -static VSTInfo * -vstfx_info_from_plugin (VSTState* vstfx) -{ - VSTInfo* info = (VSTInfo*) malloc (sizeof (VSTInfo)); - - AEffect *plugin; - int i; - - /*We need to init the creator because some plugins - fail to implement getVendorString, and so won't stuff the - string with any name*/ - - char creator[65] = "Unknown\0"; - - if(!vstfx) - { - vstfx_error( "** ERROR ** VSTFXinfofile : vstfx ptr is 0\n" ); - return 0; - } - - if(!info) - return 0; - - plugin = vstfx->plugin; - - info->name = strdup(vstfx->handle->name ); - - /*If the plugin doesn't bother to implement GetVendorString we will - have pre-stuffed the string with 'Unkown' */ - - plugin->dispatcher (plugin, effGetVendorString, 0, 0, creator, 0); - - /*Some plugins DO implement GetVendorString, but DON'T put a name in it - so if its just a zero length string we replace it with 'Unknown' */ - - if (strlen(creator) == 0) { - info->creator = strdup("Unknown"); - } else { - info->creator = strdup (creator); - } - - info->UniqueID = plugin->uniqueID; - - info->Category = strdup("None"); // FIXME: - info->numInputs = plugin->numInputs; - info->numOutputs = plugin->numOutputs; - info->numParams = plugin->numParams; - info->wantMidi = vstfx_can_midi(vstfx); - info->hasEditor = plugin->flags & effFlagsHasEditor ? true : false; - info->canProcessReplacing = plugin->flags & effFlagsCanReplacing ? true : false; - info->ParamNames = (char **) malloc(sizeof(char*)*info->numParams); - info->ParamLabels = (char **) malloc(sizeof(char*)*info->numParams); - - for(i=0; i < info->numParams; i++) { - char name[64]; - char label[64]; - - /*Not all plugins give parameters labels as well as names*/ - - strcpy(name, "No Name"); - strcpy(label, "No Label"); - - plugin->dispatcher (plugin, effGetParamName, i, 0, name, 0); - info->ParamNames[i] = strdup(name); - - //NOTE: 'effGetParamLabel' is no longer defined in vestige headers - //plugin->dispatcher (plugin, effGetParamLabel, i, 0, label, 0); - info->ParamLabels[i] = strdup(label); - } - return info; -} - -/* A simple 'dummy' audiomaster callback which should be ok, -we will only be instantiating the plugin in order to get its info*/ - -static intptr_t -simple_master_callback (AEffect *, int32_t opcode, int32_t, intptr_t, void *, float) -{ - if (opcode == audioMasterVersion) - return 2; - else - return 0; -} - -/*Try to get plugin info - first by looking for a .fsi cache of the -data, and if that doesn't exist, load the plugin, get its data and -then cache it for future ref*/ - -VSTInfo * -vstfx_get_info (char* dllpath) -{ - FILE* infofile; - VSTHandle* h; - VSTState* vstfx; - VSTInfo* info; - - if ((infofile = vstfx_infofile_for_read (dllpath)) != 0) { - VSTInfo *info; - info = load_vstfx_info_file (infofile); - fclose (infofile); - return info; - } - - if(!(h = vstfx_load(dllpath))) - return 0; - - if(!(vstfx = vstfx_instantiate(h, simple_master_callback, 0))) { - vstfx_unload(h); - vstfx_error( "** ERROR ** VSTFXinfofile : Instantiate failed\n" ); - return 0; - } - - infofile = vstfx_infofile_for_write (dllpath); - - if(!infofile) { - vstfx_close(vstfx); - vstfx_unload(h); - vstfx_error("cannot create new FST info file for plugin"); - return 0; - } - - info = vstfx_info_from_plugin(vstfx); - - save_vstfx_info_file(info, infofile); - fclose (infofile); - - vstfx_close(vstfx); - vstfx_unload(h); - - return info; -} - -void -vstfx_free_info (VSTInfo *info) -{ - for (int i = 0; i < info->numParams; i++) { - free (info->ParamNames[i]); - free (info->ParamLabels[i]); - } - - free (info->name); - free (info->creator); - free (info->Category); - free (info); -} - - diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 6d79413a1f..d6b72dbf2d 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -391,7 +391,7 @@ def build(bld): obj.defines += [ 'WINDOWS_VST_SUPPORT' ] if bld.is_defined('LXVST_SUPPORT'): - obj.source += [ 'lxvst_plugin.cc', 'vstfx.cc', 'vstfxinfofile.cc' ] + obj.source += [ 'lxvst_plugin.cc', 'linux_vst_support.cc', 'linux_vst_info_file.cc' ] obj.defines += [ 'LXVST_SUPPORT' ] if bld.is_defined('WINDOWS_VST_SUPPORT') or bld.is_defined('LXVST_SUPPORT'):