fix crash when copy'ing latent plugins
[ardour.git] / libs / ardour / plugin_manager.cc
index 6d68df688f4e152781c672cd1836cb4eb444d039..23515a2e2e67867556cc4e167501358408fab5db 100644 (file)
 #include <sys/types.h>
 #include <cstdio>
 #include <cstdlib>
-#include <fstream>
+
+#include <glib.h>
+#include "pbd/gstdio_compat.h"
 
 #ifdef HAVE_LRDF
 #include <lrdf.h>
 #endif
 
 #ifdef WINDOWS_VST_SUPPORT
+#include "ardour/vst_info_file.h"
 #include "fst.h"
 #include "pbd/basename.h"
 #include <cstring>
+
+// dll-info
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+
 #endif // WINDOWS_VST_SUPPORT
 
 #ifdef LXVST_SUPPORT
+#include "ardour/vst_info_file.h"
 #include "ardour/linux_vst_support.h"
 #include "pbd/basename.h"
 #include <cstring>
@@ -46,8 +57,9 @@
 
 #include <glibmm/miscutils.h>
 #include <glibmm/pattern.h>
+#include <glibmm/fileutils.h>
+#include <glibmm/miscutils.h>
 
-#include "pbd/pathscanner.h"
 #include "pbd/whitespace.h"
 #include "pbd/file_utils.h"
 
 #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"
@@ -81,7 +95,7 @@
 #include "pbd/error.h"
 #include "pbd/stl_delete.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 #include "ardour/debug.h"
 
@@ -90,9 +104,10 @@ using namespace PBD;
 using namespace std;
 
 PluginManager* PluginManager::_instance = 0;
+std::string PluginManager::scanner_bin_path = "";
 
 PluginManager&
-PluginManager::instance() 
+PluginManager::instance()
 {
        if (!_instance) {
                _instance = new PluginManager;
@@ -106,10 +121,44 @@ PluginManager::PluginManager ()
        , _ladspa_plugin_info(0)
        , _lv2_plugin_info(0)
        , _au_plugin_info(0)
+       , _lua_plugin_info(0)
+       , _cancel_scan(false)
+       , _cancel_timeout(false)
 {
        char* s;
        string lrdf_path;
 
+#if defined WINDOWS_VST_SUPPORT || defined LXVST_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"))){
@@ -140,62 +189,300 @@ PluginManager::PluginManager ()
                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 _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");
+       _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
 
+#if (defined WINDOWS_VST_SUPPORT || defined LXVST_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"), "", false);
+       _cancel_scan = false;
+}
+
+void
+PluginManager::cancel_plugin_scan ()
+{
+       _cancel_scan = true;
+}
+
+void
+PluginManager::cancel_plugin_timeout ()
+{
+       _cancel_timeout = true;
+}
+
+void
+PluginManager::clear_vst_cache ()
+{
+#if 1 // clean old cache and error files. (remove this code after 4.3 or 5.0)
+#ifdef WINDOWS_VST_SUPPORT
+       {
+               vector<string> fsi_files;
+               find_files_matching_regex (fsi_files, Config->get_plugin_path_vst(), "\\" VST_EXT_INFOFILE "$", true);
+               for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+                       ::g_unlink(i->c_str());
+               }
+       }
+       {
+               vector<string> fsi_files;
+               find_files_matching_regex (fsi_files, Config->get_plugin_path_vst(), "\\.fsi$", true);
+               for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+                       ::g_unlink(i->c_str());
+               }
+       }
+       {
+               vector<string> fsi_files;
+               find_files_matching_regex (fsi_files, Config->get_plugin_path_vst(), "\\.err$", true);
+               for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+                       ::g_unlink(i->c_str());
+               }
+       }
+#endif
+
+#ifdef LXVST_SUPPORT
+       {
+               vector<string> fsi_files;
+               find_files_matching_regex (fsi_files, Config->get_plugin_path_lxvst(), "\\" VST_EXT_INFOFILE "$", true);
+               for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+                       ::g_unlink(i->c_str());
+               }
+       }
+       {
+               vector<string> fsi_files;
+               find_files_matching_regex (fsi_files, Config->get_plugin_path_lxvst(), "\\.fsi$", true);
+               for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+                       ::g_unlink(i->c_str());
+               }
+       }
+       {
+               vector<string> fsi_files;
+               find_files_matching_regex (fsi_files, Config->get_plugin_path_lxvst(), "\\.err$", true);
+               for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+                       ::g_unlink(i->c_str());
+               }
+       }
+#endif
+#if (defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT)
+       {
+               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)
+       {
+               string dn = Glib::build_filename (ARDOUR::user_cache_directory(), "vst");
+               vector<string> fsi_files;
+               find_files_matching_regex (fsi_files, dn, "\\" VST_EXT_INFOFILE "$", /* user cache is flat, no recursion */ false);
+               for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+                       ::g_unlink(i->c_str());
+               }
+       }
+#endif
+}
+
+void
+PluginManager::clear_vst_blacklist ()
+{
+#if 1 // remove old blacklist files. (remove this code after 4.3 or 5.0)
+
+#ifdef WINDOWS_VST_SUPPORT
+       {
+               vector<string> fsi_files;
+               find_files_matching_regex (fsi_files, Config->get_plugin_path_vst(), "\\" VST_EXT_BLACKLIST "$", true);
+               for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+                       ::g_unlink(i->c_str());
+               }
+       }
+#endif
+
+#ifdef LXVST_SUPPORT
+       {
+               vector<string> fsi_files;
+               find_files_matching_regex (fsi_files, Config->get_plugin_path_lxvst(), "\\" VST_EXT_BLACKLIST "$", true);
+               for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+                       ::g_unlink(i->c_str());
+               }
+       }
+#endif
+#if (defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT)
+       {
+               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);
+               }
+       }
+#endif
+
+#endif // old blacklist cleanup
+
+#if (defined WINDOWS_VST_SUPPORT || defined LXVST_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 */
 }
 
