/* This file is part of Ingen. * Copyright (C) 2007-2009 David Robillard * * Ingen is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "redlandmm/World.hpp" #include "raul/log.hpp" #include "raul/Atom.hpp" #include "ingen-config.h" #include "module/World.hpp" #include "internals/Controller.hpp" #include "internals/Delay.hpp" #include "internals/Note.hpp" #include "internals/Trigger.hpp" #include "Engine.hpp" #include "InternalPlugin.hpp" #include "NodeFactory.hpp" #include "PatchImpl.hpp" #include "ThreadManager.hpp" #ifdef HAVE_LADSPA_H #include "LADSPANode.hpp" #include "LADSPAPlugin.hpp" #endif #ifdef HAVE_SLV2 #include "slv2/slv2.h" #include "LV2Plugin.hpp" #include "LV2Node.hpp" #endif using namespace std; using namespace Raul; namespace Ingen { using namespace Internals; NodeFactory::NodeFactory(Ingen::Shared::World* world) : _world(world) , _has_loaded(false) #ifdef HAVE_SLV2 , _lv2_info(new LV2Info(world)) #endif { } NodeFactory::~NodeFactory() { for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) delete i->second; _plugins.clear(); } PluginImpl* NodeFactory::plugin(const Raul::URI& uri) { const Plugins::const_iterator i = _plugins.find(uri); return ((i != _plugins.end()) ? i->second : NULL); } /** DEPRECATED: Find a plugin by type, lib, label. * * Slow. Evil. Do not use. */ PluginImpl* NodeFactory::plugin(const string& type, const string& lib, const string& label) { if (type != "LADSPA" || lib.empty() || label.empty()) return NULL; #ifdef HAVE_LADSPA_H for (Plugins::const_iterator i = _plugins.begin(); i != _plugins.end(); ++i) { LADSPAPlugin* lp = dynamic_cast(i->second); if (lp && lp->library_name() == lib && lp->label() == label) return lp; } #endif error << "Failed to find " << type << " plugin " << lib << " / " << label << endl; return NULL; } void NodeFactory::load_plugins() { ThreadManager::assert_thread(THREAD_PRE_PROCESS); _world->rdf_world()->mutex().lock(); // Only load if we havn't already, so every client connecting doesn't cause // this (expensive!) stuff to happen. Not the best solution - would be nice // if clients could refresh plugins list for whatever reason :/ if (!_has_loaded) { _plugins.clear(); // FIXME: assert empty? load_internal_plugins(); #ifdef HAVE_SLV2 load_lv2_plugins(); #endif #ifdef HAVE_LADSPA_H load_ladspa_plugins(); #endif _has_loaded = true; } _world->rdf_world()->mutex().unlock(); } void NodeFactory::load_internal_plugins() { Shared::LV2URIMap& uris = *_world->uris().get(); InternalPlugin* controller_plug = ControllerNode::internal_plugin(uris); _plugins.insert(make_pair(controller_plug->uri(), controller_plug)); InternalPlugin* delay_plug = DelayNode::internal_plugin(uris); _plugins.insert(make_pair(delay_plug->uri(), delay_plug)); InternalPlugin* note_plug = NoteNode::internal_plugin(uris); _plugins.insert(make_pair(note_plug->uri(), note_plug)); InternalPlugin* trigger_plug = TriggerNode::internal_plugin(uris); _plugins.insert(make_pair(trigger_plug->uri(), trigger_plug)); } #ifdef HAVE_SLV2 /** Loads information about all LV2 plugins into internal plugin database. */ void NodeFactory::load_lv2_plugins() { SLV2Plugins plugins = slv2_world_get_all_plugins(_world->slv2_world()); for (unsigned i=0; i < slv2_plugins_size(plugins); ++i) { SLV2Plugin lv2_plug = slv2_plugins_get_at(plugins, i); const string uri(slv2_value_as_uri(slv2_plugin_get_uri(lv2_plug))); #ifndef NDEBUG assert(_plugins.find(uri) == _plugins.end()); #endif LV2Plugin* const plugin = new LV2Plugin(_lv2_info, uri); plugin->slv2_plugin(lv2_plug); _plugins.insert(make_pair(uri, plugin)); } slv2_plugins_free(_world->slv2_world(), plugins); } #endif // HAVE_SLV2 #ifdef HAVE_LADSPA_H /** Loads information about all LADSPA plugins into internal plugin database. */ void NodeFactory::load_ladspa_plugins() { char* env_ladspa_path = getenv("LADSPA_PATH"); string ladspa_path; if (!env_ladspa_path) { info << "Using default LADSPA_PATH=/usr/lib/ladspa:/usr/local/lib/ladspa:~/.ladspa" << endl; ladspa_path = string("/usr/lib/ladspa:/usr/local/lib/ladspa:").append( getenv("HOME")).append("/.ladspa"); } else { ladspa_path = env_ladspa_path; } // Yep, this should use an sstream alright.. while (!ladspa_path.empty()) { const string dir = ladspa_path.substr(0, ladspa_path.find(':')); if (ladspa_path.find(':') != string::npos) ladspa_path = ladspa_path.substr(ladspa_path.find(':')+1); else ladspa_path.clear(); DIR* pdir = opendir(dir.c_str()); if (pdir == NULL) continue; struct dirent* pfile; while ((pfile = readdir(pdir))) { union { void* dp; LADSPA_Descriptor_Function fp; } df; df.dp = NULL; df.fp = NULL; LADSPA_Descriptor* descriptor = NULL; if (!strcmp(pfile->d_name, ".") || !strcmp(pfile->d_name, "..")) continue; const string lib_path = Glib::build_filename(dir, pfile->d_name); // Ignore stupid libtool files. Kludge alert. if (lib_path.substr(lib_path.length()-3) == ".la") continue; Glib::Module* plugin_library = new Glib::Module(lib_path, Glib::MODULE_BIND_LOCAL); if (!plugin_library) continue; if (!(*plugin_library)) { delete plugin_library; continue; } bool found = plugin_library->get_symbol("ladspa_descriptor", df.dp); if (!found || !df.dp) { warn << "Non-LADSPA library " << lib_path << " found in LADSPA path" << endl; delete plugin_library; continue; } for (unsigned long i=0; (descriptor = (LADSPA_Descriptor*)df.fp(i)) != NULL; ++i) { char id_str[11]; snprintf(id_str, 11, "%lu", descriptor->UniqueID); const string uri = string("urn:ladspa:").append(id_str); const Plugins::const_iterator i = _plugins.find(uri); if (i == _plugins.end()) { LADSPAPlugin* plugin = new LADSPAPlugin(*_world->uris().get(), lib_path, uri, descriptor->UniqueID, descriptor->Label, descriptor->Name); _plugins.insert(make_pair(uri, plugin)); } else { warn << "Duplicate " << uri << " - Using " << i->second->library_path() << " over " << lib_path << endl; } } delete plugin_library; } closedir(pdir); } } #endif // HAVE_LADSPA_H } // namespace Ingen