svcore  1.9
FeatureExtractionPluginFactory.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006 Chris Cannam and QMUL.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "FeatureExtractionPluginFactory.h"
00017 #include "PluginIdentifier.h"
00018 
00019 #include <vamp-hostsdk/PluginHostAdapter.h>
00020 #include <vamp-hostsdk/PluginWrapper.h>
00021 
00022 #include "system/System.h"
00023 
00024 #include <QDir>
00025 #include <QFile>
00026 #include <QFileInfo>
00027 #include <QTextStream>
00028 
00029 #include <iostream>
00030 
00031 #include "base/Profiler.h"
00032 
00033 //#define DEBUG_PLUGIN_SCAN_AND_INSTANTIATE 1
00034 
00035 class PluginDeletionNotifyAdapter : public Vamp::HostExt::PluginWrapper {
00036 public:
00037     PluginDeletionNotifyAdapter(Vamp::Plugin *plugin,
00038                                 FeatureExtractionPluginFactory *factory) :
00039         PluginWrapper(plugin), m_factory(factory) { }
00040     virtual ~PluginDeletionNotifyAdapter();
00041 protected:
00042     FeatureExtractionPluginFactory *m_factory;
00043 };
00044 
00045 PluginDeletionNotifyAdapter::~PluginDeletionNotifyAdapter()
00046 {
00047     // see notes in vamp-sdk/hostext/PluginLoader.cpp from which this is drawn
00048     Vamp::Plugin *p = m_plugin;
00049     delete m_plugin;
00050     m_plugin = 0;
00051     // acceptable use after free here, as pluginDeleted uses p only as
00052     // pointer key and does not deref it
00053     if (m_factory) m_factory->pluginDeleted(p);
00054 }
00055 
00056 static FeatureExtractionPluginFactory *_nativeInstance = 0;
00057 
00058 FeatureExtractionPluginFactory *
00059 FeatureExtractionPluginFactory::instance(QString pluginType)
00060 {
00061     if (pluginType == "vamp") {
00062         if (!_nativeInstance) {
00063 //          SVDEBUG << "FeatureExtractionPluginFactory::instance(" << pluginType//                    << "): creating new FeatureExtractionPluginFactory" << endl;
00064             _nativeInstance = new FeatureExtractionPluginFactory();
00065         }
00066         return _nativeInstance;
00067     }
00068 
00069     else return 0;
00070 }
00071 
00072 FeatureExtractionPluginFactory *
00073 FeatureExtractionPluginFactory::instanceFor(QString identifier)
00074 {
00075     QString type, soName, label;
00076     PluginIdentifier::parseIdentifier(identifier, type, soName, label);
00077     return instance(type);
00078 }
00079 
00080 std::vector<QString>
00081 FeatureExtractionPluginFactory::getPluginPath()
00082 {
00083     if (!m_pluginPath.empty()) return m_pluginPath;
00084 
00085     std::vector<std::string> p = Vamp::PluginHostAdapter::getPluginPath();
00086     for (size_t i = 0; i < p.size(); ++i) m_pluginPath.push_back(p[i].c_str());
00087     return m_pluginPath;
00088 }
00089 
00090 std::vector<QString>
00091 FeatureExtractionPluginFactory::getAllPluginIdentifiers()
00092 {
00093     FeatureExtractionPluginFactory *factory;
00094     std::vector<QString> rv;
00095     
00096     factory = instance("vamp");
00097     if (factory) {
00098         std::vector<QString> tmp = factory->getPluginIdentifiers();
00099         for (size_t i = 0; i < tmp.size(); ++i) {
00100 //            cerr << "identifier: " << tmp[i] << endl;
00101             rv.push_back(tmp[i]);
00102         }
00103     }
00104 
00105     // Plugins can change the locale, revert it to default.
00106     RestoreStartupLocale();
00107 
00108     return rv;
00109 }
00110 
00111 std::vector<QString>
00112 FeatureExtractionPluginFactory::getPluginIdentifiers()
00113 {
00114     Profiler profiler("FeatureExtractionPluginFactory::getPluginIdentifiers");
00115 
00116     std::vector<QString> rv;
00117     std::vector<QString> path = getPluginPath();
00118     
00119     for (std::vector<QString>::iterator i = path.begin(); i != path.end(); ++i) {
00120 
00121 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
00122         SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: scanning directory " << i-<< endl;
00123 #endif
00124 
00125         QDir pluginDir(*i, PLUGIN_GLOB,
00126                        QDir::Name | QDir::IgnoreCase,
00127                        QDir::Files | QDir::Readable);
00128 
00129         for (unsigned int j = 0; j < pluginDir.count(); ++j) {
00130 
00131             QString soname = pluginDir.filePath(pluginDir[j]);
00132 
00133 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
00134             SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: trying potential library " << soname << endl;
00135 #endif
00136 
00137             void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL);
00138             
00139             if (!libraryHandle) {
00140                 cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to load library " << soname << ": " << DLERROR() << endl;
00141                 continue;
00142             }
00143 
00144 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
00145             SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: It's a library all right, checking for descriptor" << endl;
00146 #endif
00147 
00148             VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
00149                 DLSYM(libraryHandle, "vampGetPluginDescriptor");
00150 
00151             if (!fn) {
00152                 cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: No descriptor function in " << soname << endl;
00153                 if (DLCLOSE(libraryHandle) != 0) {
00154                     cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl;
00155                 }
00156                 continue;
00157             }
00158 
00159 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
00160             SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: Vamp descriptor found" << endl;
00161 #endif
00162 
00163             const VampPluginDescriptor *descriptor = 0;
00164             int index = 0;
00165 
00166             std::map<std::string, int> known;
00167             bool ok = true;
00168 
00169             while ((descriptor = fn(VAMP_API_VERSION, index))) {
00170 
00171                 if (known.find(descriptor->identifier) != known.end()) {
00172                     cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Plugin library "
00173                               << soname
00174                               << " returns the same plugin identifier \""
00175                               << descriptor->identifier << "\" at indices "
00176                               << known[descriptor->identifier] << " and "
00177                               << index << endl;
00178                     SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: Avoiding this library (obsolete API?)" << endl;
00179                     ok = false;
00180                     break;
00181                 } else {
00182                     known[descriptor->identifier] = index;
00183                 }
00184 
00185                 ++index;
00186             }
00187 
00188             if (ok) {
00189 
00190                 index = 0;
00191 
00192                 while ((descriptor = fn(VAMP_API_VERSION, index))) {
00193 
00194                     QString id = PluginIdentifier::createIdentifier
00195                         ("vamp", soname, descriptor->identifier);
00196                     rv.push_back(id);
00197 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
00198                     SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: Found plugin id " << id << " at index " << index << endl;
00199 #endif
00200                     ++index;
00201                 }
00202             }
00203             
00204             if (DLCLOSE(libraryHandle) != 0) {
00205                 cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl;
00206             }
00207         }
00208     }
00209 
00210     generateTaxonomy();
00211 
00212     return rv;
00213 }
00214 
00215 QString
00216 FeatureExtractionPluginFactory::findPluginFile(QString soname, QString inDir)
00217 {
00218     QString file = "";
00219 
00220 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
00221     SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile(\""
00222               << soname << "\", \"" << inDir << "\")"
00223               << endl;
00224 #endif
00225 
00226     if (inDir != "") {
00227 
00228         QDir dir(inDir, PLUGIN_GLOB,
00229                  QDir::Name | QDir::IgnoreCase,
00230                  QDir::Files | QDir::Readable);
00231         if (!dir.exists()) return "";
00232 
00233         file = dir.filePath(QFileInfo(soname).fileName());
00234 
00235         if (QFileInfo(file).exists() && QFileInfo(file).isFile()) {
00236 
00237 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
00238             SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile: "
00239                       << "found trivially at " << file << endl;
00240 #endif
00241 
00242             return file;
00243         }
00244 
00245         for (unsigned int j = 0; j < dir.count(); ++j) {
00246             file = dir.filePath(dir[j]);
00247             if (QFileInfo(file).baseName() == QFileInfo(soname).baseName()) {
00248 
00249 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
00250                 SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile: "
00251                           << "found \"" << soname << "\" at " << file << endl;
00252 #endif
00253 
00254                 return file;
00255             }
00256         }
00257 
00258 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
00259         SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile (with dir): "
00260                   << "not found" << endl;
00261 #endif
00262 
00263         return "";
00264 
00265     } else {
00266 
00267         QFileInfo fi(soname);
00268 
00269         if (fi.isAbsolute() && fi.exists() && fi.isFile()) {
00270 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
00271             SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile: "
00272                       << "found trivially at " << soname << endl;
00273 #endif
00274             return soname;
00275         }
00276 
00277         if (fi.isAbsolute() && fi.absolutePath() != "") {
00278             file = findPluginFile(soname, fi.absolutePath());
00279             if (file != "") return file;
00280         }
00281 
00282         std::vector<QString> path = getPluginPath();
00283         for (std::vector<QString>::iterator i = path.begin();
00284              i != path.end(); ++i) {
00285             if (*i != "") {
00286                 file = findPluginFile(soname, *i);
00287                 if (file != "") return file;
00288             }
00289         }
00290 
00291 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
00292         SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile: "
00293                   << "not found" << endl;
00294 #endif
00295 
00296         return "";
00297     }
00298 }
00299 
00300 Vamp::Plugin *
00301 FeatureExtractionPluginFactory::instantiatePlugin(QString identifier,
00302                                                   float inputSampleRate)
00303 {
00304     Profiler profiler("FeatureExtractionPluginFactory::instantiatePlugin");
00305 
00306     Vamp::Plugin *rv = 0;
00307     Vamp::PluginHostAdapter *plugin = 0;
00308 
00309     const VampPluginDescriptor *descriptor = 0;
00310     int index = 0;
00311 
00312     QString type, soname, label;
00313     PluginIdentifier::parseIdentifier(identifier, type, soname, label);
00314     if (type != "vamp") {
00315         SVDEBUG << "FeatureExtractionPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type << endl;
00316         return 0;
00317     }
00318 
00319     QString found = findPluginFile(soname);
00320 
00321     if (found == "") {
00322         cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find library file " << soname << endl;
00323         return 0;
00324     } else if (found != soname) {
00325 
00326 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
00327         SVDEBUG << "FeatureExtractionPluginFactory::instantiatePlugin: Given library name was " << soname << ", found at " << found << endl;
00328         cerr << soname << " -> " << found << endl;
00329 #endif
00330 
00331     }        
00332 
00333     soname = found;
00334 
00335     void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL);
00336             
00337     if (!libraryHandle) {
00338         cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to load library " << soname << ": " << DLERROR() << endl;
00339         return 0;
00340     }
00341 
00342     VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
00343         DLSYM(libraryHandle, "vampGetPluginDescriptor");
00344     
00345     if (!fn) {
00346         SVDEBUG << "FeatureExtractionPluginFactory::instantiatePlugin: No descriptor function in " << soname << endl;
00347         goto done;
00348     }
00349 
00350     while ((descriptor = fn(VAMP_API_VERSION, index))) {
00351         if (label == descriptor->identifier) break;
00352         ++index;
00353     }
00354 
00355     if (!descriptor) {
00356         cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find plugin \"" << label << "\" in library " << soname << endl;
00357         goto done;
00358     }
00359 
00360     plugin = new Vamp::PluginHostAdapter(descriptor, inputSampleRate);
00361 
00362     if (plugin) {
00363         m_handleMap[plugin] = libraryHandle;
00364         rv = new PluginDeletionNotifyAdapter(plugin, this);
00365     }
00366 
00367 //    SVDEBUG << "FeatureExtractionPluginFactory::instantiatePlugin: Constructed Vamp plugin, rv is " << rv << endl;
00368 
00370 
00371 done:
00372     if (!rv) {
00373         if (DLCLOSE(libraryHandle) != 0) {
00374             cerr << "WARNING: FeatureExtractionPluginFactory::instantiatePlugin: Failed to unload library " << soname << endl;
00375         }
00376     }
00377 
00378 //    SVDEBUG << "FeatureExtractionPluginFactory::instantiatePlugin: Instantiated plugin " << label << " from library " << soname << ": descriptor " << descriptor << ", rv "<< rv << ", label " << rv->getName() << ", outputs " << rv->getOutputDescriptors().size() << endl;
00379     
00380     return rv;
00381 }
00382 
00383 void
00384 FeatureExtractionPluginFactory::pluginDeleted(Vamp::Plugin *plugin)
00385 {
00386     void *handle = m_handleMap[plugin];
00387     if (handle) {
00388 //        SVDEBUG << "unloading library " << handle << " for plugin " << plugin << endl;
00389         DLCLOSE(handle);
00390     }
00391     m_handleMap.erase(plugin);
00392 }
00393 
00394 QString
00395 FeatureExtractionPluginFactory::getPluginCategory(QString identifier)
00396 {
00397     return m_taxonomy[identifier];
00398 }
00399 
00400 void
00401 FeatureExtractionPluginFactory::generateTaxonomy()
00402 {
00403     std::vector<QString> pluginPath = getPluginPath();
00404     std::vector<QString> path;
00405 
00406     for (size_t i = 0; i < pluginPath.size(); ++i) {
00407         if (pluginPath[i].contains("/lib/")) {
00408             QString p(pluginPath[i]);
00409             path.push_back(p);
00410             p.replace("/lib/", "/share/");
00411             path.push_back(p);
00412         }
00413         path.push_back(pluginPath[i]);
00414     }
00415 
00416     for (size_t i = 0; i < path.size(); ++i) {
00417 
00418         QDir dir(path[i], "*.cat");
00419 
00420 //      SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: directory " << path[i] << " has " << dir.count() << " .cat files" << endl;
00421         for (unsigned int j = 0; j < dir.count(); ++j) {
00422 
00423             QFile file(path[i] + "/" + dir[j]);
00424 
00425 //          SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: about to open " << (path[i]+ "/" + dir[j]) << endl;
00426 
00427             if (file.open(QIODevice::ReadOnly)) {
00428 //                  cerr << "...opened" << endl;
00429                 QTextStream stream(&file);
00430                 QString line;
00431 
00432                 while (!stream.atEnd()) {
00433                     line = stream.readLine();
00434 //                  cerr << "line is: \"" << line << "\"" << endl;
00435                     QString id = PluginIdentifier::canonicalise
00436                         (line.section("::", 0, 0));
00437                     QString cat = line.section("::", 1, 1);
00438                     m_taxonomy[id] = cat;
00439 //                  cerr << "FeatureExtractionPluginFactory: set id \"" << id << "\" to cat \"" << cat << "\"" << endl;
00440                 }
00441             }
00442         }
00443     }
00444 }