X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fvst_info_file.cc;h=ca2ef910adef8f803a7668ccd67b2cab117e5d5e;hb=cf52d6e4b40111eb04b244ec054055a4ec15dbe0;hp=8bb3f4aa7adf8451e61c5f458c399176758f4f74;hpb=30de04a58ccab82d00ff87ebdd2cece6c0aa388b;p=ardour.git diff --git a/libs/ardour/vst_info_file.cc b/libs/ardour/vst_info_file.cc index 8bb3f4aa7a..ca2ef910ad 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,29 +33,35 @@ #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 "pbd/system_exec.h" #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 "pbd/i18n.h" +#include "sha1.c" + #define MAX_STRING_LEN 256 +#define PLUGIN_SCAN_TIMEOUT (Config->get_vst_scan_timeout()) // in deciseconds using namespace std; - -// TODO: namespace public API into ARDOUR, ::Session or ::PluginManager -// consolidate vstfx_get_info_lx() and vstfx_get_info_fst() +#ifndef VST_SCANNER_APP +namespace ARDOUR { +#endif /* prototypes */ #ifdef WINDOWS_VST_SUPPORT @@ -69,43 +74,145 @@ vstfx_instantiate_and_get_info_fst (const char* dllpath, vector *infos 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; -static char * -read_string (FILE *fp) +/* *** 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 buf[MAX_STRING_LEN]; + 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)); +} - if (!fgets (buf, MAX_STRING_LEN, fp)) { - return 0; + +/* *** 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 (strlen(buf) < MAX_STRING_LEN) { - if (strlen (buf)) { - buf[strlen(buf)-1] = 0; + 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; } - return strdup (buf); - } else { - return 0; + if (s == 0) { + break; + } + bl.append (buf, s); } + ::fclose (blacklist_fd); } -/** Read an integer value from a line in fp into n, - * @return true on failure, false on success. - */ -static bool -read_int (FILE* fp, int* n) +/** mark plugin as blacklisted */ +static void vstfx_blacklist (const char *id) { - char buf[MAX_STRING_LEN]; + 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); +} - char* p = fgets (buf, MAX_STRING_LEN, fp); - if (p == 0) { - return true; +/** 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; } - return (sscanf (p, "%d", n) != 1); + 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) { @@ -122,22 +229,65 @@ vstfx_free_info (VSTInfo *info) 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); + for (vector::iterator i = infos->begin (); i != infos->end (); ++i) { + vstfx_free_info (*i); } - infos->clear(); + 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) +{ + 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; + } +} + +/** Read an integer value from a line in fp into n, + * @return true on failure, false on success. + */ +static bool +read_int (FILE* fp, int* n) +{ + char buf[MAX_STRING_LEN]; + + char* p = fgets (buf, MAX_STRING_LEN, fp); + if (p == 0) { + return true; + } + + return (sscanf (p, "%d", n) != 1); +} + +/** parse a plugin-block from the cache info file */ static bool -vstfx_load_info_block(FILE* fp, VSTInfo *info) +vstfx_load_info_block (FILE* fp, VSTInfo *info) { - if ((info->name = read_string(fp)) == 0) return false; - if ((info->creator = read_string(fp)) == 0) return false; + 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 ((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; @@ -145,26 +295,38 @@ vstfx_load_info_block(FILE* fp, VSTInfo *info) if (read_int (fp, &info->hasEditor)) return false; if (read_int (fp, &info->canProcessReplacing)) return false; - if ((info->ParamNames = (char **) malloc(sizeof(char*)*info->numParams)) == 0) { + /* backwards compatibility with old .fsi files */ + if (info->wantMidi == -1) { + info->wantMidi = 1; + } + + if ((info->numParams) == 0) { + info->ParamNames = NULL; + info->ParamLabels = NULL; + return true; + } + + 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) return false; + if ((info->ParamNames[i] = read_string (fp)) == 0) return false; } - if ((info->ParamLabels = (char **) malloc(sizeof(char*)*info->numParams)) == 0) { + 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) { + if ((info->ParamLabels[i] = read_string (fp)) == 0) { return false; } } return true; } +/** parse all blocks in a cache info file */ static bool vstfx_load_info_file (FILE* fp, vector *infos) { @@ -172,23 +334,23 @@ vstfx_load_info_file (FILE* fp, vector *infos) if ((info = (VSTInfo*) calloc (1, sizeof (VSTInfo))) == 0) { return false; } - if (vstfx_load_info_block(fp, info)) { + if (vstfx_load_info_block (fp, info)) { if (strncmp (info->Category, "Shell", 5)) { - infos->push_back(info); + infos->push_back (info); } else { int plugin_cnt = 0; - vstfx_free_info(info); - if (read_int (fp, &plugin_cnt)) { + 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); + vstfx_clear_info_list (infos); return false; } - if (vstfx_load_info_block(fp, info)) { - infos->push_back(info); + if (vstfx_load_info_block (fp, info)) { + infos->push_back (info); } else { - vstfx_free_info(info); - vstfx_clear_info_list(infos); + vstfx_free_info (info); + vstfx_clear_info_list (infos); return false; } } @@ -198,8 +360,8 @@ vstfx_load_info_file (FILE* fp, vector *infos) } return true; } - vstfx_free_info(info); - vstfx_clear_info_list(infos); + vstfx_free_info (info); + vstfx_clear_info_list (infos); return false; } @@ -232,200 +394,134 @@ vstfx_write_info_block (FILE* fp, VSTInfo *info) static void vstfx_write_info_file (FILE* fp, vector *infos) { - assert(infos); - assert(fp); + assert (infos); + assert (fp); - if (infos->size() > 1) { - vector::iterator x = infos->begin(); + 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 ); + 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); + for (; x != infos->end (); ++x) { + vstfx_write_info_block (fp, *x); } - } else if (infos->size() == 1) { - vstfx_write_info_block(fp, infos->front()); - } else { - PBD::error << "Zero plugins in VST." << endmsg; // XXX here? rather make this impossible before if it ain't already. - } -} - -static string -vstfx_blacklist_path (const char* dllpath, int personal) -{ - string dir; - if (personal) { - dir = get_personal_vst_blacklist_dir(); + } else if (infos->size () == 1) { + vstfx_write_info_block (fp, infos->front ()); } else { - dir = Glib::path_get_dirname (std::string(dllpath)); - } - - stringstream s; - s << "." << Glib::path_get_basename (dllpath) << ".fsb"; - return Glib::build_filename (dir, s.str ()); -} - -/* return true if plugin is blacklisted or has an invalid file extension */ -static bool -vstfx_blacklist_stat (const char *dllpath, int personal) -{ - if (strstr (dllpath, ".so" ) == 0 && strstr(dllpath, ".dll") == 0) { - return true; + PBD::warning << _("VST object file contains no plugins.") << endmsg; } - string const path = vstfx_blacklist_path (dllpath, personal); - - if (Glib::file_test (path, Glib::FileTest (Glib::FILE_TEST_EXISTS | Glib::FILE_TEST_IS_REGULAR))) { - struct stat dllstat; - struct stat fsbstat; - - if (stat (dllpath, &dllstat) == 0 && stat (path.c_str(), &fsbstat) == 0) { - if (dllstat.st_mtime > fsbstat.st_mtime) { - /* plugin is newer than blacklist file */ - return true; - } - } - /* stat failed or plugin is older than blacklist file */ - return true; - } - /* blacklist file does not exist */ - return false; -} - -static bool -vstfx_check_blacklist (const char *dllpath) -{ - if (vstfx_blacklist_stat(dllpath, 0)) return true; - if (vstfx_blacklist_stat(dllpath, 1)) return true; - return false; } -static FILE * -vstfx_blacklist_file (const char *dllpath) -{ - FILE *f; - if ((f = fopen (vstfx_blacklist_path (dllpath, 0).c_str(), "w"))) { - return f; - } - return fopen (vstfx_blacklist_path (dllpath, 1).c_str(), "w"); -} -static bool -vstfx_blacklist (const char *dllpath) -{ - FILE *f = vstfx_blacklist_file(dllpath); - if (f) { - fclose(f); - return true; - } - return false; -} +/* *** CACHE MANAGEMENT *** */ +/** remove info file from cache */ static void -vstfx_un_blacklist (const char *dllpath) -{ - ::g_unlink(vstfx_blacklist_path (dllpath, 0).c_str()); - ::g_unlink(vstfx_blacklist_path (dllpath, 1).c_str()); -} - -static string -vstfx_infofile_path (const char* dllpath, int personal) +vstfx_remove_infofile (const char *dllpath) { - string dir; - if (personal) { - dir = get_personal_vst_info_cache_dir(); - } else { - dir = Glib::path_get_dirname (std::string(dllpath)); - } - - stringstream s; - s << "." << Glib::path_get_basename (dllpath) << ".fsi"; - return Glib::build_filename (dir, s.str ()); + ::g_unlink (vstfx_infofile_path (dllpath).c_str ()); } -static char * -vstfx_infofile_stat (const 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; - 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 (const 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 (const 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 (const 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; @@ -434,14 +530,56 @@ 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; } +/** 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) { @@ -452,25 +590,37 @@ vstfx_parse_vst_state (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); @@ -498,11 +648,11 @@ vstfx_parse_vst_state (VSTState* vstfx) 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]; @@ -514,131 +664,120 @@ vstfx_parse_vst_state (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; } +/** 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, int type) +vstfx_info_from_plugin (const char *dllpath, VSTState* vstfx, vector *infos, enum ARDOUR::PluginType type) { - assert(vstfx); + assert (vstfx); VSTInfo *info; - if ((info = vstfx_parse_vst_state(vstfx))) { - infos->push_back(info); -#if 1 - /* 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; - string path = vstfx->handle->path; - - do { - char name[65] = "Unknown\0"; - id = plugin->dispatcher (plugin, effShellGetNextPlugin, 0, 0, name, 0); - ids.push_back(std::make_pair(id, name)); - } while ( id != 0 ); - - switch(type) { + if (!(info = vstfx_parse_vst_state (vstfx))) { + return; + } + + 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 1: fst_close(vstfx); break; + case ARDOUR::Windows_VST: + fst_close (vstfx); + break; #endif #ifdef LXVST_SUPPORT - case 2: vstfx_close (vstfx); break; + case ARDOUR::LXVST: + vstfx_close (vstfx); + break; #endif - default: assert(0); break; - } + 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() */ + 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) { // TODO use lib ardour's type + bool ok; + switch (type) { #ifdef WINDOWS_VST_SUPPORT - case 1: ok = vstfx_instantiate_and_get_info_fst(dllpath, infos, id); break; + case ARDOUR::Windows_VST: + ok = vstfx_instantiate_and_get_info_fst (dllpath, infos, id); + break; #endif #ifdef LXVST_SUPPORT - case 2: ok = vstfx_instantiate_and_get_info_lx(dllpath, infos, id); break; + case ARDOUR::LXVST: + ok = vstfx_instantiate_and_get_info_lx (dllpath, infos, id); + break; #endif - default: ok = false; + 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"); } - 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 { + 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; + } } +#endif } -/* 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 *ptr, float) -{ - const char* vstfx_can_do_strings[] = { - "supportShell", - "shellCategory" - }; - const int vstfx_can_do_string_count = 2; - 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; - } -} -static bool -vstfx_get_info_from_file(const char* dllpath, vector *infos) -{ - 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 << "Cannot get LinuxVST information form " << dllpath << ": info file load failed." << endmsg; - } - } - return rv; -} +/* *** TOP-LEVEL PLUGIN INSTANTIATION FUNCTIONS *** */ #ifdef LXVST_SUPPORT static bool @@ -647,24 +786,23 @@ vstfx_instantiate_and_get_info_lx ( { VSTHandle* h; VSTState* vstfx; - if (!(h = vstfx_load(dllpath))) { - PBD::warning << "Cannot get LinuxVST information from " << dllpath << ": load failed." << endmsg; + 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; - if (!(vstfx = vstfx_instantiate(h, simple_master_callback, 0))) { - vstfx_unload(h); - PBD::warning << "Cannot get LinuxVST information from " << dllpath << ": instantiation failed." << endmsg; + 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; } vstfx_current_loading_id = 0; - vstfx_info_from_plugin(dllpath, vstfx, infos, 2); + vstfx_info_from_plugin (dllpath, vstfx, infos, ARDOUR::LXVST); - vstfx_close (vstfx); vstfx_unload (h); return true; } @@ -677,48 +815,94 @@ vstfx_instantiate_and_get_info_fst ( { VSTHandle* h; VSTState* vstfx; - if(!(h = fst_load(dllpath))) { - PBD::warning << "Cannot get Windows VST information from " << dllpath << ": load failed." << endmsg; + if (!(h = fst_load (dllpath))) { + PBD::warning << string_compose (_("Cannot get Windows VST information from '%1': load failed."), dllpath) << endmsg; return false; } vstfx_current_loading_id = uniqueID; - if(!(vstfx = fst_instantiate(h, simple_master_callback, 0))) { - fst_unload(&h); + if (!(vstfx = fst_instantiate (h, simple_master_callback, 0))) { + fst_unload (&h); vstfx_current_loading_id = 0; - PBD::warning << "Cannot get Windows VST information from " << dllpath << ": instantiation failed." << endmsg; + PBD::warning << string_compose (_("Cannot get Windows VST information from '%1': instantiation failed."), dllpath) << endmsg; return false; } vstfx_current_loading_id = 0; - vstfx_info_from_plugin(dllpath, vstfx, infos, 1); + vstfx_info_from_plugin (dllpath, vstfx, infos, ARDOUR::Windows_VST); - fst_close(vstfx); - //fst_unload(&h); // XXX -> fst_close() 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*/) { - // TODO write to blacklist or error file..? - PBD::error << "VST scanner: " << msg; + if (!_errorlog_fd && !_errorlog_dll) { + PBD::error << "VST scanner: " << msg; + return; + } + +#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 + + 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; + } +} + +static void +set_error_log (const char* dllpath) { + assert (!_errorlog_fd); + assert (!_errorlog_dll); + _errorlog_dll = strdup (dllpath); +} + +static void +close_error_log () { + if (_errorlog_fd) { + fclose (_errorlog_fd); + _errorlog_fd = 0; + } + free (_errorlog_dll); + _errorlog_dll = 0; } + #endif + +/* *** the main function that uses all of the above *** */ + static vector * -vstfx_get_info (const char* dllpath, int type, enum VSTScanMode mode) +vstfx_get_info (const char* dllpath, enum ARDOUR::PluginType type, enum VSTScanMode mode) { FILE* infofile; vector *infos = new vector; - if (vstfx_check_blacklist(dllpath)) { + if (vst_is_blacklisted (dllpath)) { return infos; } - if (vstfx_get_info_from_file(dllpath, infos)) { + if (vstfx_get_info_from_file (dllpath, infos)) { return infos; } @@ -732,27 +916,50 @@ vstfx_get_info (const char* dllpath, int type, enum VSTScanMode mode) 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); + char **argp= (char**) calloc (3,sizeof (char*)); + argp[0] = strdup (scanner_bin_path.c_str ()); + argp[1] = strdup (dllpath); argp[2] = 0; - PBD::SystemExec scanner (scanner_bin_path, argp); + set_error_log (dllpath); + ARDOUR::SystemExec scanner (scanner_bin_path, argp); PBD::ScopedConnectionList cons; - // TODO timeout.., and honor user-terminate - //scanner->Terminated.connect_same_thread (cons, boost::bind (&scanner_terminated)) scanner.ReadStdout.connect_same_thread (cons, boost::bind (&parse_scanner_output, _1 ,_2)); if (scanner.start (2 /* send stderr&stdout via signal */)) { - PBD::error << "Cannot launch VST scanner app '" << scanner_bin_path << "': "<< strerror(errno) << endmsg; + PBD::error << string_compose (_("Cannot launch VST scanner app '%1': %2"), scanner_bin_path, strerror (errno)) << endmsg; + close_error_log (); return infos; } else { - // TODO idle loop (emit signal to GUI to call gtk_main_iteration()) check cancel. - scanner.wait(); + 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 (); } - /* re-read index */ - vstfx_clear_info_list(infos); - if (!vstfx_check_blacklist(dllpath)) { - vstfx_get_info_from_file(dllpath, infos); + 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; } @@ -763,16 +970,22 @@ vstfx_get_info (const char* dllpath, int type, enum VSTScanMode mode) bool ok; /* blacklist in case instantiation fails */ - vstfx_blacklist(dllpath); + vstfx_blacklist (dllpath); - switch (type) { // TODO use lib ardour's type + switch (type) { #ifdef WINDOWS_VST_SUPPORT - case 1: ok = vstfx_instantiate_and_get_info_fst(dllpath, infos, 0); break; + case ARDOUR::Windows_VST: + ok = vstfx_instantiate_and_get_info_fst (dllpath, infos, 0); + break; #endif #ifdef LXVST_SUPPORT - case 2: ok = vstfx_instantiate_and_get_info_lx(dllpath, infos, 0); break; + case ARDOUR::LXVST: + ok = vstfx_instantiate_and_get_info_lx (dllpath, infos, 0); + break; #endif - default: ok = false; + default: + ok = false; + break; } if (!ok) { @@ -780,12 +993,12 @@ vstfx_get_info (const char* dllpath, int type, enum VSTScanMode mode) } /* remove from blacklist */ - vstfx_un_blacklist(dllpath); + vstfx_un_blacklist (dllpath); /* crate cache/whitelist */ infofile = vstfx_infofile_for_write (dllpath); if (!infofile) { - PBD::warning << "Cannot cache VST information for " << dllpath << ": cannot create new FST info file." << endmsg; + 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); @@ -794,48 +1007,23 @@ vstfx_get_info (const char* dllpath, int type, enum VSTScanMode mode) return infos; } + /* *** public API *** */ void vstfx_free_info_list (vector *infos) { - for (vector::iterator i = infos->begin(); i != infos->end(); ++i) { - vstfx_free_info(*i); + for (vector::iterator i = infos->begin (); i != infos->end (); ++i) { + vstfx_free_info (*i); } delete infos; } -string -get_personal_vst_blacklist_dir() { - string dir = Glib::build_filename (ARDOUR::user_cache_directory(), "fst_blacklist"); - /* 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::error << "Cannt create VST cache folder '" << dir << "'" << endmsg; - //exit(1); - } - } - return dir; -} - -string -get_personal_vst_info_cache_dir() { - string dir = Glib::build_filename (ARDOUR::user_cache_directory(), "fst_info"); - /* 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::error << "Cannt create VST info folder '" << dir << "'" << endmsg; - //exit(1); - } - } - return dir; -} - #ifdef LXVST_SUPPORT vector * vstfx_get_info_lx (char* dllpath, enum VSTScanMode mode) { - return vstfx_get_info(dllpath, 2, mode); + return vstfx_get_info (dllpath, ARDOUR::LXVST, mode); } #endif @@ -843,6 +1031,10 @@ vstfx_get_info_lx (char* dllpath, enum VSTScanMode mode) vector * vstfx_get_info_fst (char* dllpath, enum VSTScanMode mode) { - return vstfx_get_info(dllpath, 1, mode); + return vstfx_get_info (dllpath, ARDOUR::Windows_VST, mode); } #endif + +#ifndef VST_SCANNER_APP +} // namespace +#endif