1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Sonic Visualiser
5     An audio file viewer and annotation editor.
6     Centre for Digital Music, Queen Mary, University of London.
7 
8     This program is free software; you can redistribute it and/or
9     modify it under the terms of the GNU General Public License as
10     published by the Free Software Foundation; either version 2 of the
11     License, or (at your option) any later version.  See the file
12     COPYING included with this distribution for more information.
13 */
14 
15 /*
16    This is a modified version of a source file from the
17    Rosegarden MIDI and audio sequencer and notation editor.
18    This file copyright 2000-2006 Chris Cannam.
19 */
20 
21 #include "DSSIPluginFactory.h"
22 #include <iostream>
23 
24 #include <QString>
25 
26 #include "DSSIPluginInstance.h"
27 #include "PluginIdentifier.h"
28 
29 #include <cstdlib>
30 
31 #include "base/Profiler.h"
32 
33 //!!!
34 #include "plugin/plugins/SamplePlayer.h"
35 
36 #include "system/System.h"
37 
38 #ifdef HAVE_LRDF
39 #include "lrdf.h"
40 #endif // HAVE_LRDF
41 
42 using std::string;
43 
DSSIPluginFactory()44 DSSIPluginFactory::DSSIPluginFactory() :
45     LADSPAPluginFactory()
46 {
47     m_hostDescriptor.DSSI_API_Version = 2;
48     m_hostDescriptor.request_transport_information = nullptr;
49     m_hostDescriptor.request_midi_send = DSSIPluginInstance::requestMidiSend;
50     m_hostDescriptor.request_non_rt_thread = DSSIPluginInstance::requestNonRTThread;
51     m_hostDescriptor.midi_send = DSSIPluginInstance::midiSend;
52 }
53 
~DSSIPluginFactory()54 DSSIPluginFactory::~DSSIPluginFactory()
55 {
56     // nothing else to do here either
57 }
58 
59 void
enumeratePlugins(std::vector<QString> & list)60 DSSIPluginFactory::enumeratePlugins(std::vector<QString> &list)
61 {
62     Profiler profiler("DSSIPluginFactory::enumeratePlugins");
63 
64     for (std::vector<QString>::iterator i = m_identifiers.begin();
65          i != m_identifiers.end(); ++i) {
66 
67         const DSSI_Descriptor *ddesc = getDSSIDescriptor(*i);
68         if (!ddesc) continue;
69 
70         const LADSPA_Descriptor *descriptor = ddesc->LADSPA_Plugin;
71         if (!descriptor) continue;
72 
73 //        SVDEBUG << "DSSIPluginFactory::enumeratePlugins: Name " << (descriptor->Name ? descriptor->Name : "NONE" ) << endl;
74 
75         list.push_back(*i);
76         list.push_back(descriptor->Name);
77         list.push_back(QString("%1").arg(descriptor->UniqueID));
78         list.push_back(descriptor->Label);
79         list.push_back(descriptor->Maker);
80         list.push_back(descriptor->Copyright);
81         list.push_back((ddesc->run_synth || ddesc->run_multiple_synths) ? "true" : "false");
82         list.push_back(ddesc->run_multiple_synths ? "true" : "false");
83         list.push_back(m_taxonomy[*i]);
84         list.push_back(QString("%1").arg(descriptor->PortCount));
85 
86         for (int p = 0; p < (int)descriptor->PortCount; ++p) {
87 
88             int type = 0;
89             if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[p])) {
90                 type |= PortType::Control;
91             } else {
92                 type |= PortType::Audio;
93             }
94             if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[p])) {
95                 type |= PortType::Input;
96             } else {
97                 type |= PortType::Output;
98             }
99 
100             list.push_back(QString("%1").arg(p));
101             list.push_back(descriptor->PortNames[p]);
102             list.push_back(QString("%1").arg(type));
103             list.push_back(QString("%1").arg(getPortDisplayHint(descriptor, p)));
104             list.push_back(QString("%1").arg(getPortMinimum(descriptor, p)));
105             list.push_back(QString("%1").arg(getPortMaximum(descriptor, p)));
106             list.push_back(QString("%1").arg(getPortDefault(descriptor, p)));
107         }
108     }
109 
110     unloadUnusedLibraries();
111 }
112 
113 RealTimePluginInstance *
instantiatePlugin(QString identifier,int instrument,int position,sv_samplerate_t sampleRate,int blockSize,int channels)114 DSSIPluginFactory::instantiatePlugin(QString identifier,
115                                      int instrument,
116                                      int position,
117                                      sv_samplerate_t sampleRate,
118                                      int blockSize,
119                                      int channels)
120 {
121     Profiler profiler("DSSIPluginFactory::instantiatePlugin");
122 
123     const DSSI_Descriptor *descriptor = getDSSIDescriptor(identifier);
124 
125     if (descriptor) {
126 
127         DSSIPluginInstance *instance =
128             new DSSIPluginInstance
129             (this, instrument, identifier, position, sampleRate, blockSize, channels,
130              descriptor);
131 
132         m_instances.insert(instance);
133 
134         return instance;
135     }
136 
137     return nullptr;
138 }
139 
140 const DSSI_Descriptor *
getDSSIDescriptor(QString identifier)141 DSSIPluginFactory::getDSSIDescriptor(QString identifier)
142 {
143     QString type, soname, label;
144     PluginIdentifier::parseIdentifier(identifier, type, soname, label);
145 
146     if (soname == PluginIdentifier::BUILTIN_PLUGIN_SONAME) {
147         if (label == "sample_player") {
148             const DSSI_Descriptor *descriptor = SamplePlayer::getDescriptor(0);
149             if (descriptor) {
150                 descriptor->receive_host_descriptor(&m_hostDescriptor);
151             }
152             return descriptor;
153         } else {
154             return nullptr;
155         }
156     }
157 
158     bool firstInLibrary = false;
159 
160     if (m_libraryHandles.find(soname) == m_libraryHandles.end()) {
161         loadLibrary(soname);
162         if (m_libraryHandles.find(soname) == m_libraryHandles.end()) {
163             cerr << "WARNING: DSSIPluginFactory::getDSSIDescriptor: loadLibrary failed for " << soname << endl;
164             return nullptr;
165         }
166         firstInLibrary = true;
167     }
168 
169     void *libraryHandle = m_libraryHandles[soname];
170 
171     DSSI_Descriptor_Function fn = (DSSI_Descriptor_Function)
172         DLSYM(libraryHandle, "dssi_descriptor");
173 
174     if (!fn) {
175         cerr << "WARNING: DSSIPluginFactory::getDSSIDescriptor: No descriptor function in library " << soname << endl;
176         return nullptr;
177     }
178 
179     const DSSI_Descriptor *descriptor = nullptr;
180 
181     int index = 0;
182     while ((descriptor = fn(index))) {
183         if (descriptor->LADSPA_Plugin->Label == label) {
184             if (firstInLibrary && (descriptor->DSSI_API_Version >= 2)) {
185                 descriptor->receive_host_descriptor(&m_hostDescriptor);
186             }
187             return descriptor;
188         }
189         ++index;
190     }
191 
192     cerr << "WARNING: DSSIPluginFactory::getDSSIDescriptor: No such plugin as " << label << " in library " << soname << endl;
193 
194     return nullptr;
195 }
196 
197 const LADSPA_Descriptor *
getLADSPADescriptor(QString identifier)198 DSSIPluginFactory::getLADSPADescriptor(QString identifier)
199 {
200     const DSSI_Descriptor *dssiDescriptor = getDSSIDescriptor(identifier);
201     if (dssiDescriptor) return dssiDescriptor->LADSPA_Plugin;
202     else return nullptr;
203 }
204 
205 
206 std::vector<QString>
getPluginPath()207 DSSIPluginFactory::getPluginPath()
208 {
209     std::vector<QString> pathList;
210     string path;
211 
212     (void)getEnvUtf8("DSSI_PATH", path);
213 
214     if (path == "") {
215 
216         path = DEFAULT_DSSI_PATH;
217 
218         string home;
219         if (getEnvUtf8("HOME", home)) {
220             string::size_type f;
221             while ((f = path.find("$HOME")) != string::npos &&
222                    f < path.length()) {
223                 path.replace(f, 5, home);
224             }
225         }
226 
227 #ifdef _WIN32
228         string pfiles;
229         if (!getEnvUtf8("ProgramFiles", pfiles)) {
230             pfiles = "C:\\Program Files";
231         }
232 
233         string::size_type f;
234         while ((f = path.find("%ProgramFiles%")) != string::npos &&
235                f < path.length()) {
236             path.replace(f, 14, pfiles);
237         }
238 #endif
239     }
240 
241     string::size_type index = 0, newindex = 0;
242 
243     while ((newindex = path.find(PATH_SEPARATOR, index)) < path.size()) {
244         pathList.push_back(path.substr(index, newindex - index).c_str());
245         index = newindex + 1;
246     }
247 
248     pathList.push_back(path.substr(index).c_str());
249 
250     return pathList;
251 }
252 
253 
254 std::vector<QString>
getLRDFPath(QString & baseUri)255 DSSIPluginFactory::getLRDFPath(QString &baseUri)
256 {
257     std::vector<QString> lrdfPaths;
258 
259 #ifdef HAVE_LRDF
260     std::vector<QString> pathList = getPluginPath();
261 
262     lrdfPaths.push_back("/usr/local/share/dssi/rdf");
263     lrdfPaths.push_back("/usr/share/dssi/rdf");
264 
265     lrdfPaths.push_back("/usr/local/share/ladspa/rdf");
266     lrdfPaths.push_back("/usr/share/ladspa/rdf");
267 
268     for (std::vector<QString>::iterator i = pathList.begin();
269          i != pathList.end(); ++i) {
270         lrdfPaths.push_back(*i + "/rdf");
271     }
272 
273 #ifdef DSSI_BASE
274     baseUri = DSSI_BASE;
275 #else
276     baseUri = "http://dssi.sourceforge.net/ontology#";
277 #endif
278 #else
279     // avoid unused parameter
280     baseUri = "";
281 #endif
282 
283     return lrdfPaths;
284 }
285 
286 
287 void
discoverPluginsFrom(QString soname)288 DSSIPluginFactory::discoverPluginsFrom(QString soname)
289 {
290     Profiler profiler("DSSIPluginFactory::discoverPlugins");
291 
292     // Note that soname is expected to be a full path at this point,
293     // of a file that is known to exist
294 
295     void *libraryHandle = DLOPEN(soname, RTLD_LAZY);
296 
297     if (!libraryHandle) {
298         cerr << "WARNING: DSSIPluginFactory::discoverPlugins: couldn't load plugin library "
299                   << soname << " - " << DLERROR() << endl;
300         return;
301     }
302 
303     DSSI_Descriptor_Function fn = (DSSI_Descriptor_Function)
304         DLSYM(libraryHandle, "dssi_descriptor");
305 
306     if (!fn) {
307         cerr << "WARNING: DSSIPluginFactory::discoverPlugins: No descriptor function in " << soname << endl;
308         return;
309     }
310 
311     const DSSI_Descriptor *descriptor = nullptr;
312 
313     int index = 0;
314     while ((descriptor = fn(index))) {
315 
316         const LADSPA_Descriptor *ladspaDescriptor = descriptor->LADSPA_Plugin;
317         if (!ladspaDescriptor) {
318             cerr << "WARNING: DSSIPluginFactory::discoverPlugins: No LADSPA descriptor for plugin " << index << " in " << soname << endl;
319             ++index;
320             continue;
321         }
322 
323         RealTimePluginDescriptor *rtd = new RealTimePluginDescriptor;
324         rtd->name = ladspaDescriptor->Name;
325         rtd->label = ladspaDescriptor->Label;
326         rtd->maker = ladspaDescriptor->Maker;
327         rtd->copyright = ladspaDescriptor->Copyright;
328         rtd->category = "";
329         rtd->isSynth = (descriptor->run_synth ||
330                         descriptor->run_multiple_synths);
331         rtd->parameterCount = 0;
332         rtd->audioInputPortCount = 0;
333         rtd->audioOutputPortCount = 0;
334         rtd->controlOutputPortCount = 0;
335 
336         QString identifier = PluginIdentifier::createIdentifier
337             ("dssi", soname, ladspaDescriptor->Label);
338 
339 #ifdef HAVE_LRDF
340         char *def_uri = nullptr;
341         lrdf_defaults *defs = nullptr;
342 
343         QString category = m_taxonomy[identifier];
344 
345         if (category == "" && m_lrdfTaxonomy[ladspaDescriptor->UniqueID] != "") {
346             m_taxonomy[identifier] = m_lrdfTaxonomy[ladspaDescriptor->UniqueID];
347             category = m_taxonomy[identifier];
348         }
349 
350         if (category == "") {
351             string name = rtd->name;
352             if (name.length() > 4 &&
353                 name.substr(name.length() - 4) == " VST") {
354                 if (descriptor->run_synth || descriptor->run_multiple_synths) {
355                     category = "VST instruments";
356                 } else {
357                     category = "VST effects";
358                 }
359                 m_taxonomy[identifier] = category;
360             }
361         }
362 
363         rtd->category = category.toStdString();
364 
365 //        cerr << "Plugin id is " << ladspaDescriptor->UniqueID
366 //                  << ", identifier is \"" << identifier
367 //                  << "\", category is \"" << category
368 //                  << "\", name is " << ladspaDescriptor->Name
369 //                  << ", label is " << ladspaDescriptor->Label
370 //                  << endl;
371 
372         def_uri = lrdf_get_default_uri(ladspaDescriptor->UniqueID);
373         if (def_uri) {
374             defs = lrdf_get_setting_values(def_uri);
375         }
376 
377         unsigned int controlPortNumber = 1;
378 
379         for (int i = 0; i < (int)ladspaDescriptor->PortCount; i++) {
380 
381             if (LADSPA_IS_PORT_CONTROL(ladspaDescriptor->PortDescriptors[i])) {
382 
383                 if (def_uri && defs) {
384 
385                     for (int j = 0; j < (int)defs->count; j++) {
386                         if (defs->items[j].pid == controlPortNumber) {
387 //                            cerr << "Default for this port (" << defs->items[j].pid << ", " << defs->items[j].label << ") is " << defs->items[j].value << "; applying this to port number " << i << " with name " << ladspaDescriptor->PortNames[i] << endl;
388                             m_portDefaults[ladspaDescriptor->UniqueID][i] =
389                                 defs->items[j].value;
390                         }
391                     }
392                 }
393 
394                 ++controlPortNumber;
395             }
396         }
397 #endif // HAVE_LRDF
398 
399         for (unsigned long i = 0; i < ladspaDescriptor->PortCount; i++) {
400             if (LADSPA_IS_PORT_CONTROL(ladspaDescriptor->PortDescriptors[i])) {
401                 if (LADSPA_IS_PORT_INPUT(ladspaDescriptor->PortDescriptors[i])) {
402                     ++rtd->parameterCount;
403                 } else {
404                     if (strcmp(ladspaDescriptor->PortNames[i], "latency") &&
405                         strcmp(ladspaDescriptor->PortNames[i], "_latency")) {
406                         ++rtd->controlOutputPortCount;
407                         rtd->controlOutputPortNames.push_back
408                             (ladspaDescriptor->PortNames[i]);
409                     }
410                 }
411             } else {
412                 if (LADSPA_IS_PORT_INPUT(ladspaDescriptor->PortDescriptors[i])) {
413                     ++rtd->audioInputPortCount;
414                 } else if (LADSPA_IS_PORT_OUTPUT(ladspaDescriptor->PortDescriptors[i])) {
415                     ++rtd->audioOutputPortCount;
416                 }
417             }
418         }
419 
420         m_identifiers.push_back(identifier);
421 
422         m_libraries[identifier] = soname;
423 
424         m_rtDescriptors[identifier] = rtd;
425 
426         ++index;
427     }
428 
429     if (DLCLOSE(libraryHandle) != 0) {
430         cerr << "WARNING: DSSIPluginFactory::discoverPlugins - can't unload " << libraryHandle << endl;
431         return;
432     }
433 }
434 
435 
436 
437