X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fplugin_manager.cc;h=596acadf6cd24af8c20c17fda788d6f7d6c7e641;hb=8b93fb02f38148c25c91ed41a4f12735be38dbf0;hp=cfdfc6af7f2ca55aa8733711d5db054c8a20966f;hpb=adcb0faf6ba025d0ed8d79676d9a91ae1e00a9fd;p=ardour.git diff --git a/libs/ardour/plugin_manager.cc b/libs/ardour/plugin_manager.cc index cfdfc6af7f..596acadf6c 100644 --- a/libs/ardour/plugin_manager.cc +++ b/libs/ardour/plugin_manager.cc @@ -26,7 +26,9 @@ #include #include #include -#include + +#include +#include "pbd/gstdio_compat.h" #ifdef HAVE_LRDF #include @@ -37,6 +39,13 @@ #include "fst.h" #include "pbd/basename.h" #include + +// dll-info +#include +#include +#include +#include + #endif // WINDOWS_VST_SUPPORT #ifdef LXVST_SUPPORT @@ -46,11 +55,19 @@ #include #endif //LXVST_SUPPORT -#include +#ifdef MACVST_SUPPORT +#include "ardour/vst_info_file.h" +#include "ardour/mac_vst_support.h" +#include "ardour/mac_vst_plugin.h" +#include "pbd/basename.h" +#include +#endif //MACVST_SUPPORT + #include #include +#include +#include -#include "pbd/pathscanner.h" #include "pbd/whitespace.h" #include "pbd/file_utils.h" @@ -58,11 +75,13 @@ #include "ardour/filesystem_paths.h" #include "ardour/ladspa.h" #include "ardour/ladspa_plugin.h" +#include "ardour/luascripting.h" +#include "ardour/luaproc.h" #include "ardour/plugin.h" #include "ardour/plugin_manager.h" #include "ardour/rc_configuration.h" -#include "ardour/ladspa_search_path.h" +#include "ardour/search_paths.h" #ifdef LV2_SUPPORT #include "ardour/lv2_plugin.h" @@ -84,7 +103,7 @@ #include "pbd/error.h" #include "pbd/stl_delete.h" -#include "i18n.h" +#include "pbd/i18n.h" #include "ardour/debug.h" @@ -93,6 +112,7 @@ using namespace PBD; using namespace std; PluginManager* PluginManager::_instance = 0; +std::string PluginManager::scanner_bin_path = ""; PluginManager& PluginManager::instance() @@ -106,14 +126,48 @@ PluginManager::instance() PluginManager::PluginManager () : _windows_vst_plugin_info(0) , _lxvst_plugin_info(0) + , _mac_vst_plugin_info(0) , _ladspa_plugin_info(0) , _lv2_plugin_info(0) , _au_plugin_info(0) - , cancel_scan(false) + , _lua_plugin_info(0) + , _cancel_scan(false) + , _cancel_timeout(false) { char* s; string lrdf_path; +#if defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT || defined MACVST_SUPPORT + // source-tree (ardev, etc) + PBD::Searchpath vstsp(Glib::build_filename(ARDOUR::ardour_dll_directory(), "fst")); + +#ifdef PLATFORM_WINDOWS + // on windows the .exe needs to be in the same folder with libardour.dll + vstsp += Glib::build_filename(windows_package_directory_path(), "bin"); +#else + // on Unices additional internal-use binaries are deployed to $libdir + vstsp += ARDOUR::ardour_dll_directory(); +#endif + + if (!PBD::find_file (vstsp, +#ifdef PLATFORM_WINDOWS + #ifdef DEBUGGABLE_SCANNER_APP + #if defined(DEBUG) || defined(_DEBUG) + "ardour-vst-scannerD.exe" + #else + "ardour-vst-scannerRDC.exe" + #endif + #else + "ardour-vst-scanner.exe" + #endif +#else + "ardour-vst-scanner" +#endif + , scanner_bin_path)) { + PBD::warning << "VST scanner app (ardour-vst-scanner) not found in path " << vstsp.to_string() << endmsg; + } +#endif + load_statuses (); if ((s = getenv ("LADSPA_RDF_PATH"))){ @@ -138,124 +192,232 @@ PluginManager::PluginManager () } #endif /* Native LinuxVST support*/ +#ifdef MACVST_SUPPORT + if (Config->get_use_macvst ()) { + add_mac_vst_presets (); + } +#endif + if ((s = getenv ("VST_PATH"))) { windows_vst_path = s; } else if ((s = getenv ("VST_PLUGINS"))) { windows_vst_path = s; } + if (windows_vst_path.length() == 0) { + windows_vst_path = vst_search_path (); + } + if ((s = getenv ("LXVST_PATH"))) { lxvst_path = s; } else if ((s = getenv ("LXVST_PLUGINS"))) { lxvst_path = s; } - if (_instance == 0) { - _instance = this; + if (lxvst_path.length() == 0) { + lxvst_path = "/usr/local/lib64/lxvst:/usr/local/lib/lxvst:/usr/lib64/lxvst:/usr/lib/lxvst:" + "/usr/local/lib64/linux_vst:/usr/local/lib/linux_vst:/usr/lib64/linux_vst:/usr/lib/linux_vst:" + "/usr/lib/vst:/usr/local/lib/vst"; } - /* the plugin manager is constructed too early to use Profile */ + /* first time setup, use 'default' path */ + if (Config->get_plugin_path_lxvst() == X_("@default@")) { + Config->set_plugin_path_lxvst(get_default_lxvst_path()); + } + if (Config->get_plugin_path_vst() == X_("@default@")) { + Config->set_plugin_path_vst(get_default_windows_vst_path()); + } - if (getenv ("ARDOUR_SAE")) { - ladspa_plugin_whitelist.push_back (1203); // single band parametric - ladspa_plugin_whitelist.push_back (1772); // caps compressor - ladspa_plugin_whitelist.push_back (1913); // fast lookahead limiter - ladspa_plugin_whitelist.push_back (1075); // simple RMS expander - ladspa_plugin_whitelist.push_back (1061); // feedback delay line (max 5s) - ladspa_plugin_whitelist.push_back (1216); // gverb - ladspa_plugin_whitelist.push_back (2150); // tap pitch shifter + if (_instance == 0) { + _instance = this; } BootMessage (_("Discovering Plugins")); + + LuaScripting::instance().scripts_changed.connect_same_thread (lua_refresh_connection, boost::bind (&PluginManager::lua_refresh_cb, this)); } PluginManager::~PluginManager() { + if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) { + // don't bother, just exit quickly. + delete _windows_vst_plugin_info; + delete _lxvst_plugin_info; + delete _mac_vst_plugin_info; + delete _ladspa_plugin_info; + delete _lv2_plugin_info; + delete _au_plugin_info; + delete _lua_plugin_info; + } } - void -PluginManager::refresh () +PluginManager::refresh (bool cache_only) { + Glib::Threads::Mutex::Lock lm (_lock, Glib::Threads::TRY_LOCK); + + if (!lm.locked()) { + return; + } + DEBUG_TRACE (DEBUG::PluginManager, "PluginManager::refresh\n"); - BootMessage (_("Discovering Plugins")); + _cancel_scan = false; + BootMessage (_("Scanning LADSPA Plugins")); ladspa_refresh (); + BootMessage (_("Scanning Lua DSP Processors")); + lua_refresh (); #ifdef LV2_SUPPORT + BootMessage (_("Scanning LV2 Plugins")); lv2_refresh (); #endif #ifdef WINDOWS_VST_SUPPORT if (Config->get_use_windows_vst()) { - windows_vst_refresh (); + if (cache_only) { + BootMessage (_("Scanning Windows VST Plugins")); + } else { + BootMessage (_("Discovering Windows VST Plugins")); + } + windows_vst_refresh (cache_only); } #endif // WINDOWS_VST_SUPPORT #ifdef LXVST_SUPPORT if(Config->get_use_lxvst()) { - lxvst_refresh(); + if (cache_only) { + BootMessage (_("Scanning Linux VST Plugins")); + } else { + BootMessage (_("Discovering Linux VST Plugins")); + } + lxvst_refresh(cache_only); } #endif //Native linuxVST SUPPORT +#ifdef MACVST_SUPPORT + if(Config->get_use_macvst ()) { + if (cache_only) { + BootMessage (_("Scanning Mac VST Plugins")); + } else { + BootMessage (_("Discovering Mac VST Plugins")); + } + mac_vst_refresh (cache_only); + } +#endif //Native Mac VST SUPPORT + +#if (defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT || defined MACVST_SUPPORT) + if (!cache_only) { + string fn = Glib::build_filename (ARDOUR::user_cache_directory(), VST_BLACKLIST); + if (Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) { + gchar *bl = NULL; + if (g_file_get_contents(fn.c_str (), &bl, NULL, NULL)) { + if (Config->get_verbose_plugin_scan()) { + PBD::info << _("VST Blacklist: ") << fn << "\n" << bl << "-----" << endmsg; + } else { + PBD::info << _("VST Blacklist:") << "\n" << bl << "-----" << endmsg; + } + g_free (bl); + } + } + } +#endif + #ifdef AUDIOUNIT_SUPPORT - au_refresh (); + if (cache_only) { + BootMessage (_("Scanning AU Plugins")); + } else { + BootMessage (_("Discovering AU Plugins")); + } + au_refresh (cache_only); #endif + BootMessage (_("Plugin Scan Complete...")); PluginListChanged (); /* EMIT SIGNAL */ - PluginScanMessage(X_("closeme"), ""); + PluginScanMessage(X_("closeme"), "", false); + _cancel_scan = false; } void PluginManager::cancel_plugin_scan () { - // TODO + _cancel_scan = true; +} + +void +PluginManager::cancel_plugin_timeout () +{ + _cancel_timeout = true; } void PluginManager::clear_vst_cache () { - // see also libs/ardour/vst_info_file.cc - vstfx_infofile_path() +#if 1 // clean old cache and error files. (remove this code after 4.3 or 5.0) #ifdef WINDOWS_VST_SUPPORT { - PathScanner scanner; - vector *fsi_files; - - fsi_files = scanner (windows_vst_path, "\\.fsi$", true, true, -1, false); - if (fsi_files) { - for (vector::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) { - ::g_unlink((*i)->c_str()); - } + vector fsi_files; + find_files_matching_regex (fsi_files, Config->get_plugin_path_vst(), "\\" VST_EXT_INFOFILE "$", true); + for (vector::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) { + ::g_unlink(i->c_str()); + } + } + { + vector fsi_files; + find_files_matching_regex (fsi_files, Config->get_plugin_path_vst(), "\\.fsi$", true); + for (vector::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) { + ::g_unlink(i->c_str()); + } + } + { + vector fsi_files; + find_files_matching_regex (fsi_files, Config->get_plugin_path_vst(), "\\.err$", true); + for (vector::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) { + ::g_unlink(i->c_str()); } - vector_delete(fsi_files); } #endif #ifdef LXVST_SUPPORT { - PathScanner scanner; - vector *fsi_files; - fsi_files = scanner (lxvst_path, "\\.fsi$", true, true, -1, false); - if (fsi_files) { - for (vector::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) { - ::g_unlink((*i)->c_str()); - } + vector fsi_files; + find_files_matching_regex (fsi_files, Config->get_plugin_path_lxvst(), "\\" VST_EXT_INFOFILE "$", true); + for (vector::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) { + ::g_unlink(i->c_str()); + } + } + { + vector fsi_files; + find_files_matching_regex (fsi_files, Config->get_plugin_path_lxvst(), "\\.fsi$", true); + for (vector::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) { + ::g_unlink(i->c_str()); + } + } + { + vector fsi_files; + find_files_matching_regex (fsi_files, Config->get_plugin_path_lxvst(), "\\.err$", true); + for (vector::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) { + ::g_unlink(i->c_str()); } - vector_delete(fsi_files); } #endif - #if (defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT) { - string personal = get_personal_vst_info_cache_dir(); - PathScanner scanner; - vector *fsi_files; - fsi_files = scanner (personal, "\\.fsi$", true, true, -1, false); - if (fsi_files) { - for (vector::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) { - ::g_unlink((*i)->c_str()); - } + string dir = Glib::build_filename (ARDOUR::user_cache_directory(), "fst_info"); + if (Glib::file_test (dir, Glib::FILE_TEST_IS_DIR)) { + PBD::remove_directory (dir); + } + } +#endif +#endif // old cache cleanup + +#if (defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT || defined MACVST_SUPPORT) + { + string dn = Glib::build_filename (ARDOUR::user_cache_directory(), "vst"); + vector fsi_files; + find_files_matching_regex (fsi_files, dn, "\\" VST_EXT_INFOFILE "$", /* user cache is flat, no recursion */ false); + for (vector::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) { + ::g_unlink(i->c_str()); } - vector_delete(fsi_files); } #endif } @@ -263,50 +425,92 @@ PluginManager::clear_vst_cache () void PluginManager::clear_vst_blacklist () { +#if 1 // remove old blacklist files. (remove this code after 4.3 or 5.0) + #ifdef WINDOWS_VST_SUPPORT { - PathScanner scanner; - vector *fsi_files; - - fsi_files = scanner (windows_vst_path, "\\.fsb$", true, true, -1, false); - if (fsi_files) { - for (vector::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) { - ::g_unlink((*i)->c_str()); - } + vector fsi_files; + find_files_matching_regex (fsi_files, Config->get_plugin_path_vst(), "\\" VST_EXT_BLACKLIST "$", true); + for (vector::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) { + ::g_unlink(i->c_str()); } - vector_delete(fsi_files); } #endif #ifdef LXVST_SUPPORT { - PathScanner scanner; - vector *fsi_files; - fsi_files = scanner (lxvst_path, "\\.fsb$", true, true, -1, false); - if (fsi_files) { - for (vector::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) { - ::g_unlink((*i)->c_str()); - } + vector fsi_files; + find_files_matching_regex (fsi_files, Config->get_plugin_path_lxvst(), "\\" VST_EXT_BLACKLIST "$", true); + for (vector::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) { + ::g_unlink(i->c_str()); } - vector_delete(fsi_files); } #endif - #if (defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT) { - string personal = get_personal_vst_blacklist_dir(); - - PathScanner scanner; - vector *fsi_files; - fsi_files = scanner (personal, "\\.fsb$", true, true, -1, false); - if (fsi_files) { - for (vector::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) { - ::g_unlink((*i)->c_str()); - } + string dir = Glib::build_filename (ARDOUR::user_cache_directory(), "fst_blacklist"); + if (Glib::file_test (dir, Glib::FILE_TEST_IS_DIR)) { + PBD::remove_directory (dir); } - vector_delete(fsi_files); } #endif + +#endif // old blacklist cleanup + +#if (defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT || defined MACVST_SUPPORT) + { + string fn = Glib::build_filename (ARDOUR::user_cache_directory(), VST_BLACKLIST); + if (Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) { + ::g_unlink (fn.c_str()); + } + } +#endif + +} + +void +PluginManager::clear_au_cache () +{ +#ifdef AUDIOUNIT_SUPPORT + AUPluginInfo::clear_cache (); +#endif +} + +void +PluginManager::clear_au_blacklist () +{ +#ifdef AUDIOUNIT_SUPPORT + string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_blacklist.txt"); + if (Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) { + ::g_unlink(fn.c_str()); + } +#endif +} + +void +PluginManager::lua_refresh () +{ + if (_lua_plugin_info) { + _lua_plugin_info->clear (); + } else { + _lua_plugin_info = new ARDOUR::PluginInfoList (); + } + ARDOUR::LuaScriptList & _scripts (LuaScripting::instance ().scripts (LuaScriptInfo::DSP)); + for (LuaScriptList::const_iterator s = _scripts.begin(); s != _scripts.end(); ++s) { + LuaPluginInfoPtr lpi (new LuaPluginInfo(*s)); + _lua_plugin_info->push_back (lpi); + } +} + +void +PluginManager::lua_refresh_cb () +{ + Glib::Threads::Mutex::Lock lm (_lock, Glib::Threads::TRY_LOCK); + if (!lm.locked()) { + return; + } + lua_refresh (); + PluginListChanged (); /* EMIT SIGNAL */ } void @@ -328,25 +532,17 @@ PluginManager::ladspa_refresh () DEBUG_TRACE (DEBUG::PluginManager, string_compose ("LADSPA: search along: [%1]\n", ladspa_search_path().to_string())); - Glib::PatternSpec so_extension_pattern("*.so"); - Glib::PatternSpec dylib_extension_pattern("*.dylib"); - Glib::PatternSpec dll_extension_pattern("*.dll"); - - find_matching_files_in_search_path (ladspa_search_path (), - so_extension_pattern, ladspa_modules); - - find_matching_files_in_search_path (ladspa_search_path (), - dylib_extension_pattern, ladspa_modules); - - find_matching_files_in_search_path (ladspa_search_path (), - dll_extension_pattern, ladspa_modules); + find_files_matching_pattern (ladspa_modules, ladspa_search_path (), "*.so"); + find_files_matching_pattern (ladspa_modules, ladspa_search_path (), "*.dylib"); + find_files_matching_pattern (ladspa_modules, ladspa_search_path (), "*.dll"); for (vector::iterator i = ladspa_modules.begin(); i != ladspa_modules.end(); ++i) { - ARDOUR::PluginScanMessage(_("LADSPA"), *i); + ARDOUR::PluginScanMessage(_("LADSPA"), *i, false); ladspa_discover (*i); } } +#ifdef HAVE_LRDF static bool rdf_filter (const string &str, void* /*arg*/) { return str[0] != '.' && @@ -355,6 +551,7 @@ static bool rdf_filter (const string &str, void* /*arg*/) (str.find(".n3") == (str.length() - 3)) || (str.find(".ttl") == (str.length() - 4))); } +#endif void PluginManager::add_ladspa_presets() @@ -368,6 +565,12 @@ PluginManager::add_windows_vst_presets() add_presets ("windows-vst"); } +void +PluginManager::add_mac_vst_presets() +{ + add_presets ("mac-vst"); +} + void PluginManager::add_lxvst_presets() { @@ -378,9 +581,8 @@ void PluginManager::add_presets(string domain) { #ifdef HAVE_LRDF - PathScanner scanner; - vector *presets; - vector::iterator x; + vector presets; + vector::iterator x; char* envvar; if ((envvar = getenv ("HOME")) == 0) { @@ -388,18 +590,15 @@ PluginManager::add_presets(string domain) } string path = string_compose("%1/.%2/rdf", envvar, domain); - presets = scanner (path, rdf_filter, 0, false, true); + find_files_matching_filter (presets, path, rdf_filter, 0, false, true); - if (presets) { - for (x = presets->begin(); x != presets->end (); ++x) { - string file = "file:" + **x; - if (lrdf_read_file(file.c_str())) { - warning << string_compose(_("Could not parse rdf file: %1"), *x) << endmsg; - } + for (x = presets.begin(); x != presets.end (); ++x) { + string file = "file:" + *x; + if (lrdf_read_file(file.c_str())) { + warning << string_compose(_("Could not parse rdf file: %1"), *x) << endmsg; } - - vector_delete (presets); } + #endif } @@ -407,22 +606,17 @@ void PluginManager::add_lrdf_data (const string &path) { #ifdef HAVE_LRDF - PathScanner scanner; - vector* rdf_files; - vector::iterator x; + vector rdf_files; + vector::iterator x; - rdf_files = scanner (path, rdf_filter, 0, false, true); + find_files_matching_filter (rdf_files, path, rdf_filter, 0, false, true); - if (rdf_files) { - for (x = rdf_files->begin(); x != rdf_files->end (); ++x) { - const string uri(string("file://") + **x); + for (x = rdf_files.begin(); x != rdf_files.end (); ++x) { + const string uri(string("file://") + *x); - if (lrdf_read_file(uri.c_str())) { - warning << "Could not parse rdf file: " << uri << endmsg; - } + if (lrdf_read_file(uri.c_str())) { + warning << "Could not parse rdf file: " << uri << endmsg; } - - vector_delete (rdf_files); } #endif } @@ -455,6 +649,11 @@ PluginManager::ladspa_discover (string path) DEBUG_TRACE (DEBUG::PluginManager, string_compose ("LADSPA plugin found at %1\n", path)); for (uint32_t i = 0; ; ++i) { + /* if a ladspa plugin allocates memory here + * it is never free()ed (or plugin-dependent only when unloading). + * For some plugins memory allocated is incremental, we should + * avoid re-scanning plugins and file bug reports. + */ if ((descriptor = dfunc (i)) == 0) { break; } @@ -595,11 +794,21 @@ PluginManager::lv2_refresh () #ifdef AUDIOUNIT_SUPPORT void -PluginManager::au_refresh () +PluginManager::au_refresh (bool cache_only) { DEBUG_TRACE (DEBUG::PluginManager, "AU: refresh\n"); + + // disable automatic discovery in case we crash + bool discover_at_start = Config->get_discover_audio_units (); + Config->set_discover_audio_units (false); + Config->save_state(); + delete _au_plugin_info; - _au_plugin_info = AUPluginInfo::discover(); + _au_plugin_info = AUPluginInfo::discover(cache_only && !discover_at_start); + + // successful scan re-enabled automatic discovery if it was set + Config->set_discover_audio_units (discover_at_start); + Config->save_state(); } #endif @@ -607,7 +816,7 @@ PluginManager::au_refresh () #ifdef WINDOWS_VST_SUPPORT void -PluginManager::windows_vst_refresh () +PluginManager::windows_vst_refresh (bool cache_only) { if (_windows_vst_plugin_info) { _windows_vst_plugin_info->clear (); @@ -615,64 +824,127 @@ PluginManager::windows_vst_refresh () _windows_vst_plugin_info = new ARDOUR::PluginInfoList(); } - if (windows_vst_path.length() == 0) { - windows_vst_path = "/usr/local/lib/vst:/usr/lib/vst"; - } - - windows_vst_discover_from_path (windows_vst_path); -} - -int -PluginManager::add_windows_vst_directory (string path) -{ - if (windows_vst_discover_from_path (path) == 0) { - windows_vst_path += ':'; - windows_vst_path += path; - return 0; - } - return -1; + windows_vst_discover_from_path (Config->get_plugin_path_vst(), cache_only); } static bool windows_vst_filter (const string& str, void * /*arg*/) { /* Not a dotfile, has a prefix before a period, suffix is "dll" */ - - return str[0] != '.' && (str.length() > 4 && str.find (".dll") == (str.length() - 4)); + return str[0] != '.' && str.length() > 4 && strings_equal_ignore_case (".dll", str.substr(str.length() - 4)); } int -PluginManager::windows_vst_discover_from_path (string path) +PluginManager::windows_vst_discover_from_path (string path, bool cache_only) { - PathScanner scanner; - vector *plugin_objects; - vector::iterator x; + vector plugin_objects; + vector::iterator x; int ret = 0; - DEBUG_TRACE (DEBUG::PluginManager, string_compose ("detecting Windows VST plugins along %1\n", path)); + DEBUG_TRACE (DEBUG::PluginManager, string_compose ("Discovering Windows VST plugins along %1\n", path)); + + if (Config->get_verbose_plugin_scan()) { + info << string_compose (_("--- Windows VST plugins Scan: %1"), path) << endmsg; + } - plugin_objects = scanner (windows_vst_path, windows_vst_filter, 0, false, true); + find_files_matching_filter (plugin_objects, path, windows_vst_filter, 0, false, true, true); - if (plugin_objects) { - for (x = plugin_objects->begin(); x != plugin_objects->end (); ++x) { - ARDOUR::PluginScanMessage(_("VST"), **x); - windows_vst_discover (**x); - } + for (x = plugin_objects.begin(); x != plugin_objects.end (); ++x) { + ARDOUR::PluginScanMessage(_("VST"), *x, !cache_only && !cancelled()); + windows_vst_discover (*x, cache_only || cancelled()); + } - vector_delete (plugin_objects); + if (Config->get_verbose_plugin_scan()) { + info << _("--- Windows VST plugins Scan Done") << endmsg; } return ret; } +static std::string dll_info (std::string path) { + std::string rv; + uint8_t buf[68]; + uint16_t type = 0; + off_t pe_hdr_off = 0; + + int fd = g_open(path.c_str(), O_RDONLY, 0444); + + if (fd < 0) { + return _("cannot open dll"); // TODO strerror() + } + + if (68 != read (fd, buf, 68)) { + rv = _("invalid dll, file too small"); + goto errorout; + } + if (buf[0] != 'M' && buf[1] != 'Z') { + rv = _("not a dll"); + goto errorout; + } + + pe_hdr_off = *((int32_t*) &buf[60]); + if (pe_hdr_off !=lseek (fd, pe_hdr_off, SEEK_SET)) { + rv = _("cannot determine dll type"); + goto errorout; + } + if (6 != read (fd, buf, 6)) { + rv = _("cannot read dll PE header"); + goto errorout; + } + + if (buf[0] != 'P' && buf[1] != 'E') { + rv = _("invalid dll PE header"); + goto errorout; + } + + type = *((uint16_t*) &buf[4]); + switch (type) { + case 0x014c: + rv = _("i386 (32-bit)"); + break; + case 0x0200: + rv = _("Itanium"); + break; + case 0x8664: + rv = _("x64 (64-bit)"); + break; + case 0: + rv = _("Native Architecture"); + break; + default: + rv = _("Unknown Architecture"); + break; + } +errorout: + assert (rv.length() > 0); + close (fd); + return rv; +} + int -PluginManager::windows_vst_discover (string path) +PluginManager::windows_vst_discover (string path, bool cache_only) { DEBUG_TRACE (DEBUG::PluginManager, string_compose ("windows_vst_discover '%1'\n", path)); - vector * finfos = vstfx_get_info_fst (const_cast (path.c_str())); + if (Config->get_verbose_plugin_scan()) { + if (cache_only) { + info << string_compose (_(" * %1 (cache only)"), path) << endmsg; + } else { + info << string_compose (_(" * %1 - %2"), path, dll_info (path)) << endmsg; + } + } + + _cancel_timeout = false; + vector * finfos = vstfx_get_info_fst (const_cast (path.c_str()), + cache_only ? VST_SCAN_CACHE_ONLY : VST_SCAN_USE_APP); + + // TODO get extended error messae from vstfx_get_info_fst() e.g blacklisted, 32/64bit compat, + // .err file scanner output etc. if (finfos->empty()) { DEBUG_TRACE (DEBUG::PluginManager, string_compose ("Cannot get Windows VST information from '%1'\n", path)); + if (Config->get_verbose_plugin_scan()) { + info << _(" -> Cannot get Windows VST information, plugin ignored.") << endmsg; + } return -1; } @@ -682,7 +954,7 @@ PluginManager::windows_vst_discover (string path) char buf[32]; if (!finfo->canProcessReplacing) { - warning << string_compose (_("VST plugin %1 does not support processReplacing, and so cannot be used in %2 at this time"), + warning << string_compose (_("VST plugin %1 does not support processReplacing, and cannot be used in %2 at this time"), finfo->name, PROGRAM_NAME) << endl; continue; @@ -707,7 +979,8 @@ PluginManager::windows_vst_discover (string path) info->index = 0; info->n_inputs.set_audio (finfo->numInputs); info->n_outputs.set_audio (finfo->numOutputs); - info->n_inputs.set_midi (finfo->wantMidi ? 1 : 0); + info->n_inputs.set_midi ((finfo->wantMidi&1) ? 1 : 0); + info->n_outputs.set_midi ((finfo->wantMidi&2) ? 1 : 0); info->type = ARDOUR::Windows_VST; // TODO: check dup-IDs (lxvst AND windows vst) @@ -715,8 +988,8 @@ PluginManager::windows_vst_discover (string path) if (!_windows_vst_plugin_info->empty()) { for (PluginInfoList::iterator i =_windows_vst_plugin_info->begin(); i != _windows_vst_plugin_info->end(); ++i) { - if ((info->type == (*i)->type)&&(info->unique_id == (*i)->unique_id)) { - warning << "Ignoring duplicate Windows VST plugin " << info->name << "\n"; + if ((info->type == (*i)->type) && (info->unique_id == (*i)->unique_id)) { + warning << string_compose (_("Ignoring duplicate Windows VST plugin \"%1\""), info->name) << endmsg; duplicate = true; break; } @@ -727,6 +1000,9 @@ PluginManager::windows_vst_discover (string path) DEBUG_TRACE (DEBUG::PluginManager, string_compose ("Windows VST plugin ID '%1'\n", info->unique_id)); _windows_vst_plugin_info->push_back (info); discovered++; + if (Config->get_verbose_plugin_scan()) { + PBD::info << string_compose (_(" -> OK (VST Plugin \"%1\" was added)."), info->name) << endmsg; + } } } @@ -736,35 +1012,124 @@ PluginManager::windows_vst_discover (string path) #endif // WINDOWS_VST_SUPPORT -#ifdef LXVST_SUPPORT - +#ifdef MACVST_SUPPORT void -PluginManager::lxvst_refresh () +PluginManager::mac_vst_refresh (bool cache_only) { - if (_lxvst_plugin_info) { - _lxvst_plugin_info->clear (); + if (_mac_vst_plugin_info) { + _mac_vst_plugin_info->clear (); } else { - _lxvst_plugin_info = new ARDOUR::PluginInfoList(); + _mac_vst_plugin_info = new ARDOUR::PluginInfoList(); } - if (lxvst_path.length() == 0) { - lxvst_path = "/usr/local/lib64/lxvst:/usr/local/lib/lxvst:/usr/lib64/lxvst:/usr/lib/lxvst:" - "/usr/local/lib64/linux_vst:/usr/local/lib/linux_vst:/usr/lib64/linux_vst:/usr/lib/linux_vst:" - "/usr/lib/vst:/usr/local/lib/vst"; + mac_vst_discover_from_path ("~/Library/Audio/Plug-Ins/VST:/Library/Audio/Plug-Ins/VST", cache_only); +} + +static bool mac_vst_filter (const string& str, void *) +{ + if (!Glib::file_test (str, Glib::FILE_TEST_IS_DIR)) { + return false; } + string plist = Glib::build_filename (str, "Contents", "Info.plist"); + if (!Glib::file_test (plist, Glib::FILE_TEST_IS_REGULAR)) { + return false; + } + return str[0] != '.' && str.length() > 4 && strings_equal_ignore_case (".vst", str.substr(str.length() - 4)); +} - lxvst_discover_from_path (lxvst_path); +int +PluginManager::mac_vst_discover_from_path (string path, bool cache_only) +{ + vector plugin_objects; + vector::iterator x; + + find_paths_matching_filter (plugin_objects, path, mac_vst_filter, 0, true, true, false); + + for (x = plugin_objects.begin(); x != plugin_objects.end (); ++x) { + ARDOUR::PluginScanMessage(_("MacVST"), *x, !cache_only && !cancelled()); + mac_vst_discover (*x, cache_only || cancelled()); + } + return 0; } int -PluginManager::add_lxvst_directory (string path) +PluginManager::mac_vst_discover (string path, bool cache_only) { - if (lxvst_discover_from_path (path) == 0) { - lxvst_path += ':'; - lxvst_path += path; - return 0; + DEBUG_TRACE (DEBUG::PluginManager, string_compose ("checking apparent MacVST plugin at %1\n", path)); + + _cancel_timeout = false; + + vector* finfos = vstfx_get_info_mac (const_cast (path.c_str()), + cache_only ? VST_SCAN_CACHE_ONLY : VST_SCAN_USE_APP); + + if (finfos->empty()) { + DEBUG_TRACE (DEBUG::PluginManager, string_compose ("Cannot get Mac VST information from '%1'\n", path)); + return -1; } - return -1; + + uint32_t discovered = 0; + for (vector::iterator x = finfos->begin(); x != finfos->end(); ++x) { + VSTInfo* finfo = *x; + char buf[32]; + + if (!finfo->canProcessReplacing) { + warning << string_compose (_("Mac VST plugin %1 does not support processReplacing, and so cannot be used in %2 at this time"), + finfo->name, PROGRAM_NAME) + << endl; + continue; + } + + PluginInfoPtr info (new MacVSTPluginInfo); + + info->name = finfo->name; + + snprintf (buf, sizeof (buf), "%d", finfo->UniqueID); + info->unique_id = buf; + info->category = "MacVST"; + info->path = path; + info->creator = finfo->creator; + info->index = 0; + info->n_inputs.set_audio (finfo->numInputs); + info->n_outputs.set_audio (finfo->numOutputs); + info->n_inputs.set_midi ((finfo->wantMidi&1) ? 1 : 0); + info->n_outputs.set_midi ((finfo->wantMidi&2) ? 1 : 0); + info->type = ARDOUR::MacVST; + + bool duplicate = false; + if (!_mac_vst_plugin_info->empty()) { + for (PluginInfoList::iterator i =_mac_vst_plugin_info->begin(); i != _mac_vst_plugin_info->end(); ++i) { + if ((info->type == (*i)->type)&&(info->unique_id == (*i)->unique_id)) { + warning << "Ignoring duplicate Mac VST plugin " << info->name << "\n"; + duplicate = true; + break; + } + } + } + + if (!duplicate) { + _mac_vst_plugin_info->push_back (info); + discovered++; + } + } + + vstfx_free_info_list (finfos); + return discovered > 0 ? 0 : -1; +} + +#endif // MAC_VST_SUPPORT + +#ifdef LXVST_SUPPORT + +void +PluginManager::lxvst_refresh (bool cache_only) +{ + if (_lxvst_plugin_info) { + _lxvst_plugin_info->clear (); + } else { + _lxvst_plugin_info = new ARDOUR::PluginInfoList(); + } + + lxvst_discover_from_path (Config->get_plugin_path_lxvst(), cache_only); } static bool lxvst_filter (const string& str, void *) @@ -775,11 +1140,10 @@ static bool lxvst_filter (const string& str, void *) } int -PluginManager::lxvst_discover_from_path (string path) +PluginManager::lxvst_discover_from_path (string path, bool cache_only) { - PathScanner scanner; - vector *plugin_objects; - vector::iterator x; + vector plugin_objects; + vector::iterator x; int ret = 0; #ifndef NDEBUG @@ -788,26 +1152,24 @@ PluginManager::lxvst_discover_from_path (string path) DEBUG_TRACE (DEBUG::PluginManager, string_compose ("Discovering linuxVST plugins along %1\n", path)); - plugin_objects = scanner (lxvst_path, lxvst_filter, 0, false, true); + find_files_matching_filter (plugin_objects, Config->get_plugin_path_lxvst(), lxvst_filter, 0, false, true, true); - if (plugin_objects) { - for (x = plugin_objects->begin(); x != plugin_objects->end (); ++x) { - ARDOUR::PluginScanMessage(_("LXVST"), **x); - lxvst_discover (**x); - } - - vector_delete (plugin_objects); + for (x = plugin_objects.begin(); x != plugin_objects.end (); ++x) { + ARDOUR::PluginScanMessage(_("LXVST"), *x, !cache_only && !cancelled()); + lxvst_discover (*x, cache_only || cancelled()); } return ret; } int -PluginManager::lxvst_discover (string path) +PluginManager::lxvst_discover (string path, bool cache_only) { DEBUG_TRACE (DEBUG::PluginManager, string_compose ("checking apparent LXVST plugin at %1\n", path)); - vector * finfos = vstfx_get_info_lx (const_cast (path.c_str())); + _cancel_timeout = false; + vector * finfos = vstfx_get_info_lx (const_cast (path.c_str()), + cache_only ? VST_SCAN_CACHE_ONLY : VST_SCAN_USE_APP); if (finfos->empty()) { DEBUG_TRACE (DEBUG::PluginManager, string_compose ("Cannot get Linux VST information from '%1'\n", path)); @@ -843,7 +1205,8 @@ PluginManager::lxvst_discover (string path) info->index = 0; info->n_inputs.set_audio (finfo->numInputs); info->n_outputs.set_audio (finfo->numOutputs); - info->n_inputs.set_midi (finfo->wantMidi ? 1 : 0); + info->n_inputs.set_midi ((finfo->wantMidi&1) ? 1 : 0); + info->n_outputs.set_midi ((finfo->wantMidi&2) ? 1 : 0); info->type = ARDOUR::LXVST; /* Make sure we don't find the same plugin in more than one place along @@ -893,14 +1256,8 @@ PluginManager::get_status (const PluginInfoPtr& pi) void PluginManager::save_statuses () { - ofstream ofs; std::string path = Glib::build_filename (user_config_directory(), "plugin_statuses"); - - ofs.open (path.c_str(), ios_base::openmode (ios::out|ios::trunc)); - - if (!ofs) { - return; - } + stringstream ofs; for (PluginStatusList::iterator i = statuses.begin(); i != statuses.end(); ++i) { switch ((*i).type) { @@ -919,6 +1276,12 @@ PluginManager::save_statuses () case LXVST: ofs << "LXVST"; break; + case MacVST: + ofs << "MacVST"; + break; + case Lua: + ofs << "Lua"; + break; } ofs << ' '; @@ -939,19 +1302,20 @@ PluginManager::save_statuses () ofs << (*i).unique_id;; ofs << endl; } - - ofs.close (); + g_file_set_contents (path.c_str(), ofs.str().c_str(), -1, NULL); + PluginStatusesChanged (); /* EMIT SIGNAL */ } void PluginManager::load_statuses () { std::string path = Glib::build_filename (user_config_directory(), "plugin_statuses"); - ifstream ifs (path.c_str()); - - if (!ifs) { + gchar *fbuf = NULL; + if (!g_file_get_contents (path.c_str(), &fbuf, NULL, NULL)) { return; } + stringstream ifs (fbuf); + g_free (fbuf); std::string stype; std::string sstatus; @@ -1004,6 +1368,8 @@ PluginManager::load_statuses () type = Windows_VST; } else if (stype == "LXVST") { type = LXVST; + } else if (stype == "Lua") { + type = Lua; } else { error << string_compose (_("unknown plugin type \"%1\" - ignored"), stype) << endmsg; @@ -1014,8 +1380,6 @@ PluginManager::load_statuses () strip_whitespace_edges (id); set_status (type, id, status); } - - ifs.close (); } void @@ -1044,12 +1408,22 @@ PluginManager::windows_vst_plugin_info () #endif } +ARDOUR::PluginInfoList& +PluginManager::mac_vst_plugin_info () +{ +#ifdef MACVST_SUPPORT + assert(_mac_vst_plugin_info); + return *_mac_vst_plugin_info; +#else + return _empty_plugin_info; +#endif +} + ARDOUR::PluginInfoList& PluginManager::lxvst_plugin_info () { #ifdef LXVST_SUPPORT - if (!_lxvst_plugin_info) - lxvst_refresh(); + assert(_lxvst_plugin_info); return *_lxvst_plugin_info; #else return _empty_plugin_info; @@ -1059,8 +1433,7 @@ PluginManager::lxvst_plugin_info () ARDOUR::PluginInfoList& PluginManager::ladspa_plugin_info () { - if (!_ladspa_plugin_info) - ladspa_refresh(); + assert(_ladspa_plugin_info); return *_ladspa_plugin_info; } @@ -1068,8 +1441,7 @@ ARDOUR::PluginInfoList& PluginManager::lv2_plugin_info () { #ifdef LV2_SUPPORT - if (!_lv2_plugin_info) - lv2_refresh(); + assert(_lv2_plugin_info); return *_lv2_plugin_info; #else return _empty_plugin_info; @@ -1080,10 +1452,16 @@ ARDOUR::PluginInfoList& PluginManager::au_plugin_info () { #ifdef AUDIOUNIT_SUPPORT - if (!_au_plugin_info) - au_refresh(); - return *_au_plugin_info; -#else - return _empty_plugin_info; + if (_au_plugin_info) { + return *_au_plugin_info; + } #endif + return _empty_plugin_info; +} + +ARDOUR::PluginInfoList& +PluginManager::lua_plugin_info () +{ + assert(_lua_plugin_info); + return *_lua_plugin_info; }