@@ -213,29 +500,22 @@ PluginManager::ladspa_refresh ()
        /* Only add standard locations to ladspa_path if it doesn't
         * already contain them. Check for trailing G_DIR_SEPARATOR too.
         */
-       
+
        vector<string> ladspa_modules;
 
        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<std::string>::iterator i = ladspa_modules.begin(); i != ladspa_modules.end(); ++i) {
+               ARDOUR::PluginScanMessage(_("LADSPA"), *i, false);
                ladspa_discover (*i);
        }
 }
 
+#ifdef HAVE_LRDF
 static bool rdf_filter (const string &str, void* /*arg*/)
 {
        return str[0] != '.' &&
@@ -244,6 +524,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()
@@ -267,9 +548,8 @@ void
 PluginManager::add_presets(string domain)
 {
 #ifdef HAVE_LRDF
-       PathScanner scanner;
-       vector<string *> *presets;
-       vector<string *>::iterator x;
+       vector<string> presets;
+       vector<string>::iterator x;
 
        char* envvar;
        if ((envvar = getenv ("HOME")) == 0) {
@@ -277,18 +557,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
 }
 
@@ -296,22 +573,17 @@ void
 PluginManager::add_lrdf_data (const string &path)
 {
 #ifdef HAVE_LRDF
-       PathScanner scanner;
-       vector<string *>* rdf_files;
-       vector<string *>::iterator x;
+       vector<string> rdf_files;
+       vector<string>::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
 }
@@ -344,6 +616,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;
                }
@@ -484,11 +761,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
@@ -496,7 +783,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 ();
@@ -504,97 +791,190 @@ 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);
+       windows_vst_discover_from_path (Config->get_plugin_path_vst(), cache_only);
 }
 
-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;
-}
-
-static bool windows_vst_filter (const string& str, void *arg)
+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<string *> *plugin_objects;
-       vector<string *>::iterator x;
+       vector<string> plugin_objects;
+       vector<string>::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));
 
-       plugin_objects = scanner (windows_vst_path, windows_vst_filter, 0, false, true);
+       if (Config->get_verbose_plugin_scan()) {
+               info << string_compose (_("--- Windows VST plugins Scan: %1"), path) << endmsg;
+       }
 
-       if (plugin_objects) {
-               for (x = plugin_objects->begin(); x != plugin_objects->end (); ++x) {
-                       windows_vst_discover (**x);
-               }
+       find_files_matching_filter (plugin_objects, Config->get_plugin_path_vst(), windows_vst_filter, 0, false, true, true);
+
+       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)
 {
-       VSTInfo* finfo;
-       char buf[32];
+       DEBUG_TRACE (DEBUG::PluginManager, string_compose ("windows_vst_discover '%1'\n", path));
 
-       if ((finfo = fst_get_info (const_cast<char *> (path.c_str()))) == 0) {
-               warning << "Cannot get Windows VST information from " << path << endmsg;
-               return -1;
+       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;
+               }
        }
 
