fix various issues with AU ID handling
[ardour2.git] / libs / ardour / plugin_manager.cc
blobf45ef27b36f01567761b82555aaab3fdab609590
1 /*
2 Copyright (C) 2000-2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include <sys/types.h>
21 #include <cstdio>
22 #include <lrdf.h>
23 #include <dlfcn.h>
24 #include <cstdlib>
25 #include <fstream>
27 #ifdef VST_SUPPORT
28 #include <fst.h>
29 #include <pbd/basename.h>
30 #include <cstring>
31 #endif // VST_SUPPORT
33 #include <glibmm/miscutils.h>
35 #include <pbd/pathscanner.h>
36 #include <pbd/whitespace.h>
38 #include <ardour/ladspa.h>
39 #include <ardour/session.h>
40 #include <ardour/plugin_manager.h>
41 #include <ardour/plugin.h>
42 #include <ardour/ladspa_plugin.h>
44 #ifdef HAVE_SLV2
45 #include <slv2/slv2.h>
46 #include <ardour/lv2_plugin.h>
47 #endif
49 #ifdef VST_SUPPORT
50 #include <ardour/vst_plugin.h>
51 #endif
53 #ifdef HAVE_AUDIOUNITS
54 #include <ardour/audio_unit.h>
55 #include <Carbon/Carbon.h>
56 #endif
58 #include <pbd/error.h>
59 #include <pbd/stl_delete.h>
61 #include "i18n.h"
63 using namespace ARDOUR;
64 using namespace PBD;
65 using namespace std;
67 PluginManager* PluginManager::_manager = 0;
69 PluginManager::PluginManager ()
71 char* s;
72 string lrdf_path;
74 load_statuses ();
76 #ifdef GTKOSX
77 ProcessSerialNumber psn = { 0, kCurrentProcess };
78 OSStatus returnCode = TransformProcessType(& psn, kProcessTransformToForegroundApplication);
79 if( returnCode != 0) {
80 error << _("Cannot become GUI app") << endmsg;
82 #endif
84 if ((s = getenv ("LADSPA_RDF_PATH"))){
85 lrdf_path = s;
88 if (lrdf_path.length() == 0) {
89 lrdf_path = "/usr/local/share/ladspa/rdf:/usr/share/ladspa/rdf";
92 add_lrdf_data(lrdf_path);
93 add_ladspa_presets();
94 #ifdef VST_SUPPORT
95 if (Config->get_use_vst()) {
96 add_vst_presets();
98 #endif /* VST_SUPPORT */
100 if ((s = getenv ("LADSPA_PATH"))) {
101 ladspa_path = s;
104 if ((s = getenv ("VST_PATH"))) {
105 vst_path = s;
106 } else if ((s = getenv ("VST_PLUGINS"))) {
107 vst_path = s;
110 if (_manager == 0) {
111 _manager = this;
114 /* the plugin manager is constructed too early to use Profile */
116 if (getenv ("ARDOUR_SAE")) {
117 ladspa_plugin_whitelist.push_back (1203); // single band parametric
118 ladspa_plugin_whitelist.push_back (1772); // caps compressor
119 ladspa_plugin_whitelist.push_back (1913); // fast lookahead limiter
120 ladspa_plugin_whitelist.push_back (1075); // simple RMS expander
121 ladspa_plugin_whitelist.push_back (1061); // feedback delay line (max 5s)
122 ladspa_plugin_whitelist.push_back (1216); // gverb
123 ladspa_plugin_whitelist.push_back (2150); // tap pitch shifter
126 #ifdef HAVE_SLV2
127 _lv2_world = new LV2World();
128 #endif
130 BootMessage (_("Discovering Plugins"));
132 refresh ();
135 void
136 PluginManager::refresh ()
138 ladspa_refresh ();
139 #ifdef HAVE_SLV2
140 lv2_refresh ();
141 #endif
142 #ifdef VST_SUPPORT
143 if (Config->get_use_vst()) {
144 vst_refresh ();
146 #endif // VST_SUPPORT
147 #ifdef HAVE_AUDIOUNITS
148 au_refresh ();
149 #endif
152 void
153 PluginManager::ladspa_refresh ()
155 _ladspa_plugin_info.clear ();
157 static const char *standard_paths[] = {
158 "/usr/local/lib64/ladspa",
159 "/usr/local/lib/ladspa",
160 "/usr/lib64/ladspa",
161 "/usr/lib/ladspa",
162 "/Library/Audio/Plug-Ins/LADSPA",
166 /* allow LADSPA_PATH to augment, not override standard locations */
168 /* Only add standard locations to ladspa_path if it doesn't
169 * already contain them. Check for trailing '/'s too.
172 int i;
173 for (i = 0; standard_paths[i][0]; i++) {
174 size_t found = ladspa_path.find(standard_paths[i]);
175 if (found != ladspa_path.npos) {
176 switch (ladspa_path[found + strlen(standard_paths[i])]) {
177 case ':' :
178 case '\0':
179 continue;
180 case '/' :
181 if (ladspa_path[found + strlen(standard_paths[i]) + 1] == ':' ||
182 ladspa_path[found + strlen(standard_paths[i]) + 1] == '\0') {
183 continue;
187 if (!ladspa_path.empty())
188 ladspa_path += ":";
190 ladspa_path += standard_paths[i];
194 ladspa_discover_from_path (ladspa_path);
199 PluginManager::add_ladspa_directory (string path)
201 if (ladspa_discover_from_path (path) == 0) {
202 ladspa_path += ':';
203 ladspa_path += path;
204 return 0;
206 return -1;
209 static bool ladspa_filter (const string& str, void *arg)
211 /* Not a dotfile, has a prefix before a period, suffix is "so" */
213 return str[0] != '.' && (str.length() > 3 && str.find (".so") == (str.length() - 3));
217 PluginManager::ladspa_discover_from_path (string path)
219 PathScanner scanner;
220 vector<string *> *plugin_objects;
221 vector<string *>::iterator x;
222 int ret = 0;
224 plugin_objects = scanner (ladspa_path, ladspa_filter, 0, true, true);
226 if (plugin_objects) {
227 for (x = plugin_objects->begin(); x != plugin_objects->end (); ++x) {
228 ladspa_discover (**x);
232 vector_delete (plugin_objects);
233 return ret;
236 static bool rdf_filter (const string &str, void *arg)
238 return str[0] != '.' &&
239 ((str.find(".rdf") == (str.length() - 4)) ||
240 (str.find(".rdfs") == (str.length() - 5)) ||
241 (str.find(".n3") == (str.length() - 3)));
244 void
245 PluginManager::add_ladspa_presets()
247 add_presets ("ladspa");
250 void
251 PluginManager::add_vst_presets()
253 add_presets ("vst");
255 void
256 PluginManager::add_presets(string domain)
259 PathScanner scanner;
260 vector<string *> *presets;
261 vector<string *>::iterator x;
263 char* envvar;
264 if ((envvar = getenv ("HOME")) == 0) {
265 return;
268 string path = string_compose("%1/.%2/rdf", envvar, domain);
269 presets = scanner (path, rdf_filter, 0, true, true);
271 if (presets) {
272 for (x = presets->begin(); x != presets->end (); ++x) {
273 string file = "file:" + **x;
274 if (lrdf_read_file(file.c_str())) {
275 warning << string_compose(_("Could not parse rdf file: %1"), *x) << endmsg;
280 vector_delete (presets);
283 void
284 PluginManager::add_lrdf_data (const string &path)
286 PathScanner scanner;
287 vector<string *>* rdf_files;
288 vector<string *>::iterator x;
289 string uri;
291 rdf_files = scanner (path, rdf_filter, 0, true, true);
293 if (rdf_files) {
294 for (x = rdf_files->begin(); x != rdf_files->end (); ++x) {
295 uri = "file://" + **x;
297 if (lrdf_read_file(uri.c_str())) {
298 warning << "Could not parse rdf file: " << uri << endmsg;
303 vector_delete (rdf_files);
306 int
307 PluginManager::ladspa_discover (string path)
309 void *module;
310 const LADSPA_Descriptor *descriptor;
311 LADSPA_Descriptor_Function dfunc;
312 const char *errstr;
314 if ((module = dlopen (path.c_str(), RTLD_NOW)) == 0) {
315 error << string_compose(_("LADSPA: cannot load module \"%1\" (%2)"), path, dlerror()) << endmsg;
316 return -1;
319 dfunc = (LADSPA_Descriptor_Function) dlsym (module, "ladspa_descriptor");
321 if ((errstr = dlerror()) != 0) {
322 error << string_compose(_("LADSPA: module \"%1\" has no descriptor function."), path) << endmsg;
323 error << errstr << endmsg;
324 dlclose (module);
325 return -1;
328 for (uint32_t i = 0; ; ++i) {
329 if ((descriptor = dfunc (i)) == 0) {
330 break;
333 if (!ladspa_plugin_whitelist.empty()) {
334 if (find (ladspa_plugin_whitelist.begin(), ladspa_plugin_whitelist.end(), descriptor->UniqueID) == ladspa_plugin_whitelist.end()) {
335 continue;
339 PluginInfoPtr info(new LadspaPluginInfo);
340 info->name = descriptor->Name;
341 info->category = get_ladspa_category(descriptor->UniqueID);
342 info->creator = descriptor->Maker;
343 info->path = path;
344 info->index = i;
345 info->n_inputs = 0;
346 info->n_outputs = 0;
347 info->type = ARDOUR::LADSPA;
349 char buf[32];
350 snprintf (buf, sizeof (buf), "%lu", descriptor->UniqueID);
351 info->unique_id = buf;
353 for (uint32_t n=0; n < descriptor->PortCount; ++n) {
354 if ( LADSPA_IS_PORT_AUDIO (descriptor->PortDescriptors[n]) ) {
355 if ( LADSPA_IS_PORT_INPUT (descriptor->PortDescriptors[n]) ) {
356 info->n_inputs++;
358 else if ( LADSPA_IS_PORT_OUTPUT (descriptor->PortDescriptors[n]) ) {
359 info->n_outputs++;
364 if(_ladspa_plugin_info.empty()){
365 _ladspa_plugin_info.push_back (info);
368 //Ensure that the plugin is not already in the plugin list.
370 bool found = false;
372 for (PluginInfoList::const_iterator i = _ladspa_plugin_info.begin(); i != _ladspa_plugin_info.end(); ++i) {
373 if(0 == info->unique_id.compare((*i)->unique_id)){
374 found = true;
378 if(!found){
379 _ladspa_plugin_info.push_back (info);
383 // GDB WILL NOT LIKE YOU IF YOU DO THIS
384 // dlclose (module);
386 return 0;
389 string
390 PluginManager::get_ladspa_category (uint32_t plugin_id)
392 char buf[256];
393 lrdf_statement pattern;
395 snprintf(buf, sizeof(buf), "%s%" PRIu32, LADSPA_BASE, plugin_id);
396 pattern.subject = buf;
397 pattern.predicate = (char*)RDF_TYPE;
398 pattern.object = 0;
399 pattern.object_type = lrdf_uri;
401 lrdf_statement* matches1 = lrdf_matches (&pattern);
403 if (!matches1) {
404 return "Unknown";
407 pattern.subject = matches1->object;
408 pattern.predicate = (char*)(LADSPA_BASE "hasLabel");
409 pattern.object = 0;
410 pattern.object_type = lrdf_literal;
412 lrdf_statement* matches2 = lrdf_matches (&pattern);
413 lrdf_free_statements(matches1);
415 if (!matches2) {
416 return ("Unknown");
419 string label = matches2->object;
420 lrdf_free_statements(matches2);
422 return label;
425 #ifdef HAVE_SLV2
426 void
427 PluginManager::lv2_refresh ()
429 lv2_discover();
433 PluginManager::lv2_discover ()
435 _lv2_plugin_info = LV2PluginInfo::discover(_lv2_world);
436 return 0;
438 #endif
440 #ifdef HAVE_AUDIOUNITS
441 void
442 PluginManager::au_refresh ()
444 au_discover();
448 PluginManager::au_discover ()
450 _au_plugin_info = AUPluginInfo::discover();
451 return 0;
454 #endif
456 #ifdef VST_SUPPORT
458 void
459 PluginManager::vst_refresh ()
461 _vst_plugin_info.clear ();
463 if (vst_path.length() == 0) {
464 vst_path = "/usr/local/lib/vst:/usr/lib/vst";
467 vst_discover_from_path (vst_path);
471 PluginManager::add_vst_directory (string path)
473 if (vst_discover_from_path (path) == 0) {
474 vst_path += ':';
475 vst_path += path;
476 return 0;
478 return -1;
481 static bool vst_filter (const string& str, void *arg)
483 /* Not a dotfile, has a prefix before a period, suffix is "dll" */
485 return str[0] != '.' && (str.length() > 4 && str.find (".dll") == (str.length() - 4));
489 PluginManager::vst_discover_from_path (string path)
491 PathScanner scanner;
492 vector<string *> *plugin_objects;
493 vector<string *>::iterator x;
494 int ret = 0;
496 info << "detecting VST plugins along " << path << endmsg;
498 plugin_objects = scanner (vst_path, vst_filter, 0, true, true);
500 if (plugin_objects) {
501 for (x = plugin_objects->begin(); x != plugin_objects->end (); ++x) {
502 std::cerr << " discovering " << **x << std::endl;
503 vst_discover (**x);
507 vector_delete (plugin_objects);
508 return ret;
512 PluginManager::vst_discover (string path)
514 FSTInfo* finfo;
515 char buf[32];
517 if ((finfo = fst_get_info (const_cast<char *> (path.c_str()))) == 0) {
518 warning << "Cannot get VST information from " << path << endmsg;
519 return -1;
522 if (!finfo->canProcessReplacing) {
523 warning << string_compose (_("VST plugin %1 does not support processReplacing, and so cannot be used in ardour at this time"),
524 finfo->name)
525 << endl;
528 PluginInfoPtr info(new VSTPluginInfo);
530 /* what a goddam joke freeware VST is */
532 if (!strcasecmp ("The Unnamed plugin", finfo->name)) {
533 info->name = PBD::basename_nosuffix (path);
534 } else {
535 info->name = finfo->name;
539 snprintf (buf, sizeof (buf), "%d", finfo->UniqueID);
540 info->unique_id = buf;
541 info->category = "VST";
542 info->path = path;
543 info->creator = finfo->creator;
544 info->index = 0;
545 info->n_inputs = finfo->numInputs;
546 info->n_outputs = finfo->numOutputs;
547 info->type = ARDOUR::VST;
549 _vst_plugin_info.push_back (info);
550 fst_free_info (finfo);
552 return 0;
555 #endif // VST_SUPPORT
557 PluginManager::PluginStatusType
558 PluginManager::get_status (const PluginInfoPtr& pi)
560 PluginStatus ps (pi->type, pi->unique_id);
561 PluginStatusList::const_iterator i = find (statuses.begin(), statuses.end(), ps);
562 if (i == statuses.end() ) {
563 return Normal;
564 } else {
565 return i->status;
569 void
570 PluginManager::save_statuses ()
572 Glib::ustring path = Glib::build_filename (get_user_ardour_path (), "plugin_statuses");
573 ofstream ofs;
575 ofs.open (path.c_str(), ios_base::openmode (ios::out|ios::trunc));
577 if (!ofs) {
578 return;
581 for (PluginStatusList::iterator i = statuses.begin(); i != statuses.end(); ++i) {
582 switch ((*i).type) {
583 case LADSPA:
584 ofs << "LADSPA";
585 break;
586 case AudioUnit:
587 ofs << "AudioUnit";
588 break;
589 case LV2:
590 ofs << "LV2";
591 break;
592 case VST:
593 ofs << "VST";
594 break;
597 ofs << ' ';
599 switch ((*i).status) {
600 case Normal:
601 ofs << "Normal";
602 break;
603 case Favorite:
604 ofs << "Favorite";
605 break;
606 case Hidden:
607 ofs << "Hidden";
608 break;
611 ofs << ' ';
613 ofs << (*i).unique_id;
615 ofs << endl;
618 ofs.close ();
621 void
622 PluginManager::load_statuses ()
624 Glib::ustring path = Glib::build_filename (get_user_ardour_path (),"plugin_statuses");
626 ifstream ifs (path.c_str());
628 if (!ifs) {
629 return;
632 std::string stype;
633 std::string sstatus;
634 std::string id;
635 PluginType type;
636 PluginStatusType status;
637 char buf[1024];
639 while (ifs) {
641 ifs >> stype;
642 if (!ifs) {
643 break;
647 ifs >> sstatus;
648 if (!ifs) {
649 break;
653 /* rest of the line is the plugin ID */
655 ifs.getline (buf, sizeof (buf), '\n');
656 if (!ifs) {
657 break;
660 if (sstatus == "Normal") {
661 status = Normal;
662 } else if (sstatus == "Favorite") {
663 status = Favorite;
664 } else if (sstatus == "Hidden") {
665 status = Hidden;
666 } else {
667 error << string_compose (_("unknown plugin status type \"%1\" - all entries ignored"), sstatus)
668 << endmsg;
669 statuses.clear ();
670 break;
673 if (stype == "LADSPA") {
674 type = LADSPA;
675 } else if (stype == "AudioUnit") {
676 type = AudioUnit;
677 } else if (stype == "LV2") {
678 type = LV2;
679 } else if (stype == "VST") {
680 type = VST;
681 } else {
682 error << string_compose (_("unknown plugin type \"%1\" - ignored"), stype)
683 << endmsg;
684 continue;
687 id = buf;
688 strip_whitespace_edges (id);
690 #ifdef HAVE_AUDIOUNITS
691 if (type == AudioUnit) {
692 string fixed = AUPlugin::maybe_fix_broken_au_id (id);
693 if (fixed.empty()) {
694 error << string_compose (_("Your favorite plugin list contains an AU plugin whose ID cannot be understood - ignored (%1)"), id) << endmsg;
695 continue;
697 id = fixed;
699 #endif
700 set_status (type, id, status);
703 ifs.close ();
706 void
707 PluginManager::set_status (PluginType t, string id, PluginStatusType status)
709 PluginStatus ps (t, id, status);
710 statuses.erase (ps);
712 if (status == Normal) {
713 return;
716 pair<PluginStatusList::iterator, bool> res = statuses.insert (ps);