/* This file is part of Ingen. * Copyright (C) 2007-2009 Dave 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 "redlandmm/World.hpp" #include "raul/log.hpp" #include "raul/Atom.hpp" #include "ingen-config.h" #include "module/World.hpp" #include "internals/Note.hpp" #include "internals/Trigger.hpp" #include "internals/Controller.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) if (i->second->type() != Shared::Plugin::Internal) 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 == "" || label == "") 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() { assert(ThreadManager::current_thread_id() == 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() { // FIXME: This is a touch gross... const SampleRate r = 48000; const size_t s = sizeof(Sample); Engine& engine = *_world->local_engine; PatchImpl* parent = new PatchImpl(engine, "dummy", 1, NULL, r, s, 1); NodeImpl* n = NULL; n = new NoteNode(*engine.buffer_factory(), "foo", 1, parent, r, s); _plugins.insert(make_pair(n->plugin_impl()->uri(), n->plugin_impl())); delete n; n = new TriggerNode(*engine.buffer_factory(), "foo", 1, parent, r, s); _plugins.insert(make_pair(n->plugin_impl()->uri(), n->plugin_impl())); delete n; n = new ControllerNode(*engine.buffer_factory(), "foo", 1, parent, r, s); _plugins.insert(make_pair(n->plugin_impl()->uri(), n->plugin_impl())); delete n; delete parent; } #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 != "") { 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 = ""; 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 = 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 || !(*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(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