-       if (!finfo->canProcessReplacing) {
-               warning << string_compose (_("VST plugin %1 does not support processReplacing, and so cannot be used in %2 at this time"),
-                                          finfo->name, PROGRAM_NAME)
-                       << endl;
+       _cancel_timeout = false;
+       vector<VSTInfo*> * finfos = vstfx_get_info_fst (const_cast<char *> (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;
        }
 
-       PluginInfoPtr info (new WindowsVSTPluginInfo);
+       uint32_t discovered = 0;
+       for (vector<VSTInfo *>::iterator x = finfos->begin(); x != finfos->end(); ++x) {
+               VSTInfo* finfo = *x;
+               char buf[32];
 
-       /* what a joke freeware VST is */
+               if (!finfo->canProcessReplacing) {
+                       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;
+               }
 
-       if (!strcasecmp ("The Unnamed plugin", finfo->name)) {
-               info->name = PBD::basename_nosuffix (path);
-       } else {
-               info->name = finfo->name;
-       }
+               PluginInfoPtr info (new WindowsVSTPluginInfo);
 
+               /* what a joke freeware VST is */
 
-       snprintf (buf, sizeof (buf), "%d", finfo->UniqueID);
-       info->unique_id = buf;
-       info->category = "VST";
-       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 : 0);
-       info->type = ARDOUR::Windows_VST;
+               if (!strcasecmp ("The Unnamed plugin", finfo->name)) {
+                       info->name = PBD::basename_nosuffix (path);
+               } else {
+                       info->name = finfo->name;
+               }
 
-       _windows_vst_plugin_info->push_back (info);
-       fst_free_info (finfo);
 
-       return 0;
+               snprintf (buf, sizeof (buf), "%d", finfo->UniqueID);
+               info->unique_id = buf;
+               info->category = "VST";
+               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::Windows_VST;
+
+               // TODO: check dup-IDs (lxvst AND windows vst)
+               bool duplicate = false;
+
+               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 << string_compose (_("Ignoring duplicate Windows VST plugin \"%1\""), info->name) << endmsg;
+                                       duplicate = true;
+                                       break;
+                               }
+                       }
+               }
+
+               if (!duplicate) {
+                       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;
+                       }
+               }
+       }
+
+       vstfx_free_info_list (finfos);
+       return discovered > 0 ? 0 : -1;
 }
 
 #endif // WINDOWS_VST_SUPPORT
@@ -602,7 +982,7 @@ PluginManager::windows_vst_discover (string path)
 #ifdef LXVST_SUPPORT
 
 void
-PluginManager::lxvst_refresh ()
+PluginManager::lxvst_refresh (bool cache_only)
 {
        if (_lxvst_plugin_info) {
                _lxvst_plugin_info->clear ();
@@ -610,24 +990,7 @@ PluginManager::lxvst_refresh ()
                _lxvst_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";
-       }
-
-       lxvst_discover_from_path (lxvst_path);
-}
-
-int
-PluginManager::add_lxvst_directory (string path)
-{
-       if (lxvst_discover_from_path (path) == 0) {
-               lxvst_path += ':';
-               lxvst_path += path;
-               return 0;
-       }
-       return -1;
+       lxvst_discover_from_path (Config->get_plugin_path_lxvst(), cache_only);
 }
 
 static bool lxvst_filter (const string& str, void *)
@@ -638,11 +1001,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<string *> *plugin_objects;
-       vector<string *>::iterator x;
+       vector<string> plugin_objects;
+       vector<string>::iterator x;
        int ret = 0;
 
 #ifndef NDEBUG
