X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fvst_info_file.cc;h=68b376d8d3039614f5d14abc22a90b534d2d4588;hb=bc0bb9662e939c1b37f4335c05292a7b0ad21a20;hp=7d5aaf3795fd7752a7643d4ff4ffa317bbffd1ed;hpb=bcf6c764625ab4b98e486977ef477deb4f1d64c4;p=ardour.git diff --git a/libs/ardour/vst_info_file.cc b/libs/ardour/vst_info_file.cc index 7d5aaf3795..68b376d8d3 100644 --- a/libs/ardour/vst_info_file.cc +++ b/libs/ardour/vst_info_file.cc @@ -22,11 +22,10 @@ * e.g. its name, creator etc. */ -#include #include #include -#include +#include #include #include @@ -34,21 +33,218 @@ #include #include #include -#include #include -#include +#include "pbd/gstdio_compat.h" #include #include "pbd/error.h" +#include "pbd/compose.h" +#ifndef VST_SCANNER_APP +#include "ardour/plugin_manager.h" // scanner_bin_path +#include "ardour/rc_configuration.h" +#include "ardour/system_exec.h" +#endif + +#include "ardour/filesystem_paths.h" #include "ardour/linux_vst_support.h" +#include "ardour/plugin_types.h" #include "ardour/vst_info_file.h" +#include "i18n.h" +#include "sha1.c" + #define MAX_STRING_LEN 256 +#define PLUGIN_SCAN_TIMEOUT (Config->get_vst_scan_timeout()) // in deciseconds using namespace std; +#ifndef VST_SCANNER_APP +namespace ARDOUR { +#endif + +/* prototypes */ +#ifdef WINDOWS_VST_SUPPORT +#include +static bool +vstfx_instantiate_and_get_info_fst (const char* dllpath, vector *infos, int uniqueID); +#endif + +#ifdef LXVST_SUPPORT +static bool vstfx_instantiate_and_get_info_lx (const char* dllpath, vector *infos, int uniqueID); +#endif + +/* ID for shell plugins */ +static int vstfx_current_loading_id = 0; + +/* *** CACHE FILE PATHS *** */ + +static string +get_vst_info_cache_dir () { + string dir = Glib::build_filename (ARDOUR::user_cache_directory (), "vst"); + /* if the directory doesn't exist, try to create it */ + if (!Glib::file_test (dir, Glib::FILE_TEST_IS_DIR)) { + if (g_mkdir (dir.c_str (), 0700)) { + PBD::fatal << "Cannot create VST info folder '" << dir << "'" << endmsg; + } + } + return dir; +} + +static string +vstfx_infofile_path (const char* dllpath) +{ + char hash[41]; + Sha1Digest s; + sha1_init (&s); + sha1_write (&s, (const uint8_t *) dllpath, strlen (dllpath)); + sha1_result_hash (&s, hash); + return Glib::build_filename (get_vst_info_cache_dir (), std::string (hash) + std::string (VST_EXT_INFOFILE)); +} + + +/* *** VST Blacklist *** */ + +static void vstfx_read_blacklist (std::string &bl) { + FILE * blacklist_fd = NULL; + bl = ""; + + string fn = Glib::build_filename (ARDOUR::user_cache_directory (), VST_BLACKLIST); + + if (!Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) { + return; + } + + if (! (blacklist_fd = g_fopen (fn.c_str (), "rb"))) { + return; + } + + while (!feof (blacklist_fd)) { + char buf[1024]; + size_t s = fread (buf, sizeof(char), 1024, blacklist_fd); + if (ferror (blacklist_fd)) { + PBD::error << string_compose (_("error reading VST Blacklist file %1 (%2)"), fn, strerror (errno)) << endmsg; + bl = ""; + break; + } + if (s == 0) { + break; + } + bl.append (buf, s); + } + ::fclose (blacklist_fd); +} + +/** mark plugin as blacklisted */ +static void vstfx_blacklist (const char *id) +{ + string fn = Glib::build_filename (ARDOUR::user_cache_directory (), VST_BLACKLIST); + FILE * blacklist_fd = NULL; + if (! (blacklist_fd = g_fopen (fn.c_str (), "a"))) { + PBD::error << string_compose (_("Cannot append to VST blacklist for '%1'"), id) << endmsg; + return; + } + assert (NULL == strchr (id, '\n')); + fprintf (blacklist_fd, "%s\n", id); + ::fclose (blacklist_fd); +} + +/** mark plugin as not blacklisted */ +static void vstfx_un_blacklist (const char *idcs) +{ + string id (idcs); + string fn = Glib::build_filename (ARDOUR::user_cache_directory (), VST_BLACKLIST); + if (!Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) { + PBD::warning << _("Expected VST Blacklist file does not exist.") << endmsg; + return; + } + + std::string bl; + vstfx_read_blacklist (bl); + + ::g_unlink (fn.c_str ()); + + assert (!Glib::file_test (fn, Glib::FILE_TEST_EXISTS)); + assert (id.find ("\n") == string::npos); + + id += "\n"; // add separator + const size_t rpl = bl.find (id); + if (rpl != string::npos) { + bl.replace (rpl, id.size (), ""); + } + if (bl.empty ()) { + return; + } + + FILE * blacklist_fd = NULL; + if (! (blacklist_fd = g_fopen (fn.c_str (), "w"))) { + PBD::error << _("Cannot open VST blacklist.") << endmsg;; + return; + } + fprintf (blacklist_fd, "%s", bl.c_str ()); + ::fclose (blacklist_fd); +} + +/* return true if plugin is blacklisted */ +static bool vst_is_blacklisted (const char *idcs) +{ + // TODO ideally we'd also check if the VST has been updated since blacklisting + string id (idcs); + string fn = Glib::build_filename (ARDOUR::user_cache_directory (), VST_BLACKLIST); + if (!Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) { + return false; + } + + std::string bl; + vstfx_read_blacklist (bl); + + assert (id.find ("\n") == string::npos); + + id += "\n"; // add separator + const size_t rpl = bl.find (id); + if (rpl != string::npos) { + return true; + } + return false; +} + + + +/* *** MEMORY MANAGEMENT *** */ + +/** cleanup single allocated VSTInfo */ +static 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->ParamNames); + free (info->ParamLabels); + free (info); +} +/** reset vector */ +static void +vstfx_clear_info_list (vector *infos) +{ + for (vector::iterator i = infos->begin (); i != infos->end (); ++i) { + vstfx_free_info (*i); + } + infos->clear (); +} + + +/* *** CACHE FILE I/O *** */ + +/** Helper function to read a line from the cache file + * @return newly allocated string of NULL + */ static char * read_string (FILE *fp) { @@ -58,9 +254,9 @@ read_string (FILE *fp) return 0; } - if (strlen(buf) < MAX_STRING_LEN) { + if (strlen (buf) < MAX_STRING_LEN) { if (strlen (buf)) { - buf[strlen(buf)-1] = 0; + buf[strlen (buf)-1] = 0; } return strdup (buf); } else { @@ -84,51 +280,93 @@ read_int (FILE* fp, int* n) return (sscanf (p, "%d", n) != 1); } -static VSTInfo * -load_vstfx_info_file (FILE* fp) +/** parse a plugin-block from the cache info file */ +static bool +vstfx_load_info_block (FILE* fp, VSTInfo *info) { - VSTInfo *info; - - if ((info = (VSTInfo*) malloc (sizeof (VSTInfo))) == 0) { - return 0; + if ((info->name = read_string (fp)) == 0) return false; + if ((info->creator = read_string (fp)) == 0) return false; + if (read_int (fp, &info->UniqueID)) return false; + if ((info->Category = read_string (fp)) == 0) return false; + if (read_int (fp, &info->numInputs)) return false; + if (read_int (fp, &info->numOutputs)) return false; + if (read_int (fp, &info->numParams)) return false; + if (read_int (fp, &info->wantMidi)) return false; + if (read_int (fp, &info->hasEditor)) return false; + if (read_int (fp, &info->canProcessReplacing)) return false; + + /* backwards compatibility with old .fsi files */ + if (info->wantMidi == -1) { + info->wantMidi = 1; } - if ((info->name = read_string(fp)) == 0) goto error; - if ((info->creator = read_string(fp)) == 0) goto error; - if (read_int (fp, &info->UniqueID)) goto error; - if ((info->Category = read_string(fp)) == 0) goto error; - if (read_int (fp, &info->numInputs)) goto error; - if (read_int (fp, &info->numOutputs)) goto error; - if (read_int (fp, &info->numParams)) goto error; - if (read_int (fp, &info->wantMidi)) goto error; - if (read_int (fp, &info->hasEditor)) goto error; - if (read_int (fp, &info->canProcessReplacing)) goto error; + if ((info->numParams) == 0) { + info->ParamNames = NULL; + info->ParamLabels = NULL; + return true; + } - if ((info->ParamNames = (char **) malloc(sizeof(char*)*info->numParams)) == 0) { - goto error; + if ((info->ParamNames = (char **) malloc (sizeof (char*) * info->numParams)) == 0) { + return false; } for (int i = 0; i < info->numParams; ++i) { - if ((info->ParamNames[i] = read_string(fp)) == 0) goto error; + if ((info->ParamNames[i] = read_string (fp)) == 0) return false; } - if ((info->ParamLabels = (char **) malloc(sizeof(char*)*info->numParams)) == 0) { - goto error; + if ((info->ParamLabels = (char **) malloc (sizeof (char*) * info->numParams)) == 0) { + return false; } for (int i = 0; i < info->numParams; ++i) { - if ((info->ParamLabels[i] = read_string(fp)) == 0) goto error; + if ((info->ParamLabels[i] = read_string (fp)) == 0) { + return false; + } } + return true; +} - return info; - -error: - free (info); - return 0; +/** parse all blocks in a cache info file */ +static bool +vstfx_load_info_file (FILE* fp, vector *infos) +{ + VSTInfo *info; + if ((info = (VSTInfo*) calloc (1, sizeof (VSTInfo))) == 0) { + return false; + } + if (vstfx_load_info_block (fp, info)) { + if (strncmp (info->Category, "Shell", 5)) { + infos->push_back (info); + } else { + int plugin_cnt = 0; + vstfx_free_info (info); + if (!read_int (fp, &plugin_cnt)) { + for (int i = 0; i < plugin_cnt; i++) { + if ((info = (VSTInfo*) calloc (1, sizeof (VSTInfo))) == 0) { + vstfx_clear_info_list (infos); + return false; + } + if (vstfx_load_info_block (fp, info)) { + infos->push_back (info); + } else { + vstfx_free_info (info); + vstfx_clear_info_list (infos); + return false; + } + } + } else { + return false; /* Bad file */ + } + } + return true; + } + vstfx_free_info (info); + vstfx_clear_info_list (infos); + return false; } -static int -save_vstfx_info_file (VSTInfo *info, FILE* fp) +static void +vstfx_write_info_block (FILE* fp, VSTInfo *info) { assert (info); assert (fp); @@ -151,118 +389,139 @@ save_vstfx_info_file (VSTInfo *info, FILE* fp) for (int i = 0; i < info->numParams; i++) { fprintf (fp, "%s\n", info->ParamLabels[i]); } - - return 0; } -static string -vstfx_infofile_path (char* dllpath, int personal) +static void +vstfx_write_info_file (FILE* fp, vector *infos) { - string dir; - if (personal) { - dir = Glib::build_filename (Glib::get_home_dir (), ".fst"); - - /* If the directory doesn't exist, try to create it */ - if (!Glib::file_test (dir, Glib::FILE_TEST_IS_DIR)) { - if (g_mkdir (dir.c_str (), 0700)) { - return 0; - } - } + assert (infos); + assert (fp); + if (infos->size () > 1) { + vector::iterator x = infos->begin (); + /* write out the shell info first along with count of the number of + * plugins contained in this shell + */ + vstfx_write_info_block (fp, *x); + fprintf (fp, "%d\n", (int)infos->size () - 1 ); + ++x; + /* Now write out the info for each plugin */ + for (; x != infos->end (); ++x) { + vstfx_write_info_block (fp, *x); + } + } else if (infos->size () == 1) { + vstfx_write_info_block (fp, infos->front ()); } else { - dir = Glib::path_get_dirname (std::string(dllpath)); + PBD::warning << _("VST object file contains no plugins.") << endmsg; } +} + + +/* *** CACHE MANAGEMENT *** */ - stringstream s; - s << "." << Glib::path_get_basename (dllpath) << ".fsi"; - return Glib::build_filename (dir, s.str ()); +/** remove info file from cache */ +static void +vstfx_remove_infofile (const char *dllpath) +{ + ::g_unlink (vstfx_infofile_path (dllpath).c_str ()); } -static char * -vstfx_infofile_stat (char *dllpath, struct stat* statbuf, int personal) +/** cache file for given plugin + * @return FILE of the .fsi cache if found and up-to-date*/ +static FILE * +vstfx_infofile_for_read (const char* dllpath) { - if (strstr (dllpath, ".so" ) == 0 && strstr(dllpath, ".dll") == 0) { + const size_t slen = strlen (dllpath); + if ( + (slen <= 3 || g_ascii_strcasecmp (&dllpath[slen-3], ".so")) + && + (slen <= 4 || g_ascii_strcasecmp (&dllpath[slen-4], ".dll")) + ) { return 0; } - string const path = vstfx_infofile_path (dllpath, personal); + string const path = vstfx_infofile_path (dllpath); if (Glib::file_test (path, Glib::FileTest (Glib::FILE_TEST_EXISTS | Glib::FILE_TEST_IS_REGULAR))) { + GStatBuf dllstat; + GStatBuf fsistat; - /* 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.c_str(), statbuf) == 0) { - if (dllstat.st_mtime <= statbuf->st_mtime) { + if (g_stat (dllpath, &dllstat) == 0) { + if (g_stat (path.c_str (), &fsistat) == 0) { + if (dllstat.st_mtime <= fsistat.st_mtime) { /* plugin is older than info file */ - return strdup (path.c_str ()); + return g_fopen (path.c_str (), "rb"); } } } + PBD::warning << string_compose (_("Ignored VST plugin which is newer than cache: '%1' (cache: '%2')"), dllpath, path) << endmsg; + PBD::info << _("Re-Scan Plugins (Preferences > Plugins) to update the cache, also make sure your system-time is set correctly.") << endmsg; } - - return 0; + return NULL; } - +/** newly created cache file for given plugin + * @return FILE for the .fsi cache or NULL on error + */ static FILE * -vstfx_infofile_for_read (char* dllpath) +vstfx_infofile_for_write (const char* dllpath) { - struct stat own_statbuf; - struct stat sys_statbuf; - FILE *rv = NULL; - - char* own_info = vstfx_infofile_stat (dllpath, &own_statbuf, 1); - char* 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 */ - rv = g_fopen (sys_info, "rb"); - } - } else { - rv = g_fopen (own_info, "rb"); - } - } else if (sys_info) { - rv = g_fopen (sys_info, "rb"); + const size_t slen = strlen (dllpath); + if ( + (slen <= 3 || g_ascii_strcasecmp (&dllpath[slen-3], ".so")) + && + (slen <= 4 || g_ascii_strcasecmp (&dllpath[slen-4], ".dll")) + ) { + return NULL; } - free(own_info); - free(sys_info); - return rv; + string const path = vstfx_infofile_path (dllpath); + return g_fopen (path.c_str (), "wb"); } -static FILE * -vstfx_infofile_create (char* dllpath, int personal) +/** check if cache-file exists, is up-to-date and parse cache file + * @param infos [return] loaded plugin info + * @return true if .fsi cache was read successfully, false otherwise + */ +static bool +vstfx_get_info_from_file (const char* dllpath, vector *infos) { - if (strstr (dllpath, ".so" ) == 0 && strstr(dllpath, ".dll") == 0) { - return 0; + FILE* infofile; + bool rv = false; + if ((infofile = vstfx_infofile_for_read (dllpath)) != 0) { + rv = vstfx_load_info_file (infofile, infos); + fclose (infofile); + if (!rv) { + PBD::warning << string_compose (_("Cannot get VST information for '%1': failed to load cache file."), dllpath) << endmsg; + } } - - string const path = vstfx_infofile_path (dllpath, personal); - return fopen (path.c_str(), "w"); + return rv; } -static FILE * -vstfx_infofile_for_write (char* dllpath) + + +/* *** VST system-under-test methods *** */ + +static +bool vstfx_midi_input (VSTState* vstfx) { - FILE* f; + AEffect* plugin = vstfx->plugin; + + int const 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 ((f = vstfx_infofile_create (dllpath, 0)) == 0) { - f = vstfx_infofile_create (dllpath, 1); + if ((plugin->flags & effFlagsIsSynth) || (plugin->dispatcher (plugin, effCanDo, 0, 0, const_cast ("receiveVstEvents"), 0.0f) > 0)) { + return true; + } } - return f; + return false; } static -int vstfx_can_midi (VSTState* vstfx) +bool vstfx_midi_output (VSTState* vstfx) { AEffect* plugin = vstfx->plugin; @@ -271,16 +530,58 @@ int vstfx_can_midi (VSTState* vstfx) 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; + if ( (plugin->dispatcher (plugin, effCanDo, 0, 0, const_cast ("sendVstEvents"), 0.0f) > 0) + || (plugin->dispatcher (plugin, effCanDo, 0, 0, const_cast ("sendVstMidiEvent"), 0.0f) > 0) + ) { + return true; } } return false; } -static VSTInfo * -vstfx_info_from_plugin (VSTState* vstfx) +/** simple 'dummy' audiomaster callback to instantiate the plugin + * and query information + */ +static intptr_t +simple_master_callback (AEffect *, int32_t opcode, int32_t, intptr_t, void *ptr, float) +{ + const char* vstfx_can_do_strings[] = { + "supplyIdle", + "sendVstTimeInfo", + "sendVstEvents", + "sendVstMidiEvent", + "receiveVstEvents", + "receiveVstMidiEvent", + "supportShell", + "shellCategory", + "shellCategorycurID" + }; + const int vstfx_can_do_string_count = 9; + + if (opcode == audioMasterVersion) { + return 2400; + } + else if (opcode == audioMasterCanDo) { + for (int i = 0; i < vstfx_can_do_string_count; i++) { + if (! strcmp (vstfx_can_do_strings[i], (const char*)ptr)) { + return 1; + } + } + return 0; + } + else if (opcode == audioMasterCurrentId) { + return vstfx_current_loading_id; + } + else { + return 0; + } +} + + +/** main plugin query and test function */ +static VSTInfo* +vstfx_parse_vst_state (VSTState* vstfx) { assert (vstfx); @@ -289,41 +590,69 @@ vstfx_info_from_plugin (VSTState* vstfx) return 0; } - /*We need to init the creator because some plugins - fail to implement getVendorString, and so won't stuff the - string with any name*/ + /* 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"; + char creator[65] = "Unknown"; + char name[65] = ""; AEffect* plugin = vstfx->plugin; - info->name = strdup (vstfx->handle->name); + + plugin->dispatcher (plugin, effGetEffectName, 0, 0, name, 0); + + if (strlen (name) == 0) { + plugin->dispatcher (plugin, effGetProductString, 0, 0, name, 0); + } + + if (strlen (name) == 0) { + info->name = strdup (vstfx->handle->name); + } else { + info->name = strdup (name); + } /*If the plugin doesn't bother to implement GetVendorString we will - have pre-stuffed the string with 'Unkown' */ + * have pre-stuffed the string with 'Unknown' */ 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' */ + /* 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) { + if (strlen (creator) == 0) { info->creator = strdup ("Unknown"); } else { info->creator = strdup (creator); } + + switch (plugin->dispatcher (plugin, effGetPlugCategory, 0, 0, 0, 0)) + { + case kPlugCategEffect: info->Category = strdup ("Effect"); break; + case kPlugCategSynth: info->Category = strdup ("Synth"); break; + case kPlugCategAnalysis: info->Category = strdup ("Anaylsis"); break; + case kPlugCategMastering: info->Category = strdup ("Mastering"); break; + case kPlugCategSpacializer: info->Category = strdup ("Spacializer"); break; + case kPlugCategRoomFx: info->Category = strdup ("RoomFx"); break; + case kPlugSurroundFx: info->Category = strdup ("SurroundFx"); break; + case kPlugCategRestoration: info->Category = strdup ("Restoration"); break; + case kPlugCategOfflineProcess: info->Category = strdup ("Offline"); break; + case kPlugCategShell: info->Category = strdup ("Shell"); break; + case kPlugCategGenerator: info->Category = strdup ("Generator"); break; + default: info->Category = strdup ("Unknown"); break; + } + info->UniqueID = plugin->uniqueID; - info->Category = strdup("None"); /* XXX */ info->numInputs = plugin->numInputs; info->numOutputs = plugin->numOutputs; info->numParams = plugin->numParams; - info->wantMidi = vstfx_can_midi(vstfx); + info->wantMidi = (vstfx_midi_input (vstfx) ? 1 : 0) | (vstfx_midi_output (vstfx) ? 2 : 0); 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); + info->ParamNames = (char **) malloc (sizeof (char*)*info->numParams); + info->ParamLabels = (char **) malloc (sizeof (char*)*info->numParams); for (int i = 0; i < info->numParams; ++i) { char name[64]; @@ -335,158 +664,377 @@ vstfx_info_from_plugin (VSTState* vstfx) strcpy (label, "No Label"); plugin->dispatcher (plugin, effGetParamName, i, 0, name, 0); - info->ParamNames[i] = strdup(name); + 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); + 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) +/** wrapper around \ref vstfx_parse_vst_state, + * iterate over plugins in shell, translate VST-info into ardour VSTState + */ +static void +vstfx_info_from_plugin (const char *dllpath, VSTState* vstfx, vector *infos, enum ARDOUR::PluginType type) { - if (opcode == audioMasterVersion) { - return 2; - } else { - return 0; + assert (vstfx); + VSTInfo *info; + + if (!(info = vstfx_parse_vst_state (vstfx))) { + return; } -} -static VSTInfo * -vstfx_get_info_from_file(char* dllpath, bool &found) -{ - FILE* infofile; - VSTInfo *info = 0; - found = false; - if ((infofile = vstfx_infofile_for_read (dllpath)) != 0) { - found = true; - info = load_vstfx_info_file (infofile); - fclose (infofile); - if (info == 0) { - PBD::warning << "Cannot get LinuxVST information form " << dllpath << ": info file load failed." << endmsg; + infos->push_back (info); +#if 1 // shell-plugin support + /* If this plugin is a Shell and we are not already inside a shell plugin + * read the info for all of the plugins contained in this shell. + */ + if (!strncmp (info->Category, "Shell", 5) + && vstfx->handle->plugincnt == 1) { + int id; + vector< pair > ids; + AEffect *plugin = vstfx->plugin; + + do { + char name[65] = "Unknown"; + id = plugin->dispatcher (plugin, effShellGetNextPlugin, 0, 0, name, 0); + ids.push_back (std::make_pair (id, name)); + } while ( id != 0 ); + + switch (type) { +#ifdef WINDOWS_VST_SUPPORT + case ARDOUR::Windows_VST: + fst_close (vstfx); + break; +#endif +#ifdef LXVST_SUPPORT + case ARDOUR::LXVST: + vstfx_close (vstfx); + break; +#endif + default: + assert (0); + break; + } + + for (vector< pair >::iterator x = ids.begin (); x != ids.end (); ++x) { + id = (*x).first; + if (id == 0) continue; + /* recurse vstfx_get_info() */ + + bool ok; + switch (type) { +#ifdef WINDOWS_VST_SUPPORT + case ARDOUR::Windows_VST: + ok = vstfx_instantiate_and_get_info_fst (dllpath, infos, id); + break; +#endif +#ifdef LXVST_SUPPORT + case ARDOUR::LXVST: + ok = vstfx_instantiate_and_get_info_lx (dllpath, infos, id); + break; +#endif + default: + ok = false; + break; + } + if (ok) { + // One shell (some?, all?) does not report the actual plugin name + // even after the shelled plugin has been instantiated. + // Replace the name of the shell with the real name. + info = infos->back (); + free (info->name); + + if ((*x).second.length () == 0) { + info->name = strdup ("Unknown"); + } + else { + info->name = strdup ((*x).second.c_str ()); + } + } + } + } else { + switch (type) { +#ifdef WINDOWS_VST_SUPPORT + case ARDOUR::Windows_VST: + fst_close (vstfx); + break; +#endif +#ifdef LXVST_SUPPORT + case ARDOUR::LXVST: + vstfx_close (vstfx); + break; +#endif + default: + assert (0); + break; } } - return info; +#endif } -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->ParamNames); - free (info->ParamLabels); - free (info); -} -#ifdef LXVST_SUPPORT -/** 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 -*/ +/* *** TOP-LEVEL PLUGIN INSTANTIATION FUNCTIONS *** */ -VSTInfo * -vstfx_get_info_lx (char* dllpath) +#ifdef LXVST_SUPPORT +static bool +vstfx_instantiate_and_get_info_lx ( + const char* dllpath, vector *infos, int uniqueID) { - FILE* infofile; VSTHandle* h; VSTState* vstfx; + if (!(h = vstfx_load (dllpath))) { + PBD::warning << string_compose (_("Cannot get LinuxVST information from '%1': load failed."), dllpath) << endmsg; + return false; + } + + vstfx_current_loading_id = uniqueID; - bool used_cache; - VSTInfo* info = vstfx_get_info_from_file(dllpath, used_cache); - if (used_cache) { - PBD::info << "using cache for LinuxVST plugin '" << dllpath << "'" << endmsg; - return info; + if (!(vstfx = vstfx_instantiate (h, simple_master_callback, 0))) { + vstfx_unload (h); + PBD::warning << string_compose (_("Cannot get LinuxVST information from '%1': instantiation failed."), dllpath) << endmsg; + return false; } - if (!(h = vstfx_load(dllpath))) { - PBD::warning << "Cannot get LinuxVST information from " << dllpath << ": load failed." << endmsg; - return 0; + vstfx_current_loading_id = 0; + + vstfx_info_from_plugin (dllpath, vstfx, infos, ARDOUR::LXVST); + + vstfx_unload (h); + return true; +} +#endif + +#ifdef WINDOWS_VST_SUPPORT +static bool +vstfx_instantiate_and_get_info_fst ( + const char* dllpath, vector *infos, int uniqueID) +{ + VSTHandle* h; + VSTState* vstfx; + if (!(h = fst_load (dllpath))) { + PBD::warning << string_compose (_("Cannot get Windows VST information from '%1': load failed."), dllpath) << endmsg; + return false; } - if (!(vstfx = vstfx_instantiate(h, simple_master_callback, 0))) { - vstfx_unload(h); - PBD::warning << "Cannot get LinuxVST information from " << dllpath << ": instantiation failed." << endmsg; - return 0; + vstfx_current_loading_id = uniqueID; + + if (!(vstfx = fst_instantiate (h, simple_master_callback, 0))) { + fst_unload (&h); + vstfx_current_loading_id = 0; + PBD::warning << string_compose (_("Cannot get Windows VST information from '%1': instantiation failed."), dllpath) << endmsg; + return false; } + vstfx_current_loading_id = 0; - infofile = vstfx_infofile_for_write (dllpath); + vstfx_info_from_plugin (dllpath, vstfx, infos, ARDOUR::Windows_VST); - if (!infofile) { - vstfx_close(vstfx); - vstfx_unload(h); - PBD::warning << "Cannot get LinuxVST information from " << dllpath << ": cannot create new FST info file." << endmsg; - return 0; + return true; +} +#endif + + + +/* *** ERROR LOGGING *** */ +#ifndef VST_SCANNER_APP + +static FILE * _errorlog_fd = 0; +static char * _errorlog_dll = 0; + +static void parse_scanner_output (std::string msg, size_t /*len*/) +{ + if (!_errorlog_fd && !_errorlog_dll) { + PBD::error << "VST scanner: " << msg; + return; } - info = vstfx_info_from_plugin (vstfx); +#if 0 // TODO + if (!_errorlog_fd) { + if (!(_errorlog_fd = g_fopen (vstfx_errorfile_path (_errorlog_dll).c_str (), "w"))) { + PBD::error << "Cannot create plugin error-log for plugin " << _errorlog_dll; + free (_errorlog_dll); + _errorlog_dll = NULL; + } + } +#endif - save_vstfx_info_file (info, infofile); - fclose (infofile); + if (_errorlog_fd) { + fprintf (_errorlog_fd, "%s\n", msg.c_str ()); + } else if (_errorlog_dll) { + PBD::error << "VST '" << _errorlog_dll << "': " << msg; + } else { + PBD::error << "VST scanner: " << msg; + } +} - vstfx_close (vstfx); - vstfx_unload (h); +static void +set_error_log (const char* dllpath) { + assert (!_errorlog_fd); + assert (!_errorlog_dll); + _errorlog_dll = strdup (dllpath); +} - return info; +static void +close_error_log () { + if (_errorlog_fd) { + fclose (_errorlog_fd); + _errorlog_fd = 0; + } + free (_errorlog_dll); + _errorlog_dll = 0; } + #endif -#ifdef WINDOWS_VST_SUPPORT -#include -VSTInfo * -vstfx_get_info_fst (char* dllpath) +/* *** the main function that uses all of the above *** */ + +static vector * +vstfx_get_info (const char* dllpath, enum ARDOUR::PluginType type, enum VSTScanMode mode) { FILE* infofile; - VSTHandle* h; - VSTState* vstfx; + vector *infos = new vector; - bool used_cache; - VSTInfo* info = vstfx_get_info_from_file(dllpath, used_cache); - if (used_cache) { - PBD::info << "using cache for VST plugin '" << dllpath << "'" << endmsg; - return info; + if (vst_is_blacklisted (dllpath)) { + return infos; } - if(!(h = fst_load(dllpath))) { - PBD::warning << "Cannot get VST information from " << dllpath << ": load failed." << endmsg; - return 0; + if (vstfx_get_info_from_file (dllpath, infos)) { + return infos; } - if(!(vstfx = fst_instantiate(h, simple_master_callback, 0))) { - fst_unload(&h); - PBD::warning << "Cannot get VST information from " << dllpath << ": instantiation failed." << endmsg; - return 0; +#ifndef VST_SCANNER_APP + std::string scanner_bin_path = ARDOUR::PluginManager::scanner_bin_path; + + if (mode == VST_SCAN_CACHE_ONLY) { + /* never scan explicitly, use cache only */ + return infos; + } + else if (mode == VST_SCAN_USE_APP && scanner_bin_path != "") { + /* use external scanner app */ + + char **argp= (char**) calloc (3,sizeof (char*)); + argp[0] = strdup (scanner_bin_path.c_str ()); + argp[1] = strdup (dllpath); + argp[2] = 0; + + set_error_log (dllpath); + ARDOUR::SystemExec scanner (scanner_bin_path, argp); + PBD::ScopedConnectionList cons; + scanner.ReadStdout.connect_same_thread (cons, boost::bind (&parse_scanner_output, _1 ,_2)); + if (scanner.start (2 /* send stderr&stdout via signal */)) { + PBD::error << string_compose (_("Cannot launch VST scanner app '%1': %2"), scanner_bin_path, strerror (errno)) << endmsg; + close_error_log (); + return infos; + } else { + int timeout = PLUGIN_SCAN_TIMEOUT; + bool no_timeout = (timeout <= 0); + ARDOUR::PluginScanTimeout (timeout); + while (scanner.is_running () && (no_timeout || timeout > 0)) { + if (!no_timeout && !ARDOUR::PluginManager::instance ().no_timeout ()) { + if (timeout%5 == 0) { + ARDOUR::PluginScanTimeout (timeout); + } + --timeout; + } + ARDOUR::GUIIdle (); + Glib::usleep (100000); + + if (ARDOUR::PluginManager::instance ().cancelled ()) { + // remove info file (might be incomplete) + vstfx_remove_infofile (dllpath); + // remove temporary blacklist file (scan incomplete) + vstfx_un_blacklist (dllpath); + scanner.terminate (); + close_error_log (); + return infos; + } + } + scanner.terminate (); + } + close_error_log (); + /* re-read index (generated by external scanner) */ + vstfx_clear_info_list (infos); + if (!vst_is_blacklisted (dllpath)) { + vstfx_get_info_from_file (dllpath, infos); + } + return infos; + } + /* else .. instantiate and check in in ardour process itself */ +#else + (void) mode; // unused parameter +#endif + + bool ok; + /* blacklist in case instantiation fails */ + vstfx_blacklist (dllpath); + + switch (type) { +#ifdef WINDOWS_VST_SUPPORT + case ARDOUR::Windows_VST: + ok = vstfx_instantiate_and_get_info_fst (dllpath, infos, 0); + break; +#endif +#ifdef LXVST_SUPPORT + case ARDOUR::LXVST: + ok = vstfx_instantiate_and_get_info_lx (dllpath, infos, 0); + break; +#endif + default: + ok = false; + break; } - infofile = vstfx_infofile_for_write (dllpath); + if (!ok) { + return infos; + } + + /* remove from blacklist */ + vstfx_un_blacklist (dllpath); + /* crate cache/whitelist */ + infofile = vstfx_infofile_for_write (dllpath); if (!infofile) { - fst_close(vstfx); - //fst_unload(&h); // XXX -> fst_close() - PBD::warning << "Cannot get VST information from " << dllpath << ": cannot create new FST info file." << endmsg; - return 0; + PBD::warning << string_compose (_("Cannot cache VST information for '%1': cannot create cache file."), dllpath) << endmsg; + return infos; + } else { + vstfx_write_info_file (infofile, infos); + fclose (infofile); } + return infos; +} - info = vstfx_info_from_plugin(vstfx); - save_vstfx_info_file (info, infofile); - fclose (infofile); - fst_close(vstfx); - //fst_unload(&h); // XXX -> fst_close() +/* *** public API *** */ - return info; +void +vstfx_free_info_list (vector *infos) +{ + for (vector::iterator i = infos->begin (); i != infos->end (); ++i) { + vstfx_free_info (*i); + } + delete infos; +} + +#ifdef LXVST_SUPPORT +vector * +vstfx_get_info_lx (char* dllpath, enum VSTScanMode mode) +{ + return vstfx_get_info (dllpath, ARDOUR::LXVST, mode); +} +#endif + +#ifdef WINDOWS_VST_SUPPORT +vector * +vstfx_get_info_fst (char* dllpath, enum VSTScanMode mode) +{ + return vstfx_get_info (dllpath, ARDOUR::Windows_VST, mode); } #endif + +#ifndef VST_SCANNER_APP +} // namespace +#endif