annotate plugin/FeatureExtractionPluginFactory.cpp @ 165:5ae5885d6ce3

* Add support for plugin classification using category files. Add separate menus listing plugins by category, maker, and plugin name.
author Chris Cannam
date Thu, 21 Sep 2006 16:43:50 +0000
parents 4b2ea82fd0ed
children 06ad01f3e553
rev   line source
Chris@49 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@52 4 Sonic Visualiser
Chris@52 5 An audio file viewer and annotation editor.
Chris@52 6 Centre for Digital Music, Queen Mary, University of London.
Chris@52 7 This file copyright 2006 Chris Cannam.
Chris@0 8
Chris@52 9 This program is free software; you can redistribute it and/or
Chris@52 10 modify it under the terms of the GNU General Public License as
Chris@52 11 published by the Free Software Foundation; either version 2 of the
Chris@52 12 License, or (at your option) any later version. See the file
Chris@52 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "FeatureExtractionPluginFactory.h"
Chris@0 17 #include "PluginIdentifier.h"
Chris@0 18
Chris@66 19 #include "vamp/vamp.h"
Chris@66 20 #include "vamp-sdk/PluginHostAdapter.h"
Chris@66 21
Chris@150 22 #include "system/System.h"
Chris@66 23
Chris@66 24 #include <QDir>
Chris@66 25 #include <QFile>
Chris@66 26 #include <QFileInfo>
Chris@165 27 #include <QTextStream>
Chris@66 28
Chris@0 29 #include <iostream>
Chris@0 30
Chris@0 31 static FeatureExtractionPluginFactory *_nativeInstance = 0;
Chris@0 32
Chris@0 33 FeatureExtractionPluginFactory *
Chris@0 34 FeatureExtractionPluginFactory::instance(QString pluginType)
Chris@0 35 {
Chris@71 36 if (pluginType == "vamp") {
Chris@0 37 if (!_nativeInstance) {
Chris@0 38 std::cerr << "FeatureExtractionPluginFactory::instance(" << pluginType.toStdString()
Chris@0 39 << "): creating new FeatureExtractionPluginFactory" << std::endl;
Chris@0 40 _nativeInstance = new FeatureExtractionPluginFactory();
Chris@0 41 }
Chris@0 42 return _nativeInstance;
Chris@0 43 }
Chris@0 44
Chris@0 45 else return 0;
Chris@0 46 }
Chris@0 47
Chris@0 48 FeatureExtractionPluginFactory *
Chris@0 49 FeatureExtractionPluginFactory::instanceFor(QString identifier)
Chris@0 50 {
Chris@0 51 QString type, soName, label;
Chris@0 52 PluginIdentifier::parseIdentifier(identifier, type, soName, label);
Chris@0 53 return instance(type);
Chris@0 54 }
Chris@0 55
Chris@0 56 std::vector<QString>
Chris@66 57 FeatureExtractionPluginFactory::getPluginPath()
Chris@66 58 {
Chris@117 59 if (!m_pluginPath.empty()) return m_pluginPath;
Chris@117 60
Chris@66 61 std::vector<QString> path;
Chris@66 62 std::string envPath;
Chris@66 63
Chris@78 64 char *cpath = getenv("VAMP_PATH");
Chris@66 65 if (cpath) envPath = cpath;
Chris@66 66
Chris@66 67 if (envPath == "") {
Chris@78 68 envPath = DEFAULT_VAMP_PATH;
Chris@66 69 char *chome = getenv("HOME");
Chris@66 70 if (chome) {
Chris@78 71 std::string home(chome);
Chris@78 72 int f;
Chris@78 73 while ((f = envPath.find("$HOME")) >= 0 && f < envPath.length()) {
Chris@78 74 envPath.replace(f, 5, home);
Chris@78 75 }
Chris@66 76 }
Chris@83 77 #ifdef Q_WS_WIN32
Chris@83 78 char *cpfiles = getenv("ProgramFiles");
Chris@83 79 if (!cpfiles) cpfiles = "C:\\Program Files";
Chris@83 80 std::string pfiles(cpfiles);
Chris@83 81 int f;
Chris@83 82 while ((f = envPath.find("%ProgramFiles%")) >= 0 && f < envPath.length()) {
Chris@83 83 envPath.replace(f, 14, pfiles);
Chris@83 84 }
Chris@83 85 #endif
Chris@66 86 }
Chris@66 87
Chris@78 88 std::cerr << "VAMP path is: \"" << envPath << "\"" << std::endl;
Chris@78 89
Chris@66 90 std::string::size_type index = 0, newindex = 0;
Chris@66 91
Chris@78 92 while ((newindex = envPath.find(PATH_SEPARATOR, index)) < envPath.size()) {
Chris@66 93 path.push_back(envPath.substr(index, newindex - index).c_str());
Chris@66 94 index = newindex + 1;
Chris@66 95 }
Chris@66 96
Chris@66 97 path.push_back(envPath.substr(index).c_str());
Chris@66 98
Chris@117 99 m_pluginPath = path;
Chris@66 100 return path;
Chris@66 101 }
Chris@66 102
Chris@66 103 std::vector<QString>
Chris@0 104 FeatureExtractionPluginFactory::getAllPluginIdentifiers()
Chris@0 105 {
Chris@0 106 FeatureExtractionPluginFactory *factory;
Chris@0 107 std::vector<QString> rv;
Chris@0 108
Chris@66 109 factory = instance("vamp");
Chris@0 110 if (factory) {
Chris@0 111 std::vector<QString> tmp = factory->getPluginIdentifiers();
Chris@0 112 for (size_t i = 0; i < tmp.size(); ++i) {
Chris@0 113 rv.push_back(tmp[i]);
Chris@0 114 }
Chris@0 115 }
Chris@0 116
Chris@0 117 // Plugins can change the locale, revert it to default.
Chris@0 118 setlocale(LC_ALL, "C");
Chris@0 119 return rv;
Chris@0 120 }
Chris@0 121
Chris@0 122 std::vector<QString>
Chris@0 123 FeatureExtractionPluginFactory::getPluginIdentifiers()
Chris@0 124 {
Chris@0 125 std::vector<QString> rv;
Chris@66 126 std::vector<QString> path = getPluginPath();
Chris@66 127
Chris@66 128 for (std::vector<QString>::iterator i = path.begin(); i != path.end(); ++i) {
Chris@66 129
Chris@117 130 // std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: scanning directory " << i->toStdString() << std::endl;
Chris@66 131
Chris@66 132 QDir pluginDir(*i, PLUGIN_GLOB,
Chris@66 133 QDir::Name | QDir::IgnoreCase,
Chris@66 134 QDir::Files | QDir::Readable);
Chris@66 135
Chris@66 136 for (unsigned int j = 0; j < pluginDir.count(); ++j) {
Chris@66 137
Chris@66 138 QString soname = pluginDir.filePath(pluginDir[j]);
Chris@66 139
Chris@66 140 void *libraryHandle = DLOPEN(soname, RTLD_LAZY);
Chris@66 141
Chris@66 142 if (!libraryHandle) {
Chris@71 143 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to load library " << soname.toStdString() << ": " << DLERROR() << std::endl;
Chris@66 144 continue;
Chris@66 145 }
Chris@66 146
Chris@66 147 VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
Chris@66 148 DLSYM(libraryHandle, "vampGetPluginDescriptor");
Chris@66 149
Chris@66 150 if (!fn) {
Chris@66 151 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: No descriptor function in " << soname.toStdString() << std::endl;
Chris@66 152 if (DLCLOSE(libraryHandle) != 0) {
Chris@66 153 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname.toStdString() << std::endl;
Chris@66 154 }
Chris@66 155 continue;
Chris@66 156 }
Chris@66 157
Chris@66 158 const VampPluginDescriptor *descriptor = 0;
Chris@66 159 int index = 0;
Chris@66 160
Chris@66 161 while ((descriptor = fn(index))) {
Chris@82 162 QString id = PluginIdentifier::createIdentifier
Chris@82 163 ("vamp", soname, descriptor->name);
Chris@66 164 rv.push_back(id);
Chris@66 165 std::cerr << "Found id " << id.toStdString() << std::endl;
Chris@66 166 ++index;
Chris@66 167 }
Chris@66 168
Chris@66 169 if (DLCLOSE(libraryHandle) != 0) {
Chris@66 170 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname.toStdString() << std::endl;
Chris@66 171 }
Chris@66 172 }
Chris@66 173 }
Chris@66 174
Chris@165 175 generateTaxonomy();
Chris@165 176
Chris@0 177 return rv;
Chris@0 178 }
Chris@0 179
Chris@66 180 QString
Chris@66 181 FeatureExtractionPluginFactory::findPluginFile(QString soname, QString inDir)
Chris@66 182 {
Chris@66 183 QString file = "";
Chris@66 184
Chris@66 185 if (inDir != "") {
Chris@66 186
Chris@66 187 QDir dir(inDir, PLUGIN_GLOB,
Chris@66 188 QDir::Name | QDir::IgnoreCase,
Chris@66 189 QDir::Files | QDir::Readable);
Chris@66 190 if (!dir.exists()) return "";
Chris@66 191
Chris@66 192 file = dir.filePath(QFileInfo(soname).fileName());
Chris@66 193 if (QFileInfo(file).exists()) {
Chris@66 194 return file;
Chris@66 195 }
Chris@66 196
Chris@66 197 for (unsigned int j = 0; j < dir.count(); ++j) {
Chris@66 198 file = dir.filePath(dir[j]);
Chris@66 199 if (QFileInfo(file).baseName() == QFileInfo(soname).baseName()) {
Chris@66 200 return file;
Chris@66 201 }
Chris@66 202 }
Chris@66 203
Chris@66 204 return "";
Chris@66 205
Chris@66 206 } else {
Chris@66 207
Chris@66 208 QFileInfo fi(soname);
Chris@66 209 if (fi.exists()) return soname;
Chris@66 210
Chris@66 211 if (fi.isAbsolute() && fi.absolutePath() != "") {
Chris@66 212 file = findPluginFile(soname, fi.absolutePath());
Chris@66 213 if (file != "") return file;
Chris@66 214 }
Chris@66 215
Chris@66 216 std::vector<QString> path = getPluginPath();
Chris@66 217 for (std::vector<QString>::iterator i = path.begin();
Chris@66 218 i != path.end(); ++i) {
Chris@66 219 if (*i != "") {
Chris@66 220 file = findPluginFile(soname, *i);
Chris@66 221 if (file != "") return file;
Chris@66 222 }
Chris@66 223 }
Chris@66 224
Chris@66 225 return "";
Chris@66 226 }
Chris@66 227 }
Chris@66 228
Chris@66 229 Vamp::Plugin *
Chris@0 230 FeatureExtractionPluginFactory::instantiatePlugin(QString identifier,
Chris@0 231 float inputSampleRate)
Chris@0 232 {
Chris@66 233 Vamp::Plugin *rv = 0;
Chris@66 234
Chris@66 235 const VampPluginDescriptor *descriptor = 0;
Chris@66 236 int index = 0;
Chris@66 237
Chris@66 238 QString type, soname, label;
Chris@66 239 PluginIdentifier::parseIdentifier(identifier, type, soname, label);
Chris@71 240 if (type != "vamp") {
Chris@0 241 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type.toStdString() << std::endl;
Chris@0 242 return 0;
Chris@0 243 }
Chris@0 244
Chris@66 245 QString found = findPluginFile(soname);
Chris@66 246
Chris@66 247 if (found == "") {
Chris@66 248 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find library file " << soname.toStdString() << std::endl;
Chris@117 249 return 0;
Chris@66 250 } else if (found != soname) {
Chris@117 251 // std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: WARNING: Given library name was " << soname.toStdString() << ", found at " << found.toStdString() << std::endl;
Chris@117 252 // std::cerr << soname.toStdString() << " -> " << found.toStdString() << std::endl;
Chris@0 253 }
Chris@0 254
Chris@66 255 soname = found;
Chris@66 256
Chris@66 257 void *libraryHandle = DLOPEN(soname, RTLD_LAZY);
Chris@66 258
Chris@66 259 if (!libraryHandle) {
Chris@71 260 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to load library " << soname.toStdString() << ": " << DLERROR() << std::endl;
Chris@66 261 return 0;
Chris@19 262 }
Chris@19 263
Chris@66 264 VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
Chris@66 265 DLSYM(libraryHandle, "vampGetPluginDescriptor");
Chris@66 266
Chris@66 267 if (!fn) {
Chris@66 268 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: No descriptor function in " << soname.toStdString() << std::endl;
Chris@66 269 goto done;
Chris@0 270 }
Chris@0 271
Chris@66 272 while ((descriptor = fn(index))) {
Chris@66 273 if (label == descriptor->name) break;
Chris@66 274 ++index;
Chris@47 275 }
Chris@47 276
Chris@66 277 if (!descriptor) {
Chris@66 278 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find plugin \"" << label.toStdString() << "\" in library " << soname.toStdString() << std::endl;
Chris@66 279 goto done;
Martin@37 280 }
Martin@37 281
Chris@66 282 rv = new Vamp::PluginHostAdapter(descriptor, inputSampleRate);
Chris@66 283
Chris@117 284 // std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Constructed Vamp plugin, rv is " << rv << std::endl;
Chris@79 285
Chris@66 286 //!!! need to dlclose() when plugins from a given library are unloaded
Chris@66 287
Chris@66 288 done:
Chris@66 289 if (!rv) {
Chris@66 290 if (DLCLOSE(libraryHandle) != 0) {
Chris@66 291 std::cerr << "WARNING: FeatureExtractionPluginFactory::instantiatePlugin: Failed to unload library " << soname.toStdString() << std::endl;
Chris@66 292 }
Chris@66 293 }
Chris@73 294
Chris@73 295 // std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Instantiated plugin " << label.toStdString() << " from library " << soname.toStdString() << ": descriptor " << descriptor << ", rv "<< rv << ", label " << rv->getName() << ", outputs " << rv->getOutputDescriptors().size() << std::endl;
Chris@73 296
Chris@66 297 return rv;
Chris@0 298 }
Chris@0 299
Chris@165 300 QString
Chris@165 301 FeatureExtractionPluginFactory::getPluginCategory(QString identifier)
Chris@165 302 {
Chris@165 303 return m_taxonomy[identifier];
Chris@165 304 }
Chris@165 305
Chris@165 306 void
Chris@165 307 FeatureExtractionPluginFactory::generateTaxonomy()
Chris@165 308 {
Chris@165 309 std::vector<QString> pluginPath = getPluginPath();
Chris@165 310 std::vector<QString> path;
Chris@165 311
Chris@165 312 for (size_t i = 0; i < pluginPath.size(); ++i) {
Chris@165 313 if (pluginPath[i].contains("/lib/")) {
Chris@165 314 QString p(pluginPath[i]);
Chris@165 315 path.push_back(p);
Chris@165 316 p.replace("/lib/", "/share/");
Chris@165 317 path.push_back(p);
Chris@165 318 }
Chris@165 319 path.push_back(pluginPath[i]);
Chris@165 320 }
Chris@165 321
Chris@165 322 for (size_t i = 0; i < path.size(); ++i) {
Chris@165 323
Chris@165 324 QDir dir(path[i], "*.cat");
Chris@165 325
Chris@165 326 // std::cerr << "LADSPAPluginFactory::generateFallbackCategories: directory " << path[i].toStdString() << " has " << dir.count() << " .cat files" << std::endl;
Chris@165 327 for (unsigned int j = 0; j < dir.count(); ++j) {
Chris@165 328
Chris@165 329 QFile file(path[i] + "/" + dir[j]);
Chris@165 330
Chris@165 331 // std::cerr << "LADSPAPluginFactory::generateFallbackCategories: about to open " << (path[i].toStdString() + "/" + dir[j].toStdString()) << std::endl;
Chris@165 332
Chris@165 333 if (file.open(QIODevice::ReadOnly)) {
Chris@165 334 // std::cerr << "...opened" << std::endl;
Chris@165 335 QTextStream stream(&file);
Chris@165 336 QString line;
Chris@165 337
Chris@165 338 while (!stream.atEnd()) {
Chris@165 339 line = stream.readLine();
Chris@165 340 // std::cerr << "line is: \"" << line.toStdString() << "\"" << std::endl;
Chris@165 341 QString id = PluginIdentifier::canonicalise
Chris@165 342 (line.section("::", 0, 0));
Chris@165 343 QString cat = line.section("::", 1, 1);
Chris@165 344 m_taxonomy[id] = cat;
Chris@165 345 // std::cerr << "FeatureExtractionPluginFactory: set id \"" << id.toStdString() << "\" to cat \"" << cat.toStdString() << "\"" << std::endl;
Chris@165 346 }
Chris@165 347 }
Chris@165 348 }
Chris@165 349 }
Chris@165 350 }