@@ -651,78 +1013,90 @@ 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);
-
-       if (plugin_objects) {
-               for (x = plugin_objects->begin(); x != plugin_objects->end (); ++x) {
-                       lxvst_discover (**x);
-               }
+       find_files_matching_filter (plugin_objects, Config->get_plugin_path_lxvst(), lxvst_filter, 0, false, true, true);
 
-               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)
 {
-       VSTInfo* finfo;
-       char buf[32];
-
        DEBUG_TRACE (DEBUG::PluginManager, string_compose ("checking apparent LXVST plugin at %1\n", path));
 
-       if ((finfo = vstfx_get_info (const_cast<char *> (path.c_str()))) == 0) {
+       _cancel_timeout = false;
+       vector<VSTInfo*> * finfos = vstfx_get_info_lx (const_cast<char *> (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));
                return -1;
        }
 
-       if (!finfo->canProcessReplacing) {
-               warning << string_compose (_("linuxVST plugin %1 does not support processReplacing, and so cannot be used in %2 at this time"),
-                                          finfo->name, PROGRAM_NAME)
-                       << endl;
-       }
+       uint32_t discovered = 0;
+       for (vector<VSTInfo *>::iterator x = finfos->begin(); x != finfos->end(); ++x) {
+               VSTInfo* finfo = *x;
+               char buf[32];
 
-       PluginInfoPtr info(new LXVSTPluginInfo);
+               if (!finfo->canProcessReplacing) {
+                       warning << string_compose (_("linuxVST plugin %1 does not support processReplacing, and so cannot be used in %2 at this time"),
+                                                        finfo->name, PROGRAM_NAME)
+                               << endl;
+                       continue;
+               }
 
-       if (!strcasecmp ("The Unnamed plugin", finfo->name)) {
-               info->name = PBD::basename_nosuffix (path);
-       } else {
-               info->name = finfo->name;
-       }
-
-       
-       snprintf (buf, sizeof (buf), "%d", finfo->UniqueID);
-       info->unique_id = buf;
-       info->category = "linuxVSTs";
-       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 : 0);
-       info->type = ARDOUR::LXVST;
-
-        /* Make sure we don't find the same plugin in more than one place along
-          the LXVST_PATH We can't use a simple 'find' because the path is included
-          in the PluginInfo, and that is the one thing we can be sure MUST be
-          different if a duplicate instance is found.  So we just compare the type
-          and unique ID (which for some VSTs isn't actually unique...)
-       */
-       
-       if (!_lxvst_plugin_info->empty()) {
-               for (PluginInfoList::iterator i =_lxvst_plugin_info->begin(); i != _lxvst_plugin_info->end(); ++i) {
-                       if ((info->type == (*i)->type)&&(info->unique_id == (*i)->unique_id)) {
-                               warning << "Ignoring duplicate Linux VST plugin " << info->name << "\n";
-                               vstfx_free_info(finfo);
-                               return 0;
+               PluginInfoPtr info(new LXVSTPluginInfo);
+
+               if (!strcasecmp ("The Unnamed plugin", finfo->name)) {
+                       info->name = PBD::basename_nosuffix (path);
+               } else {
+                       info->name = finfo->name;
+               }
+
+
+               snprintf (buf, sizeof (buf), "%d", finfo->UniqueID);
+               info->unique_id = buf;
+               info->category = "linuxVSTs";
+               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::LXVST;
+
+                                       /* Make sure we don't find the same plugin in more than one place along
+                        the LXVST_PATH We can't use a simple 'find' because the path is included
+                        in the PluginInfo, and that is the one thing we can be sure MUST be
+                        different if a duplicate instance is found.  So we just compare the type
+                        and unique ID (which for some VSTs isn't actually unique...)
+               */
+
+               // TODO: check dup-IDs with windowsVST, too
+               bool duplicate = false;
+               if (!_lxvst_plugin_info->empty()) {
+                       for (PluginInfoList::iterator i =_lxvst_plugin_info->begin(); i != _lxvst_plugin_info->end(); ++i) {
+                               if ((info->type == (*i)->type)&&(info->unique_id == (*i)->unique_id)) {
+                                       warning << "Ignoring duplicate Linux VST plugin " << info->name << "\n";
+                                       duplicate = true;
+                                       break;
+                               }
                        }
                }
+
+               if (!duplicate) {
+                       _lxvst_plugin_info->push_back (info);
+                       discovered++;
+               }
        }
-       
-       _lxvst_plugin_info->push_back (info);
-       vstfx_free_info (finfo);
 
-       return 0;
+       vstfx_free_info_list (finfos);
+       return discovered > 0 ? 0 : -1;
 }
 
 #endif // LXVST_SUPPORT
@@ -743,14 +1117,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) {
@@ -769,6 +1137,9 @@ PluginManager::save_statuses ()
                case LXVST:
                        ofs << "LXVST";
                        break;
+               case Lua:
+                       ofs << "Lua";
+                       break;
                }
 
                ofs << ' ';
@@ -789,19 +1160,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;
@@ -854,6 +1226,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;
@@ -864,8 +1238,6 @@ PluginManager::load_statuses ()
                strip_whitespace_edges (id);
                set_status (type, id, status);
        }
-
-       ifs.close ();
 }
 
 void
@@ -898,8 +1270,7 @@ 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;
@@ -909,8 +1280,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;
 }
 
@@ -918,8 +1288,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;
@@ -930,10 +1299,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;
 }