svcore  1.9
FeatureExtractionPluginFactory.cpp
Go to the documentation of this file.
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  This file copyright 2006 Chris Cannam and QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
17 #include "PluginIdentifier.h"
18 
19 #include <vamp-hostsdk/PluginHostAdapter.h>
20 #include <vamp-hostsdk/PluginWrapper.h>
21 
22 #include "system/System.h"
23 
24 #include <QDir>
25 #include <QFile>
26 #include <QFileInfo>
27 #include <QTextStream>
28 
29 #include <iostream>
30 
31 #include "base/Profiler.h"
32 
33 //#define DEBUG_PLUGIN_SCAN_AND_INSTANTIATE 1
34 
35 class PluginDeletionNotifyAdapter : public Vamp::HostExt::PluginWrapper {
36 public:
37  PluginDeletionNotifyAdapter(Vamp::Plugin *plugin,
39  PluginWrapper(plugin), m_factory(factory) { }
41 protected:
43 };
44 
46 {
47  // see notes in vamp-sdk/hostext/PluginLoader.cpp from which this is drawn
48  Vamp::Plugin *p = m_plugin;
49  delete m_plugin;
50  m_plugin = 0;
51  // acceptable use after free here, as pluginDeleted uses p only as
52  // pointer key and does not deref it
54 }
55 
57 
60 {
61  if (pluginType == "vamp") {
62  if (!_nativeInstance) {
63 // SVDEBUG << "FeatureExtractionPluginFactory::instance(" << pluginType// << "): creating new FeatureExtractionPluginFactory" << endl;
65  }
66  return _nativeInstance;
67  }
68 
69  else return 0;
70 }
71 
74 {
75  QString type, soName, label;
76  PluginIdentifier::parseIdentifier(identifier, type, soName, label);
77  return instance(type);
78 }
79 
80 std::vector<QString>
82 {
83  if (!m_pluginPath.empty()) return m_pluginPath;
84 
85  std::vector<std::string> p = Vamp::PluginHostAdapter::getPluginPath();
86  for (size_t i = 0; i < p.size(); ++i) m_pluginPath.push_back(p[i].c_str());
87  return m_pluginPath;
88 }
89 
90 std::vector<QString>
92 {
94  std::vector<QString> rv;
95 
96  factory = instance("vamp");
97  if (factory) {
98  std::vector<QString> tmp = factory->getPluginIdentifiers();
99  for (size_t i = 0; i < tmp.size(); ++i) {
100 // cerr << "identifier: " << tmp[i] << endl;
101  rv.push_back(tmp[i]);
102  }
103  }
104 
105  // Plugins can change the locale, revert it to default.
107 
108  return rv;
109 }
110 
111 std::vector<QString>
113 {
114  Profiler profiler("FeatureExtractionPluginFactory::getPluginIdentifiers");
115 
116  std::vector<QString> rv;
117  std::vector<QString> path = getPluginPath();
118 
119  for (std::vector<QString>::iterator i = path.begin(); i != path.end(); ++i) {
120 
121 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
122  SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: scanning directory " << i-<< endl;
123 #endif
124 
125  QDir pluginDir(*i, PLUGIN_GLOB,
126  QDir::Name | QDir::IgnoreCase,
127  QDir::Files | QDir::Readable);
128 
129  for (unsigned int j = 0; j < pluginDir.count(); ++j) {
130 
131  QString soname = pluginDir.filePath(pluginDir[j]);
132 
133 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
134  SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: trying potential library " << soname << endl;
135 #endif
136 
137  void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL);
138 
139  if (!libraryHandle) {
140  cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to load library " << soname << ": " << DLERROR() << endl;
141  continue;
142  }
143 
144 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
145  SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: It's a library all right, checking for descriptor" << endl;
146 #endif
147 
148  VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
149  DLSYM(libraryHandle, "vampGetPluginDescriptor");
150 
151  if (!fn) {
152  cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: No descriptor function in " << soname << endl;
153  if (DLCLOSE(libraryHandle) != 0) {
154  cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl;
155  }
156  continue;
157  }
158 
159 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
160  SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: Vamp descriptor found" << endl;
161 #endif
162 
163  const VampPluginDescriptor *descriptor = 0;
164  int index = 0;
165 
166  std::map<std::string, int> known;
167  bool ok = true;
168 
169  while ((descriptor = fn(VAMP_API_VERSION, index))) {
170 
171  if (known.find(descriptor->identifier) != known.end()) {
172  cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Plugin library "
173  << soname
174  << " returns the same plugin identifier \""
175  << descriptor->identifier << "\" at indices "
176  << known[descriptor->identifier] << " and "
177  << index << endl;
178  SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: Avoiding this library (obsolete API?)" << endl;
179  ok = false;
180  break;
181  } else {
182  known[descriptor->identifier] = index;
183  }
184 
185  ++index;
186  }
187 
188  if (ok) {
189 
190  index = 0;
191 
192  while ((descriptor = fn(VAMP_API_VERSION, index))) {
193 
195  ("vamp", soname, descriptor->identifier);
196  rv.push_back(id);
197 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
198  SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: Found plugin id " << id << " at index " << index << endl;
199 #endif
200  ++index;
201  }
202  }
203 
204  if (DLCLOSE(libraryHandle) != 0) {
205  cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl;
206  }
207  }
208  }
209 
211 
212  return rv;
213 }
214 
215 QString
217 {
218  QString file = "";
219 
220 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
221  SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile(\""
222  << soname << "\", \"" << inDir << "\")"
223  << endl;
224 #endif
225 
226  if (inDir != "") {
227 
228  QDir dir(inDir, PLUGIN_GLOB,
229  QDir::Name | QDir::IgnoreCase,
230  QDir::Files | QDir::Readable);
231  if (!dir.exists()) return "";
232 
233  file = dir.filePath(QFileInfo(soname).fileName());
234 
235  if (QFileInfo(file).exists() && QFileInfo(file).isFile()) {
236 
237 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
238  SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile: "
239  << "found trivially at " << file << endl;
240 #endif
241 
242  return file;
243  }
244 
245  for (unsigned int j = 0; j < dir.count(); ++j) {
246  file = dir.filePath(dir[j]);
247  if (QFileInfo(file).baseName() == QFileInfo(soname).baseName()) {
248 
249 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
250  SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile: "
251  << "found \"" << soname << "\" at " << file << endl;
252 #endif
253 
254  return file;
255  }
256  }
257 
258 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
259  SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile (with dir): "
260  << "not found" << endl;
261 #endif
262 
263  return "";
264 
265  } else {
266 
267  QFileInfo fi(soname);
268 
269  if (fi.isAbsolute() && fi.exists() && fi.isFile()) {
270 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
271  SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile: "
272  << "found trivially at " << soname << endl;
273 #endif
274  return soname;
275  }
276 
277  if (fi.isAbsolute() && fi.absolutePath() != "") {
278  file = findPluginFile(soname, fi.absolutePath());
279  if (file != "") return file;
280  }
281 
282  std::vector<QString> path = getPluginPath();
283  for (std::vector<QString>::iterator i = path.begin();
284  i != path.end(); ++i) {
285  if (*i != "") {
286  file = findPluginFile(soname, *i);
287  if (file != "") return file;
288  }
289  }
290 
291 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
292  SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile: "
293  << "not found" << endl;
294 #endif
295 
296  return "";
297  }
298 }
299 
300 Vamp::Plugin *
302  float inputSampleRate)
303 {
304  Profiler profiler("FeatureExtractionPluginFactory::instantiatePlugin");
305 
306  Vamp::Plugin *rv = 0;
307  Vamp::PluginHostAdapter *plugin = 0;
308 
309  const VampPluginDescriptor *descriptor = 0;
310  int index = 0;
311 
312  QString type, soname, label;
313  PluginIdentifier::parseIdentifier(identifier, type, soname, label);
314  if (type != "vamp") {
315  SVDEBUG << "FeatureExtractionPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type << endl;
316  return 0;
317  }
318 
319  QString found = findPluginFile(soname);
320 
321  if (found == "") {
322  cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find library file " << soname << endl;
323  return 0;
324  } else if (found != soname) {
325 
326 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
327  SVDEBUG << "FeatureExtractionPluginFactory::instantiatePlugin: Given library name was " << soname << ", found at " << found << endl;
328  cerr << soname << " -> " << found << endl;
329 #endif
330 
331  }
332 
333  soname = found;
334 
335  void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL);
336 
337  if (!libraryHandle) {
338  cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to load library " << soname << ": " << DLERROR() << endl;
339  return 0;
340  }
341 
342  VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
343  DLSYM(libraryHandle, "vampGetPluginDescriptor");
344 
345  if (!fn) {
346  SVDEBUG << "FeatureExtractionPluginFactory::instantiatePlugin: No descriptor function in " << soname << endl;
347  goto done;
348  }
349 
350  while ((descriptor = fn(VAMP_API_VERSION, index))) {
351  if (label == descriptor->identifier) break;
352  ++index;
353  }
354 
355  if (!descriptor) {
356  cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find plugin \"" << label << "\" in library " << soname << endl;
357  goto done;
358  }
359 
360  plugin = new Vamp::PluginHostAdapter(descriptor, inputSampleRate);
361 
362  if (plugin) {
363  m_handleMap[plugin] = libraryHandle;
364  rv = new PluginDeletionNotifyAdapter(plugin, this);
365  }
366 
367 // SVDEBUG << "FeatureExtractionPluginFactory::instantiatePlugin: Constructed Vamp plugin, rv is " << rv << endl;
368 
370 
371 done:
372  if (!rv) {
373  if (DLCLOSE(libraryHandle) != 0) {
374  cerr << "WARNING: FeatureExtractionPluginFactory::instantiatePlugin: Failed to unload library " << soname << endl;
375  }
376  }
377 
378 // SVDEBUG << "FeatureExtractionPluginFactory::instantiatePlugin: Instantiated plugin " << label << " from library " << soname << ": descriptor " << descriptor << ", rv "<< rv << ", label " << rv->getName() << ", outputs " << rv->getOutputDescriptors().size() << endl;
379 
380  return rv;
381 }
382 
383 void
385 {
386  void *handle = m_handleMap[plugin];
387  if (handle) {
388 // SVDEBUG << "unloading library " << handle << " for plugin " << plugin << endl;
389  DLCLOSE(handle);
390  }
391  m_handleMap.erase(plugin);
392 }
393 
394 QString
396 {
397  return m_taxonomy[identifier];
398 }
399 
400 void
402 {
403  std::vector<QString> pluginPath = getPluginPath();
404  std::vector<QString> path;
405 
406  for (size_t i = 0; i < pluginPath.size(); ++i) {
407  if (pluginPath[i].contains("/lib/")) {
408  QString p(pluginPath[i]);
409  path.push_back(p);
410  p.replace("/lib/", "/share/");
411  path.push_back(p);
412  }
413  path.push_back(pluginPath[i]);
414  }
415 
416  for (size_t i = 0; i < path.size(); ++i) {
417 
418  QDir dir(path[i], "*.cat");
419 
420 // SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: directory " << path[i] << " has " << dir.count() << " .cat files" << endl;
421  for (unsigned int j = 0; j < dir.count(); ++j) {
422 
423  QFile file(path[i] + "/" + dir[j]);
424 
425 // SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: about to open " << (path[i]+ "/" + dir[j]) << endl;
426 
427  if (file.open(QIODevice::ReadOnly)) {
428 // cerr << "...opened" << endl;
429  QTextStream stream(&file);
430  QString line;
431 
432  while (!stream.atEnd()) {
433  line = stream.readLine();
434 // cerr << "line is: \"" << line << "\"" << endl;
435  QString id = PluginIdentifier::canonicalise
436  (line.section("::", 0, 0));
437  QString cat = line.section("::", 1, 1);
438  m_taxonomy[id] = cat;
439 // cerr << "FeatureExtractionPluginFactory: set id \"" << id << "\" to cat \"" << cat << "\"" << endl;
440  }
441  }
442  }
443  }
444 }
#define DLERROR()
Definition: System.h:89
virtual Vamp::Plugin * instantiatePlugin(QString identifier, float inputSampleRate)
#define DLOPEN(a, b)
Definition: System.h:86
FeatureExtractionPluginFactory * m_factory
static QString canonicalise(QString identifier)
virtual QString getPluginCategory(QString identifier)
Get category metadata about a plugin (without instantiating it).
static void parseIdentifier(QString identifier, QString &type, QString &soName, QString &label)
static QString createIdentifier(QString type, QString soName, QString label)
virtual std::vector< QString > getPluginPath()
static FeatureExtractionPluginFactory * instance(QString pluginType)
#define DLCLOSE(a)
Definition: System.h:88
#define PLUGIN_GLOB
Definition: System.h:123
PluginDeletionNotifyAdapter(Vamp::Plugin *plugin, FeatureExtractionPluginFactory *factory)
void RestoreStartupLocale()
Definition: System.cpp:313
#define SVDEBUG
Definition: Debug.h:42
virtual QString findPluginFile(QString soname, QString inDir="")
static FeatureExtractionPluginFactory * _nativeInstance
static std::vector< QString > getAllPluginIdentifiers()
virtual std::vector< QString > getPluginIdentifiers()
static FeatureExtractionPluginFactory * instanceFor(QString identifier)
std::map< Vamp::Plugin *, void * > m_handleMap
#define DLSYM(a, b)
Definition: System.h:87
Profile point instance class.
Definition: Profiler.h:86