X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fplugin_manager.cc;h=233f986a7bbc0fb79bdf6b17bda67aa4bbe1259a;hb=a5e8a69dec467cc7d938cc3d92acb218b04486a9;hp=04652a36c77e32a6b6ec5e19efe437a94809ef5c;hpb=1ed63c52472a0f32406b009a04c8db2dc59f76d2;p=ardour.git diff --git a/libs/ardour/plugin_manager.cc b/libs/ardour/plugin_manager.cc index 04652a36c7..233f986a7b 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,19 +55,32 @@ #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 "pbd/pathexpand.h" +#include +#endif //MACVST_SUPPORT + #include #include #include #include -#include "pbd/whitespace.h" +#include "pbd/convert.h" #include "pbd/file_utils.h" +#include "pbd/tokenizer.h" +#include "pbd/whitespace.h" +#include "ardour/directory_names.h" #include "ardour/debug.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" @@ -85,7 +107,7 @@ #include "pbd/error.h" #include "pbd/stl_delete.h" -#include "i18n.h" +#include "pbd/i18n.h" #include "ardour/debug.h" @@ -108,22 +130,24 @@ 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) + , _lua_plugin_info(0) , _cancel_scan(false) , _cancel_timeout(false) { char* s; string lrdf_path; -#if defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT +#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(g_win32_get_package_installation_directory_of_module (0), "bin"); + 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(); @@ -150,6 +174,8 @@ PluginManager::PluginManager () load_statuses (); + load_tags (); + if ((s = getenv ("LADSPA_RDF_PATH"))){ lrdf_path = s; } @@ -172,6 +198,12 @@ 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"))) { @@ -207,6 +239,8 @@ PluginManager::PluginManager () } BootMessage (_("Discovering Plugins")); + + LuaScripting::instance().scripts_changed.connect_same_thread (lua_refresh_connection, boost::bind (&PluginManager::lua_refresh_cb, this)); } @@ -216,9 +250,11 @@ PluginManager::~PluginManager() // 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; } } @@ -236,6 +272,8 @@ PluginManager::refresh (bool cache_only) BootMessage (_("Scanning LADSPA Plugins")); ladspa_refresh (); + BootMessage (_("Scanning Lua DSP Processors")); + lua_refresh (); #ifdef LV2_SUPPORT BootMessage (_("Scanning LV2 Plugins")); lv2_refresh (); @@ -262,6 +300,38 @@ PluginManager::refresh (bool 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); + } else if (_mac_vst_plugin_info) { + _mac_vst_plugin_info->clear (); + } else { + _mac_vst_plugin_info = new ARDOUR::PluginInfoList(); + } +#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 if (cache_only) { BootMessage (_("Scanning AU Plugins")); @@ -292,8 +362,15 @@ PluginManager::cancel_plugin_timeout () 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 + { + 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); @@ -301,9 +378,23 @@ PluginManager::clear_vst_cache () ::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()); + } + } #endif #ifdef LXVST_SUPPORT + { + 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); @@ -311,13 +402,29 @@ PluginManager::clear_vst_cache () ::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()); + } + } #endif - #if (defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT) { - string personal = get_personal_vst_info_cache_dir(); + 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, personal, "\\.fsi$", /* user cache is flat, no recursion */ false); + 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()); } @@ -328,6 +435,8 @@ 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 { vector fsi_files; @@ -347,29 +456,33 @@ PluginManager::clear_vst_blacklist () } } #endif - #if (defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT) { - string personal = get_personal_vst_blacklist_dir(); + 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 - vector fsi_files; - find_files_matching_regex (fsi_files, personal, "\\" VST_EXT_BLACKLIST "$", /* flat user cache */ false); - for (vector::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) { - ::g_unlink(i->c_str()); +#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::au_cache_path () - string fn = Glib::build_filename (ARDOUR::user_config_directory(), "au_cache"); - if (Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) { - ::g_unlink(fn.c_str()); - } + AUPluginInfo::clear_cache (); #endif } @@ -384,6 +497,33 @@ PluginManager::clear_au_blacklist () #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); + set_tags (lpi->type, lpi->unique_id, lpi->category, lpi->name, FromPlug); + } +} + +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 PluginManager::ladspa_refresh () { @@ -436,6 +576,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() { @@ -514,6 +660,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; } @@ -527,23 +678,40 @@ PluginManager::ladspa_discover (string path) PluginInfoPtr info(new LadspaPluginInfo); info->name = descriptor->Name; info->category = get_ladspa_category(descriptor->UniqueID); - info->creator = descriptor->Maker; info->path = path; info->index = i; info->n_inputs = ChanCount(); info->n_outputs = ChanCount(); info->type = ARDOUR::LADSPA; + string::size_type pos = 0; + string creator = descriptor->Maker; + /* stupid LADSPA creator strings */ +#ifdef PLATFORM_WINDOWS + while (pos < creator.length() && creator[pos] > -2 && creator[pos] < 256 && (isalnum (creator[pos]) || isspace (creator[pos]) || creator[pos] == '.')) ++pos; +#else + while (pos < creator.length() && (isalnum (creator[pos]) || isspace (creator[pos]) || creator[pos] == '.')) ++pos; +#endif + + /* If there were too few characters to create a + * meaningful name, mark this creator as 'Unknown' + */ + if (creator.length() < 2 || pos < 3) { + info->creator = "Unknown"; + } else{ + info->creator = creator.substr (0, pos); + } + char buf[32]; snprintf (buf, sizeof (buf), "%lu", descriptor->UniqueID); info->unique_id = buf; for (uint32_t n=0; n < descriptor->PortCount; ++n) { - if ( LADSPA_IS_PORT_AUDIO (descriptor->PortDescriptors[n]) ) { - if ( LADSPA_IS_PORT_INPUT (descriptor->PortDescriptors[n]) ) { + if (LADSPA_IS_PORT_AUDIO (descriptor->PortDescriptors[n])) { + if (LADSPA_IS_PORT_INPUT (descriptor->PortDescriptors[n])) { info->n_inputs.set_audio(info->n_inputs.n_audio() + 1); } - else if ( LADSPA_IS_PORT_OUTPUT (descriptor->PortDescriptors[n]) ) { + else if (LADSPA_IS_PORT_OUTPUT (descriptor->PortDescriptors[n])) { info->n_outputs.set_audio(info->n_outputs.n_audio() + 1); } } @@ -565,6 +733,7 @@ PluginManager::ladspa_discover (string path) if(!found){ _ladspa_plugin_info->push_back (info); + set_tags (info->type, info->unique_id, info->category, info->name, FromPlug); } DEBUG_TRACE (DEBUG::PluginManager, string_compose ("Found LADSPA plugin, name: %1, Inputs: %2, Outputs: %3\n", info->name, info->n_inputs, info->n_outputs)); @@ -649,6 +818,10 @@ PluginManager::lv2_refresh () DEBUG_TRACE (DEBUG::PluginManager, "LV2: refresh\n"); delete _lv2_plugin_info; _lv2_plugin_info = LV2PluginInfo::discover(); + + for (PluginInfoList::iterator i = _lv2_plugin_info->begin(); i != _lv2_plugin_info->end(); ++i) { + set_tags ((*i)->type, (*i)->unique_id, (*i)->category, (*i)->name, FromPlug); + } } #endif @@ -669,6 +842,10 @@ PluginManager::au_refresh (bool cache_only) // successful scan re-enabled automatic discovery if it was set Config->set_discover_audio_units (discover_at_start); Config->save_state(); + + for (PluginInfoList::iterator i = _au_plugin_info->begin(); i != _au_plugin_info->end(); ++i) { + set_tags ((*i)->type, (*i)->unique_id, (*i)->category, (*i)->name, FromPlug); + } } #endif @@ -702,11 +879,16 @@ PluginManager::windows_vst_discover_from_path (string path, bool cache_only) DEBUG_TRACE (DEBUG::PluginManager, string_compose ("Discovering Windows VST plugins along %1\n", path)); + if (Session::get_disable_all_loaded_plugins ()) { + info << _("Disabled WindowsVST scan (safe mode)") << endmsg; + return -1; + } + if (Config->get_verbose_plugin_scan()) { info << string_compose (_("--- Windows VST plugins Scan: %1"), path) << endmsg; } - find_files_matching_filter (plugin_objects, Config->get_plugin_path_vst(), windows_vst_filter, 0, false, true, true); + find_files_matching_filter (plugin_objects, path, windows_vst_filter, 0, false, true, true); for (x = plugin_objects.begin(); x != plugin_objects.end (); ++x) { ARDOUR::PluginScanMessage(_("VST"), *x, !cache_only && !cancelled()); @@ -720,13 +902,77 @@ PluginManager::windows_vst_discover_from_path (string path, bool cache_only) 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, bool cache_only) { DEBUG_TRACE (DEBUG::PluginManager, string_compose ("windows_vst_discover '%1'\n", path)); if (Config->get_verbose_plugin_scan()) { - info << string_compose (_(" * %1 %2"), path, (cache_only ? _(" (cache only)") : "")) << endmsg; + 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; @@ -769,7 +1015,7 @@ PluginManager::windows_vst_discover (string path, bool cache_only) snprintf (buf, sizeof (buf), "%d", finfo->UniqueID); info->unique_id = buf; - info->category = "VST"; + info->category = finfo->Category; info->path = path; info->creator = finfo->creator; info->index = 0; @@ -779,6 +1025,9 @@ PluginManager::windows_vst_discover (string path, bool cache_only) info->n_outputs.set_midi ((finfo->wantMidi&2) ? 1 : 0); info->type = ARDOUR::Windows_VST; + /* if we don't have any tags for this plugin, make some up. */ + set_tags (info->type, info->unique_id, info->category, info->name, FromPlug); + // TODO: check dup-IDs (lxvst AND windows vst) bool duplicate = false; @@ -797,7 +1046,7 @@ PluginManager::windows_vst_discover (string path, bool cache_only) _windows_vst_plugin_info->push_back (info); discovered++; if (Config->get_verbose_plugin_scan()) { - PBD::info << string_compose (_(" -> OK. (VST Plugin \"%1\" added)."), info->name) << endmsg; + PBD::info << string_compose (_(" -> OK (VST Plugin \"%1\" was added)."), info->name) << endmsg; } } } @@ -808,6 +1057,140 @@ PluginManager::windows_vst_discover (string path, bool cache_only) #endif // WINDOWS_VST_SUPPORT +#ifdef MACVST_SUPPORT +void +PluginManager::mac_vst_refresh (bool cache_only) +{ + if (_mac_vst_plugin_info) { + _mac_vst_plugin_info->clear (); + } else { + _mac_vst_plugin_info = new ARDOUR::PluginInfoList(); + } + + 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) +{ + 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)); +} + +int +PluginManager::mac_vst_discover_from_path (string path, bool cache_only) +{ + if (Session::get_disable_all_loaded_plugins ()) { + info << _("Disabled MacVST scan (safe mode)") << endmsg; + return -1; + } + + Searchpath paths (path); + /* customized version of run_functor_for_paths() */ + for (vector::const_iterator i = paths.begin(); i != paths.end(); ++i) { + string expanded_path = path_expand (*i); + if (!Glib::file_test (expanded_path, Glib::FILE_TEST_IS_DIR)) continue; + try { + Glib::Dir dir(expanded_path); + for (Glib::DirIterator di = dir.begin(); di != dir.end(); di++) { + string fullpath = Glib::build_filename (expanded_path, *di); + + /* we're only interested in bundles */ + if (!Glib::file_test (fullpath, Glib::FILE_TEST_IS_DIR)) { + continue; + } + + if (mac_vst_filter (fullpath)) { + ARDOUR::PluginScanMessage(_("MacVST"), fullpath, !cache_only && !cancelled()); + mac_vst_discover (fullpath, cache_only || cancelled()); + continue; + } + + /* don't descend into AU bundles in the VST dir */ + if (fullpath[0] == '.' || (fullpath.length() > 10 && strings_equal_ignore_case (".component", fullpath.substr(fullpath.length() - 10)))) { + continue; + } + + /* recurse */ + mac_vst_discover_from_path (fullpath, cache_only); + } + } catch (Glib::FileError& err) { } + } + + return 0; +} + +int +PluginManager::mac_vst_discover (string path, bool cache_only) +{ + 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; + } + + 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 = finfo->Category; + 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; + + /* if we don't have any tags for this plugin, make some up. */ + set_tags (info->type, info->unique_id, info->category, info->name, FromPlug); + + 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 @@ -836,6 +1219,11 @@ PluginManager::lxvst_discover_from_path (string path, bool cache_only) vector::iterator x; int ret = 0; + if (Session::get_disable_all_loaded_plugins ()) { + info << _("Disabled LinuxVST scan (safe mode)") << endmsg; + return -1; + } + #ifndef NDEBUG (void) path; #endif @@ -889,7 +1277,7 @@ PluginManager::lxvst_discover (string path, bool cache_only) snprintf (buf, sizeof (buf), "%d", finfo->UniqueID); info->unique_id = buf; - info->category = "linuxVSTs"; + info->category = finfo->Category; info->path = path; info->creator = finfo->creator; info->index = 0; @@ -899,12 +1287,14 @@ PluginManager::lxvst_discover (string path, bool cache_only) 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...) - */ + set_tags (info->type, info->unique_id, info->category, info->name, FromPlug); + + /* 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; @@ -932,11 +1322,11 @@ PluginManager::lxvst_discover (string path, bool cache_only) PluginManager::PluginStatusType -PluginManager::get_status (const PluginInfoPtr& pi) +PluginManager::get_status (const PluginInfoPtr& pi) const { PluginStatus ps (pi->type, pi->unique_id); PluginStatusList::const_iterator i = find (statuses.begin(), statuses.end(), ps); - if (i == statuses.end() ) { + if (i == statuses.end()) { return Normal; } else { return i->status; @@ -946,14 +1336,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; - } + std::string path = Glib::build_filename (user_plugin_metadata_dir(), "plugin_statuses"); + stringstream ofs; for (PluginStatusList::iterator i = statuses.begin(); i != statuses.end(); ++i) { switch ((*i).type) { @@ -972,6 +1356,12 @@ PluginManager::save_statuses () case LXVST: ofs << "LXVST"; break; + case MacVST: + ofs << "MacVST"; + break; + case Lua: + ofs << "Lua"; + break; } ofs << ' '; @@ -989,22 +1379,24 @@ PluginManager::save_statuses () } ofs << ' '; + ofs << (*i).unique_id;; ofs << endl; } - - ofs.close (); + g_file_set_contents (path.c_str(), ofs.str().c_str(), -1, NULL); } void PluginManager::load_statuses () { - std::string path = Glib::build_filename (user_config_directory(), "plugin_statuses"); - ifstream ifs (path.c_str()); - - if (!ifs) { + std::string path; + find_file (plugin_metadata_search_path(), "plugin_statuses", path); //note: if no user folder is found, this will find the resources path + 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; @@ -1057,6 +1449,10 @@ PluginManager::load_statuses () type = Windows_VST; } else if (stype == "LXVST") { type = LXVST; + } else if (stype == "MacVST") { + type = MacVST; + } else if (stype == "Lua") { + type = Lua; } else { error << string_compose (_("unknown plugin type \"%1\" - ignored"), stype) << endmsg; @@ -1067,8 +1463,6 @@ PluginManager::load_statuses () strip_whitespace_edges (id); set_status (type, id, status); } - - ifs.close (); } void @@ -1077,14 +1471,255 @@ PluginManager::set_status (PluginType t, string id, PluginStatusType status) PluginStatus ps (t, id, status); statuses.erase (ps); - if (status == Normal) { - return; + if (status != Normal) { + statuses.insert (ps); } - statuses.insert (ps); + PluginStatusChanged (t, id, status); /* EMIT SIGNAL */ +} + +PluginType +PluginManager::to_generic_vst (const PluginType t) +{ + switch (t) { + case Windows_VST: + case LXVST: + case MacVST: + return LXVST; + default: + break; + } + return t; +} + +struct SortByTag { + bool operator() (std::string a, std::string b) { + return a.compare (b) < 0; + } +}; + +vector +PluginManager::get_tags (const PluginInfoPtr& pi) const +{ + vector tags; + + PluginTag ps (to_generic_vst(pi->type), pi->unique_id, "", "", FromPlug); + PluginTagList::const_iterator i = find (ptags.begin(), ptags.end(), ps); + if (i != ptags.end ()) { + PBD::tokenize (i->tags, string(" "), std::back_inserter (tags), true); + SortByTag sorter; + sort (tags.begin(), tags.end(), sorter); + } + return tags; +} + +std::string +PluginManager::get_tags_as_string (PluginInfoPtr const& pi) const +{ + std::string ret; + + vector tags = get_tags(pi); + for (vector::iterator t = tags.begin(); t != tags.end(); ++t) { + if (t != tags.begin ()) { + ret.append(" "); + } + ret.append(*t); + } + + return ret; +} + +std::string +PluginManager::user_plugin_metadata_dir () const +{ + std::string dir = Glib::build_filename (user_config_directory(), plugin_metadata_dir_name); + g_mkdir_with_parents (dir.c_str(), 0744); + return dir; } -ARDOUR::PluginInfoList& +void +PluginManager::save_tags () +{ + std::string path = Glib::build_filename (user_plugin_metadata_dir(), "plugin_tags"); + XMLNode* root = new XMLNode (X_("PluginTags")); + + for (PluginTagList::iterator i = ptags.begin(); i != ptags.end(); ++i) { + if ( (*i).tagtype == FromFactoryFile || (*i).tagtype == FromUserFile ) { + /* user file should contain only plugins that are (a) newly user-tagged or (b) previously unknown */ + continue; + } + XMLNode* node = new XMLNode (X_("Plugin")); + node->set_property (X_("type"), to_generic_vst ((*i).type)); + node->set_property (X_("id"), (*i).unique_id); + node->set_property (X_("tags"), (*i).tags); + node->set_property (X_("name"), (*i).name); + if ( (*i).tagtype >= FromUserFile ) { + node->set_property (X_("user-set"), "1"); + } + root->add_child_nocopy (*node); + } + + XMLTree tree; + tree.set_root (root); + if (!tree.write (path)) { + error << string_compose (_("Could not save Plugin Tags info to %1"), path) << endmsg; + } +} + +void +PluginManager::load_tags () +{ + vector tmp; + find_files_matching_pattern (tmp, plugin_metadata_search_path (), "plugin_tags"); + + for (vector::const_reverse_iterator p = tmp.rbegin (); + p != (vector::const_reverse_iterator)tmp.rend(); ++p) { + std::string path = *p; + info << string_compose (_("Loading plugin meta data file %1"), path) << endmsg; + if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) { + return; + } + + XMLTree tree; + if (!tree.read (path)) { + error << string_compose (_("Cannot parse plugin tag info from %1"), path) << endmsg; + return; + } + + for (XMLNodeConstIterator i = tree.root()->children().begin(); i != tree.root()->children().end(); ++i) { + PluginType type; + string id; + string tags; + string name; + bool user_set; + if (!(*i)->get_property (X_("type"), type) || + !(*i)->get_property (X_("id"), id) || + !(*i)->get_property (X_("tags"), tags) || + !(*i)->get_property (X_("name"), name)) { + } + if (!(*i)->get_property (X_("user-set"), user_set)) { + user_set = false; + } + strip_whitespace_edges (tags); + set_tags (type, id, tags, name, user_set ? FromUserFile : FromFactoryFile ); + } + } +} + +void +PluginManager::set_tags (PluginType t, string id, string tag, std::string name, TagType ttype ) +{ + string sanitized = sanitize_tag (tag); + + PluginTag ps (to_generic_vst (t), id, sanitized, name, ttype ); + PluginTagList::const_iterator i = find (ptags.begin(), ptags.end(), ps); + if (i == ptags.end()) { + ptags.insert (ps); + } else if ( (uint32_t) ttype >= (uint32_t) (*i).tagtype ) { // only overwrite if we are more important than the existing. Gui > UserFile > FactoryFile > Plugin + ptags.erase (ps); + ptags.insert (ps); + } + if ( ttype == FromGui ) { + PluginTagChanged (t, id, sanitized); /* EMIT SIGNAL */ + } +} + +void +PluginManager::reset_tags (PluginInfoPtr const& pi) +{ + PluginTag ps (pi->type, pi->unique_id, pi->category, pi->name, FromPlug); + + PluginTagList::const_iterator i = find (ptags.begin(), ptags.end(), ps); + if (i != ptags.end()) { + ptags.erase (ps); + ptags.insert (ps); + } +} + +std::string +PluginManager::sanitize_tag (const std::string to_sanitize) const +{ + if (to_sanitize.empty ()) { + return ""; + } + string sanitized = to_sanitize; + vector tags; + if (!PBD::tokenize (sanitized, string(" ,\n"), std::back_inserter (tags), true)) { +#ifndef NDEBUG + cerr << _("PluginManager::sanitize_tag could not tokenize string: ") << sanitized << endmsg; +#endif + return ""; + } + + /* convert tokens to lower-case, space-separated list */ + sanitized = ""; + for (vector::iterator t = tags.begin(); t != tags.end(); ++t) { + if (t != tags.begin ()) { + sanitized.append(" "); + } + sanitized.append (downcase (*t)); + } + + return sanitized; +} + +std::vector +PluginManager::get_all_tags (TagFilter tag_filter) const +{ + std::vector ret; + + PluginTagList::const_iterator pt; + for (pt = ptags.begin(); pt != ptags.end(); ++pt) { + if ((*pt).tags.empty ()) { + continue; + } + + /* if favorites_only then we need to check the info ptr and maybe skip */ + if (tag_filter == OnlyFavorites) { + PluginStatus stat ((*pt).type, (*pt).unique_id); + PluginStatusList::const_iterator i = find (statuses.begin(), statuses.end(), stat); + if ((i != statuses.end()) && (i->status == Favorite)) { + /* it's a favorite! */ + } else { + continue; + } + } + if (tag_filter == NoHidden) { + PluginStatus stat ((*pt).type, (*pt).unique_id); + PluginStatusList::const_iterator i = find (statuses.begin(), statuses.end(), stat); + if ((i != statuses.end()) && (i->status == Hidden)) { + continue; + } + } + + /* parse each plugin's tag string into separate tags */ + vector tags; + if (!PBD::tokenize ((*pt).tags, string(" "), std::back_inserter (tags), true)) { +#ifndef NDEBUG + cerr << _("PluginManager: Could not tokenize string: ") << (*pt).tags << endmsg; +#endif + continue; + } + + /* maybe add the tags we've found */ + for (vector::iterator t = tags.begin(); t != tags.end(); ++t) { + /* if this tag isn't already in the list, add it */ + vector::iterator i = find (ret.begin(), ret.end(), *t); + if (i == ret.end()) { + ret.push_back (*t); + } + } + } + + /* sort in alphabetical order */ + SortByTag sorter; + sort (ret.begin(), ret.end(), sorter); + + return ret; +} + + +const ARDOUR::PluginInfoList& PluginManager::windows_vst_plugin_info () { #ifdef WINDOWS_VST_SUPPORT @@ -1097,7 +1732,18 @@ PluginManager::windows_vst_plugin_info () #endif } -ARDOUR::PluginInfoList& +const 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 +} + +const ARDOUR::PluginInfoList& PluginManager::lxvst_plugin_info () { #ifdef LXVST_SUPPORT @@ -1108,14 +1754,14 @@ PluginManager::lxvst_plugin_info () #endif } -ARDOUR::PluginInfoList& +const ARDOUR::PluginInfoList& PluginManager::ladspa_plugin_info () { assert(_ladspa_plugin_info); return *_ladspa_plugin_info; } -ARDOUR::PluginInfoList& +const ARDOUR::PluginInfoList& PluginManager::lv2_plugin_info () { #ifdef LV2_SUPPORT @@ -1126,7 +1772,7 @@ PluginManager::lv2_plugin_info () #endif } -ARDOUR::PluginInfoList& +const ARDOUR::PluginInfoList& PluginManager::au_plugin_info () { #ifdef AUDIOUNIT_SUPPORT @@ -1136,3 +1782,10 @@ PluginManager::au_plugin_info () #endif return _empty_plugin_info; } + +const ARDOUR::PluginInfoList& +PluginManager::lua_plugin_info () +{ + assert(_lua_plugin_info); + return *_lua_plugin_info; +}