2 // LADSPAInfo.C - Class for indexing information on LADSPA Plugins
4 // Copyleft (C) 2002 Mike Rawes <myk@waxfrenzy.org>
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 // #include <config.h>
33 #include <sys/types.h>
40 #define HAVE_LIBLRDF 1
45 #include "LADSPAInfo.h"
49 LADSPAInfo::LADSPAInfo(bool override,
50 const char *path_list)
52 if (strlen(path_list) > 0) {
53 m_ExtraPaths = strdup(path_list);
57 m_LADSPAPathOverride = override;
62 LADSPAInfo::~LADSPAInfo()
68 LADSPAInfo::RescanPlugins(void)
70 // Clear out what we've got
73 if (!m_LADSPAPathOverride) {
74 // Get $LADPSA_PATH, if available
75 char *ladspa_path = getenv("LADSPA_PATH");
77 ScanPathList(ladspa_path, &LADSPAInfo::ExaminePluginLibrary);
81 cerr << "WARNING: LADSPA_PATH environment variable not set" << endl;
82 cerr << " Assuming /usr/lib/ladspa:/usr/local/lib/ladspa" << endl;
84 ScanPathList("/usr/lib/ladspa:/usr/local/lib/ladspa", &LADSPAInfo::ExaminePluginLibrary);
88 // Check any supplied extra paths
90 ScanPathList(m_ExtraPaths, &LADSPAInfo::ExaminePluginLibrary);
93 // Do we have any plugins now?
94 if (m_Plugins.size() == 0) {
95 cerr << "WARNING: No plugins found" << endl;
97 cerr << m_Plugins.size() << " plugins found in " << m_Libraries.size() << " libraries" << endl;
100 // Got some plugins. Now search for RDF data
103 char *rdf_path = getenv("LADSPA_RDF_PATH");
107 ScanPathList(rdf_path, &LADSPAInfo::ExamineRDFFile);
110 cerr << "WARNING: LADSPA_RDF_PATH environment variable not set" << endl;
111 cerr << " Assuming /usr/share/ladspa/rdf:/usr/local/share/ladspa/rdf" << endl;
114 ScanPathList("/usr/share/ladspa/rdf:/usr/local/share/ladspa/rdf", &LADSPAInfo::ExamineRDFFile);
116 MetadataRDFDescend(LADSPA_BASE "Plugin", 0);
118 // See which plugins were not added to an rdf group, and add them
119 // all into the top level 'LADSPA' one
120 list<unsigned long> rdf_p;
122 // Get indices of plugins added to groups
123 for (vector<RDFURIInfo>::iterator ri = m_RDFURIs.begin(); ri != m_RDFURIs.end(); ri++) {
124 rdf_p.insert(rdf_p.begin(), ri->Plugins.begin(), ri->Plugins.end());
127 // Add all uncategorized plugins to top level group, subclassed by their
128 // library's basename.
131 unsigned long last_p = 0;
132 for (list<unsigned long>::iterator p = rdf_p.begin(); p != rdf_p.end(); p++) {
133 if ((*p - last_p) > 1) {
134 for (unsigned long i = last_p + 1; i < *p; i++) {
135 // URI 0 is top-level "LADSPA" group
136 m_RDFURIs[0].Plugins.push_back(i);
141 while (++last_p < m_Plugins.size()) {
142 // URI 0 is top-level "LADSPA" group
143 m_RDFURIs[0].Plugins.push_back(last_p);
148 // No RDF. Add all plugins to top-level group
154 m_RDFURIs.push_back(ri);
155 m_RDFLabelLookup["LADSPA"] = 0;
157 for (unsigned long i = 0; i < m_Plugins.size(); i++) {
159 m_RDFURIs[0].Plugins.push_back(i);
166 LADSPAInfo::UnloadAllLibraries(void)
169 for (vector<PluginInfo>::iterator i = m_Plugins.begin();
170 i != m_Plugins.end(); i++) {
171 if (i->Descriptor) i->Descriptor = NULL;
174 for (vector<LibraryInfo>::iterator i = m_Libraries.begin();
175 i != m_Libraries.end(); i++) {
184 const LADSPA_Descriptor *
185 LADSPAInfo::GetDescriptorByID(unsigned long unique_id)
187 if (m_IDLookup.find(unique_id) == m_IDLookup.end()) {
188 cerr << "LADSPA Plugin ID " << unique_id << " not found!" << endl;
193 unsigned long plugin_index = m_IDLookup[unique_id];
195 PluginInfo *pi = &(m_Plugins[plugin_index]);
196 LibraryInfo *li = &(m_Libraries[pi->LibraryIndex]);
198 if (!(pi->Descriptor)) {
199 LADSPA_Descriptor_Function desc_func = GetDescriptorFunctionForLibrary(pi->LibraryIndex);
200 if (desc_func) pi->Descriptor = desc_func(pi->Index);
203 if (pi->Descriptor) {
205 // Success, so increment ref counter for library
209 return pi->Descriptor;
213 LADSPAInfo::DiscardDescriptorByID(unsigned long unique_id)
215 if (m_IDLookup.find(unique_id) == m_IDLookup.end()) {
216 cerr << "LADSPA Plugin ID " << unique_id << " not found!" << endl;
220 unsigned long plugin_index = m_IDLookup[unique_id];
222 PluginInfo *pi = &(m_Plugins[plugin_index]);
223 LibraryInfo *li = &(m_Libraries[pi->LibraryIndex]);
225 pi->Descriptor = NULL;
227 // Decrement reference counter for library, and unload if last
228 if (li->RefCount > 0) {
230 if (li->RefCount == 0) {
240 // ****************************************************************************
241 // ** SSM Specific Functions **
242 // ****************************************************************************
245 LADSPAInfo::GetIDFromFilenameAndLabel(std::string filename,
248 bool library_loaded = false;
250 if (m_FilenameLookup.find(filename) == m_FilenameLookup.end()) {
251 cerr << "LADSPA Library " << filename << " not found!" << endl;
255 unsigned long library_index = m_FilenameLookup[filename];
257 if (!(m_Libraries[library_index].Handle)) library_loaded = true;
259 LADSPA_Descriptor_Function desc_func = GetDescriptorFunctionForLibrary(library_index);
265 // Search for label in library
266 const LADSPA_Descriptor *desc;
267 for (unsigned long i = 0; (desc = desc_func(i)) != NULL; i++) {
268 string l = desc->Label;
271 // If we had to load the library, unload it
272 unsigned long id = desc->UniqueID;
273 if (library_loaded) {
274 dlclose(m_Libraries[library_index].Handle);
275 m_Libraries[library_index].Handle = NULL;
281 cerr << "Plugin " << label << " not found in library " << filename << endl;
285 const vector<LADSPAInfo::PluginEntry>
286 LADSPAInfo::GetMenuList(void)
288 m_SSMMenuList.clear();
290 DescendGroup("", "LADSPA", 1);
292 return m_SSMMenuList;
296 LADSPAInfo::GetPluginListEntryByID(unsigned long unique_id)
299 for (vector<PluginEntry>::iterator i = m_SSMMenuList.begin();
300 i != m_SSMMenuList.end(); i++, j++) {
301 if (i->UniqueID == unique_id) return j;
303 return m_SSMMenuList.size();
306 // ****************************************************************************
307 // ** Private Member Functions **
308 // ****************************************************************************
310 // Build a list of plugins by group, suitable for SSM LADSPA Plugin drop-down
311 // The top-level "LADSPA" group is not included
314 LADSPAInfo::DescendGroup(string prefix,
318 list<string> groups = GetSubGroups(group);
320 if (prefix.length() > 0) {
321 // Add an explicit '/' as we're creating sub-menus from groups
325 for (list<string>::iterator g = groups.begin(); g != groups.end(); g++) {
328 // Escape '/' and '|' characters
329 size_t x = g->find_first_of("/|");
330 if (x == string::npos) {
334 while (x < string::npos) {
335 name += g->substr(last_x, x - last_x) + '\\' + (*g)[x];
337 x = g->find_first_of("/|", x + 1);
339 name += g->substr(last_x, x - last_x);
342 DescendGroup(prefix + name, *g, depth + 1);
344 if (m_RDFLabelLookup.find(group) != m_RDFLabelLookup.end()) {
345 unsigned long uri_index = m_RDFLabelLookup[group];
347 // Create group for unclassified plugins
348 if (prefix.length() == 0) {
349 prefix = "Unclassified/";
353 // Temporary list (for sorting the plugins by name)
354 list<PluginEntry> plugins;
356 for (vector<unsigned long>::iterator p = m_RDFURIs[uri_index].Plugins.begin();
357 p != m_RDFURIs[uri_index].Plugins.end(); p++) {
359 PluginInfo *pi = &(m_Plugins[*p]);
362 // Escape '/' and '|' characters
363 size_t x = pi->Name.find_first_of("/|");
364 if (x == string::npos) {
368 while (x < string::npos) {
369 name += pi->Name.substr(last_x, x - last_x) + '\\' + pi->Name[x];
371 x = pi->Name.find_first_of("/|", x + 1);
373 name += pi->Name.substr(last_x, x - last_x);
379 pe.UniqueID = pi->UniqueID;
380 pe.Name = prefix + name;
382 plugins.push_back(pe);
386 // Deal with duplicates by numbering them
387 for (list<PluginEntry>::iterator i = plugins.begin();
388 i != plugins.end(); ) {
389 string name = i->Name;
393 while ((i != plugins.end()) && (i->Name == name)) {
396 i->Name = name + " (" + s.str() + ")";
402 // Add all ordered entries to the Menu List
403 // This ensures that plugins appear after groups
404 for (list<PluginEntry>::iterator p = plugins.begin(); p != plugins.end(); p++) {
405 m_SSMMenuList.push_back(*p);
410 // Get list of groups that are within given group. The root group is
413 LADSPAInfo::GetSubGroups(const string group)
416 unsigned long uri_index;
418 if (m_RDFLabelLookup.find(group) == m_RDFLabelLookup.end()) {
421 uri_index = m_RDFLabelLookup[group];
424 for (vector<unsigned long>::iterator sg = m_RDFURIs[uri_index].Children.begin();
425 sg != m_RDFURIs[uri_index].Children.end(); sg++) {
426 groups.push_back(m_RDFURIs[*sg].Label);
434 // Unload any loaded DLLs and clear vectors etc
436 LADSPAInfo::CleanUp(void)
438 m_MaxInputPortCount = 0;
443 // Unload loaded dlls
444 for (vector<LibraryInfo>::iterator i = m_Libraries.begin();
445 i != m_Libraries.end(); i++) {
446 if (i->Handle) dlclose(i->Handle);
452 m_RDFURILookup.clear();
461 // Given a colon-separated list of paths, examine the contents of each
462 // path, examining any regular files using the given member function,
463 // which currently can be:
465 // ExaminePluginLibrary - add plugin library info from plugins
466 // ExamineRDFFile - add plugin information from .rdf/.rdfs files
468 LADSPAInfo::ScanPathList(const char *path_list,
469 void (LADSPAInfo::*ExamineFunc)(const string,
481 // This does the same kind of thing as strtok, but strtok won't
484 while (*start != '\0') {
485 while (*start == ':') start++;
487 while (*end != ':' && *end != '\0') end++;
489 if (end - start > 0) {
490 extra = (*(end - 1) == '/') ? 0 : 1;
491 path = (char *)malloc(end - start + 1 + extra);
493 strncpy(path, start, end - start);
494 if (extra == 1) path[end - start] = '/';
495 path[end - start + extra] = '\0';
499 cerr << "WARNING: Could not open path " << path << endl;
501 while ((ep = readdir(dp))) {
503 // Stat file to get type
504 basename = ep->d_name;
505 if (!stat((path + basename).c_str(), &sb)) {
507 // We only want regular files
508 if (S_ISREG(sb.st_mode)) (*this.*ExamineFunc)(path, basename);
520 // Check given file is a valid LADSPA Plugin library
522 // If so, add path, library and plugin info
523 // to the m_Paths, m_Libraries and m_Plugins vectors.
526 LADSPAInfo::ExaminePluginLibrary(const string path,
527 const string basename)
530 LADSPA_Descriptor_Function desc_func;
531 const LADSPA_Descriptor *desc;
532 string fullpath = path + basename;
534 // We're not executing any code, so be lazy about resolving symbols
535 handle = dlopen(fullpath.c_str(), RTLD_LAZY);
538 cerr << "WARNING: File " << fullpath
539 << " could not be examined" << endl;
540 cerr << "dlerror() output:" << endl;
541 cerr << dlerror() << endl;
544 // It's a DLL, so now see if it's a LADSPA plugin library
545 desc_func = (LADSPA_Descriptor_Function)dlsym(handle,
546 "ladspa_descriptor");
549 // Is DLL, but not a LADSPA one
550 cerr << "WARNING: DLL " << fullpath
551 << " has no ladspa_descriptor function" << endl;
552 cerr << "dlerror() output:" << endl;
553 cerr << dlerror() << endl;
556 // Got ladspa_descriptor, so we can now get plugin info
557 bool library_added = false;
562 // First, check that it's not a dupe
563 if (m_IDLookup.find(desc->UniqueID) != m_IDLookup.end()) {
564 unsigned long plugin_index = m_IDLookup[desc->UniqueID];
565 unsigned long library_index = m_Plugins[plugin_index].LibraryIndex;
566 unsigned long path_index = m_Libraries[library_index].PathIndex;
568 cerr << "WARNING: Duplicated Plugin ID ("
569 << desc->UniqueID << ") found:" << endl;
571 cerr << " Plugin " << m_Plugins[plugin_index].Index
572 << " in library: " << m_Paths[path_index]
573 << m_Libraries[library_index].Basename
574 << " [First instance found]" << endl;
575 cerr << " Plugin " << i << " in library: " << fullpath
576 << " [Duplicate not added]" << endl;
578 if (CheckPlugin(desc)) {
580 // Add path if not already added
581 unsigned long path_index;
582 vector<string>::iterator p = find(m_Paths.begin(), m_Paths.end(), path);
583 if (p == m_Paths.end()) {
584 path_index = m_Paths.size();
585 m_Paths.push_back(path);
587 path_index = p - m_Paths.begin();
590 // Add library info if not already added
591 if (!library_added) {
593 li.PathIndex = path_index;
594 li.Basename = basename;
597 m_Libraries.push_back(li);
599 library_added = true;
604 pi.LibraryIndex = m_Libraries.size() - 1;
606 pi.UniqueID = desc->UniqueID;
607 pi.Label = desc->Label;
608 pi.Name = desc->Name;
609 pi.Descriptor = NULL;
610 m_Plugins.push_back(pi);
612 // Find number of input ports
613 unsigned long in_port_count = 0;
614 for (unsigned long p = 0; p < desc->PortCount; p++) {
615 if (LADSPA_IS_PORT_INPUT(desc->PortDescriptors[p])) {
619 if (in_port_count > m_MaxInputPortCount) {
620 m_MaxInputPortCount = in_port_count;
624 m_IDLookup[desc->UniqueID] = m_Plugins.size() - 1;
627 cerr << "WARNING: Plugin " << desc->UniqueID << " not added" << endl;
631 desc = desc_func(++i);
639 // Examine given RDF plugin meta-data file
641 LADSPAInfo::ExamineRDFFile(const std::string path,
642 const std::string basename)
644 string fileuri = "file://" + path + basename;
646 if (lrdf_read_file(fileuri.c_str())) {
647 cerr << "WARNING: File " << path + basename << " could not be parsed [Ignored]" << endl;
651 // Recursively add rdf information for plugins that have been
652 // found from scanning LADSPA_PATH
654 LADSPAInfo::MetadataRDFDescend(const char * uri,
655 unsigned long parent)
657 unsigned long this_uri_index;
659 // Check URI not already added
660 if (m_RDFURILookup.find(uri) == m_RDFURILookup.end()) {
667 if (ri.URI == LADSPA_BASE "Plugin") {
669 // Add top level group as "LADSPA"
670 // This will always happen, even if there are no .rdf files read by liblrdf
671 // or if there is no liblrdf support
674 char * label = lrdf_get_label(uri);
678 ri.Label = "(No label)";
682 // Add any instances found
683 lrdf_uris * instances = lrdf_get_instances(uri);
685 for (unsigned long j = 0; j < instances->count; j++) {
686 unsigned long uid = lrdf_get_uid(instances->items[j]);
687 if (m_IDLookup.find(uid) != m_IDLookup.end()) {
688 ri.Plugins.push_back(m_IDLookup[uid]);
693 lrdf_free_uris(instances);
695 m_RDFURIs.push_back(ri);
696 this_uri_index = m_RDFURIs.size() - 1;
698 m_RDFURILookup[ri.URI] = this_uri_index;
699 m_RDFLabelLookup[ri.Label] = this_uri_index;
704 this_uri_index = m_RDFURILookup[uri];
707 // Only add parent - child info if this uri is NOT the first (root) uri
708 if (this_uri_index > 0) {
709 m_RDFURIs[this_uri_index].Parents.push_back(parent);
710 m_RDFURIs[parent].Children.push_back(this_uri_index);
713 lrdf_uris * uris = lrdf_get_subclasses(uri);
716 for (unsigned long i = 0; i < uris->count; i++) {
717 MetadataRDFDescend(uris->items[i], this_uri_index);
721 lrdf_free_uris(uris);
726 LADSPAInfo::CheckPlugin(const LADSPA_Descriptor *desc)
728 #define test(t, m) { \
734 test(desc->instantiate, "WARNING: Plugin has no instatiate function");
735 test(desc->connect_port, "WARNING: Warning: Plugin has no connect_port funciton");
736 test(desc->run, "WARNING: Plugin has no run function");
737 test(!(desc->run_adding != 0 && desc->set_run_adding_gain == 0),
738 "WARNING: Plugin has run_adding but no set_run_adding_gain");
739 test(!(desc->run_adding == 0 && desc->set_run_adding_gain != 0),
740 "WARNING: Plugin has set_run_adding_gain but no run_adding");
741 test(desc->cleanup, "WARNING: Plugin has no cleanup function");
742 test(!LADSPA_IS_INPLACE_BROKEN(desc->Properties),
743 "WARNING: Plugin cannot use in place processing");
744 test(desc->PortCount, "WARNING: Plugin has no ports");
749 LADSPA_Descriptor_Function
750 LADSPAInfo::GetDescriptorFunctionForLibrary(unsigned long library_index)
752 LibraryInfo *li = &(m_Libraries[library_index]);
757 string fullpath = m_Paths[li->PathIndex];
758 fullpath.append(li->Basename);
760 // Immediate symbol resolution, as plugin code is likely to be executed
761 li->Handle = dlopen(fullpath.c_str(), RTLD_NOW);
764 // Plugin library changed since last path scan
765 cerr << "WARNING: Plugin library " << fullpath << " cannot be loaded" << endl;
766 cerr << "Rescan of plugins recommended" << endl;
767 cerr << "dlerror() output:" << endl;
768 cerr << dlerror() << endl;
773 // Got handle so now verify that it's a LADSPA plugin library
774 const LADSPA_Descriptor_Function desc_func = (LADSPA_Descriptor_Function)dlsym(li->Handle,
775 "ladspa_descriptor");
778 // Is DLL, but not a LADSPA one (changed since last path scan?)
779 cerr << "WARNING: DLL " << m_Paths[li->PathIndex] << li->Basename
780 << " has no ladspa_descriptor function" << endl;
781 cerr << "Rescan of plugins recommended" << endl;
782 cerr << "dlerror() output:" << endl;
783 cerr << dlerror() << endl;