Update NTK.
[nondaw.git] / mixer / src / LADSPAInfo.C
blob4214c97edf0d42f559594c30e26a8322154cb970
1 //
2 //  LADSPAInfo.C - Class for indexing information on LADSPA Plugins
3 //
4 //  Copyleft (C) 2002  Mike Rawes <myk@waxfrenzy.org>
5 //
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>
23 #include <vector>
24 #include <string>
25 #include <list>
26 #include <map>
27 #include <iostream>
28 #include <sstream>
29 #include <algorithm>
30 #include <stdio.h>
31 #include <cstring>
32 #include <cstdlib>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <dirent.h>
36 #include <dlfcn.h>
38 #include <ladspa.h>
40 #define HAVE_LIBLRDF 1
41 #ifdef HAVE_LIBLRDF
42 #include <lrdf.h>
43 #endif
45 #include "LADSPAInfo.h"
47 using namespace std;
49 LADSPAInfo::LADSPAInfo(bool override,
50                        const char *path_list)
52         if (strlen(path_list) > 0) {
53                 m_ExtraPaths = strdup(path_list);
54         } else {
55                 m_ExtraPaths = NULL;
56         }
57         m_LADSPAPathOverride = override;
59         RescanPlugins();
62 LADSPAInfo::~LADSPAInfo()
64         CleanUp();
67 void
68 LADSPAInfo::RescanPlugins(void)
70 // Clear out what we've got
71         CleanUp();
73         if (!m_LADSPAPathOverride) {
74         // Get $LADPSA_PATH, if available
75                 char *ladspa_path = getenv("LADSPA_PATH");
76                 if (ladspa_path) {
77                         ScanPathList(ladspa_path, &LADSPAInfo::ExaminePluginLibrary);
79                 } else {
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);
85                 }
86         }
88 // Check any supplied extra paths
89         if (m_ExtraPaths) {
90                 ScanPathList(m_ExtraPaths, &LADSPAInfo::ExaminePluginLibrary);
91         }
93 // Do we have any plugins now?
94         if (m_Plugins.size() == 0) {
95                 cerr << "WARNING: No plugins found" << endl;
96         } else {
97                 cerr << m_Plugins.size() << " plugins found in " << m_Libraries.size() << " libraries" << endl;
99 #ifdef HAVE_LIBLRDF
100         // Got some plugins. Now search for RDF data
101                 lrdf_init();
103                 char *rdf_path = getenv("LADSPA_RDF_PATH");
105                 if (rdf_path) {
106                 // Examine rdf info
107                         ScanPathList(rdf_path, &LADSPAInfo::ExamineRDFFile);
109                 } else {
110                         cerr << "WARNING: LADSPA_RDF_PATH environment variable not set" << endl;
111                         cerr << "         Assuming /usr/share/ladspa/rdf:/usr/local/share/ladspa/rdf" << endl;
113                 // Examine rdf info
114                         ScanPathList("/usr/share/ladspa/rdf:/usr/local/share/ladspa/rdf", &LADSPAInfo::ExamineRDFFile);
115                 }
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());
125                 }
127         // Add all uncategorized plugins to top level group, subclassed by their
128         // library's basename.
129                 rdf_p.unique();
130                 rdf_p.sort();
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);
137                                 }
138                         }
139                         last_p = *p;
140                 }
141                 while (++last_p < m_Plugins.size()) {
142                 // URI 0 is top-level "LADSPA" group
143                         m_RDFURIs[0].Plugins.push_back(last_p);
144                 }
146                 lrdf_cleanup();
147 #else
148         // No RDF. Add all plugins to top-level group
149                 RDFURIInfo ri;
151                 ri.URI = "";
152                 ri.Label = "LADSPA";
154                 m_RDFURIs.push_back(ri);
155                 m_RDFLabelLookup["LADSPA"] = 0;
157                 for (unsigned long i = 0; i < m_Plugins.size(); i++) {
158                 // Add plugin index
159                         m_RDFURIs[0].Plugins.push_back(i);
160                 }
161 #endif
162         }
165 void
166 LADSPAInfo::UnloadAllLibraries(void)
168 // Blank descriptors
169         for (vector<PluginInfo>::iterator i = m_Plugins.begin();
170                 i != m_Plugins.end(); i++) {
171                 if (i->Descriptor) i->Descriptor = NULL;
172         }
173 // Unload DLLs,
174         for (vector<LibraryInfo>::iterator i = m_Libraries.begin();
175                 i != m_Libraries.end(); i++) {
176                 if (i->Handle) {
177                         dlclose(i->Handle);
178                         i->Handle = NULL;
179                 }
180                 i->RefCount = 0;
181         }
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;
189                 return NULL;
190         }
192 // Got plugin index
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);
201         }
203         if (pi->Descriptor) {
205         // Success, so increment ref counter for library
206                 li->RefCount++;
207         }
209         return pi->Descriptor;
212 void
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;
217         } else {
219         // Get plugin index
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) {
229                         li->RefCount--;
230                         if (li->RefCount == 0) {
232                         // Unload library
233                                 dlclose(li->Handle);
234                                 li->Handle = NULL;
235                         }
236                 }
237         }
240 // ****************************************************************************
241 // **                      SSM Specific Functions                            **
242 // ****************************************************************************
244 unsigned long
245 LADSPAInfo::GetIDFromFilenameAndLabel(std::string filename,
246                                       std::string label)
248         bool library_loaded = false;
250         if (m_FilenameLookup.find(filename) == m_FilenameLookup.end()) {
251                 cerr << "LADSPA Library " << filename << " not found!" << endl;
252                 return 0;
253         }
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);
261         if (!desc_func) {
262                 return 0;
263         }
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;
269                 if (l == 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;
276                         }
277                         return id;
278                 }
279         }
281         cerr << "Plugin " << label << " not found in library " << filename << endl;
282         return 0;
285 const vector<LADSPAInfo::PluginEntry>
286 LADSPAInfo::GetMenuList(void)
288         m_SSMMenuList.clear();
290         DescendGroup("", "LADSPA", 1);
292         return m_SSMMenuList;
295 unsigned long
296 LADSPAInfo::GetPluginListEntryByID(unsigned long unique_id)
298         unsigned long j = 0;
299         for (vector<PluginEntry>::iterator i = m_SSMMenuList.begin();
300                 i != m_SSMMenuList.end(); i++, j++) {
301                 if (i->UniqueID == unique_id) return j;
302         }
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
313 void
314 LADSPAInfo::DescendGroup(string prefix,
315                          const string group,
316                          unsigned int depth)
318         list<string> groups = GetSubGroups(group);
320         if (prefix.length() > 0) {
321         // Add an explicit '/' as we're creating sub-menus from groups
322                 prefix += "/";
323         }
325         for (list<string>::iterator g = groups.begin(); g != groups.end(); g++) {
326                 string name;
328                 // Escape '/' and '|' characters
329                 size_t x = g->find_first_of("/|");
330                 if (x == string::npos) {
331                         name = *g;
332                 } else {
333                     size_t last_x = 0;
334                         while (x < string::npos) {
335                                 name += g->substr(last_x, x - last_x) + '\\' + (*g)[x];
336                                 last_x = x + 1;
337                                 x = g->find_first_of("/|", x + 1);
338                         }
339                         name += g->substr(last_x, x - last_x);
340                 }
342                 DescendGroup(prefix + name, *g, depth + 1);
343         }
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/";
350                         depth = depth + 1;
351                 }
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]);
360                         string name;
362                 // Escape '/' and '|' characters
363                         size_t x = pi->Name.find_first_of("/|");
364                         if (x == string::npos) {
365                                 name = pi->Name;
366                         } else {
367                                 size_t last_x = 0;
368                                 while (x < string::npos) {
369                                         name += pi->Name.substr(last_x, x - last_x) + '\\' + pi->Name[x];
370                                         last_x = x + 1;
371                                         x = pi->Name.find_first_of("/|", x + 1);
372                                 }
373                                 name += pi->Name.substr(last_x, x - last_x);
374                         }
376                         PluginEntry pe;
378                         pe.Depth = depth;
379                         pe.UniqueID = pi->UniqueID;
380                         pe.Name = prefix + name;
382                         plugins.push_back(pe);
383                 }
384                 plugins.sort();
386         // Deal with duplicates by numbering them
387                 for (list<PluginEntry>::iterator i = plugins.begin();
388                         i != plugins.end(); ) {
389                         string name = i->Name;
391                         i++;
392                         unsigned long n = 2;
393                         while ((i != plugins.end()) && (i->Name == name)) {
394                                 stringstream s;
395                                 s << n;
396                                 i->Name = name + " (" + s.str() + ")";
397                                 n++;
398                                 i++;
399                         }
400                 }
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);
406                 }
407         }
410 // Get list of groups that are within given group. The root group is
411 // always "LADSPA"
412 list<string>
413 LADSPAInfo::GetSubGroups(const string group)
415         list<string> groups;
416         unsigned long uri_index;
418         if (m_RDFLabelLookup.find(group) == m_RDFLabelLookup.end()) {
419                 return groups;
420         } else {
421                 uri_index = m_RDFLabelLookup[group];
422         }
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);
427         }
429         groups.sort();
431         return groups;
434 // Unload any loaded DLLs and clear vectors etc
435 void
436 LADSPAInfo::CleanUp(void)
438         m_MaxInputPortCount = 0;
440         m_IDLookup.clear();
441         m_Plugins.clear();
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);
447         }
449         m_Libraries.clear();
450         m_Paths.clear();
452         m_RDFURILookup.clear();
453         m_RDFURIs.clear();
455         if (m_ExtraPaths) {
456                 free(m_ExtraPaths);
457                 m_ExtraPaths = NULL;
458         }
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
467 void
468 LADSPAInfo::ScanPathList(const char *path_list,
469                          void (LADSPAInfo::*ExamineFunc)(const string,
470                                                          const string))
472         const char *start;
473         const char *end;
474         int extra;
475         char *path;
476         string basename;
477         DIR *dp;
478         struct dirent *ep;
479         struct stat sb;
481 // This does the same kind of thing as strtok, but strtok won't
482 // like the const
483         start = path_list;
484         while (*start != '\0') {
485                 while (*start == ':') start++;
486                 end = 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);
492                         if (path) {
493                                 strncpy(path, start, end - start);
494                                 if (extra == 1) path[end - start] = '/';
495                                 path[end - start + extra] = '\0';
497                                 dp = opendir(path);
498                                 if (!dp) {
499                                         cerr << "WARNING: Could not open path " << path << endl;
500                                 } else {
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);
509                                                 }
510                                         }
511                                         closedir(dp);
512                                 }
513                                 free(path);
514                         }
515                 }
516                 start = end;
517         }
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.
525 void
526 LADSPAInfo::ExaminePluginLibrary(const string path,
527                                  const string basename)
529         void *handle;
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);
537         if (!handle) {
538                 cerr << "WARNING: File " << fullpath
539                         << " could not be examined" << endl;
540                 cerr << "dlerror() output:" << endl;
541                 cerr << dlerror() << endl;
542         } else {
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");
547                 if (!desc_func) {
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;
554                 } else {
556                 // Got ladspa_descriptor, so we can now get plugin info
557                         bool library_added = false;
558                         unsigned long i = 0;
559                         desc = desc_func(i);
560                         while (desc) {
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;
577                                 } else {
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);
586                                                 } else {
587                                                         path_index = p - m_Paths.begin();
588                                                 }
590                                         // Add library info if not already added
591                                                 if (!library_added) {
592                                                         LibraryInfo li;
593                                                         li.PathIndex = path_index;
594                                                         li.Basename = basename;
595                                                         li.RefCount = 0;
596                                                         li.Handle = NULL;
597                                                         m_Libraries.push_back(li);
599                                                         library_added = true;
600                                                 }
602                                         // Add plugin info
603                                                 PluginInfo pi;
604                                                 pi.LibraryIndex = m_Libraries.size() - 1;
605                                                 pi.Index = i;
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])) {
616                                                                 in_port_count++;
617                                                         }
618                                                 }
619                                                 if (in_port_count > m_MaxInputPortCount) {
620                                                         m_MaxInputPortCount = in_port_count;
621                                                 }
623                                         // Add to index
624                                                 m_IDLookup[desc->UniqueID] = m_Plugins.size() - 1;
626                                         } else {
627                                                 cerr << "WARNING: Plugin " << desc->UniqueID << " not added" << endl;
628                                         }
629                                 }
631                                 desc = desc_func(++i);
632                         }
633                 }
634                 dlclose(handle);
635         }
638 #ifdef HAVE_LIBLRDF
639 // Examine given RDF plugin meta-data file
640 void
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;
648         }
651 // Recursively add rdf information for plugins that have been
652 // found from scanning LADSPA_PATH
653 void
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()) {
662         // Not found
663                 RDFURIInfo ri;
665                 ri.URI = uri;
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
672                         ri.Label = "LADSPA";
673                 } else {
674                         char * label = lrdf_get_label(uri);
675                         if (label) {
676                                 ri.Label = label;
677                         } else {
678                                 ri.Label = "(No label)";
679                         }
680                 }
682         // Add any instances found
683                 lrdf_uris * instances = lrdf_get_instances(uri);
684                 if (instances) {
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]);
689                                 }
690                         }
691                 }
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;
701         } else {
703         // Already added
704                 this_uri_index = m_RDFURILookup[uri];
705         }
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);
711         }
713         lrdf_uris * uris = lrdf_get_subclasses(uri);
715         if (uris) {
716                 for (unsigned long i = 0; i < uris->count; i++) {
717                         MetadataRDFDescend(uris->items[i], this_uri_index);
718                 }
719         }
721         lrdf_free_uris(uris);
723 #endif
725 bool
726 LADSPAInfo::CheckPlugin(const LADSPA_Descriptor *desc)
728 #define test(t, m) { \
729         if (!(t)) { \
730                 cerr << m << endl; \
731                 return false; \
732         } \
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");
746         return true;
749 LADSPA_Descriptor_Function
750 LADSPAInfo::GetDescriptorFunctionForLibrary(unsigned long library_index)
752         LibraryInfo *li = &(m_Libraries[library_index]);
754         if (!(li->Handle)) {
756         // Need full path
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);
762                 if (!(li->Handle)) {
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;
769                         return NULL;
770                 }
771         }
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");
776         if (!desc_func) {
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;
785         // Unload library
786                 dlclose(li->Handle);
787                 return NULL;
788         }
790         return desc_func;