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