| 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@66 | 22 #include "base/System.h" | 
| Chris@66 | 23 | 
| Chris@66 | 24 #include <QDir> | 
| Chris@66 | 25 #include <QFile> | 
| Chris@66 | 26 #include <QFileInfo> | 
| Chris@66 | 27 | 
| Chris@0 | 28 #include <iostream> | 
| Chris@0 | 29 | 
| Chris@0 | 30 static FeatureExtractionPluginFactory *_nativeInstance = 0; | 
| Chris@0 | 31 | 
| Chris@0 | 32 FeatureExtractionPluginFactory * | 
| Chris@0 | 33 FeatureExtractionPluginFactory::instance(QString pluginType) | 
| Chris@0 | 34 { | 
| Chris@71 | 35     if (pluginType == "vamp") { | 
| Chris@0 | 36 	if (!_nativeInstance) { | 
| Chris@0 | 37 	    std::cerr << "FeatureExtractionPluginFactory::instance(" << pluginType.toStdString() | 
| Chris@0 | 38 		      << "): creating new FeatureExtractionPluginFactory" << std::endl; | 
| Chris@0 | 39 	    _nativeInstance = new FeatureExtractionPluginFactory(); | 
| Chris@0 | 40 	} | 
| Chris@0 | 41 	return _nativeInstance; | 
| Chris@0 | 42     } | 
| Chris@0 | 43 | 
| Chris@0 | 44     else return 0; | 
| Chris@0 | 45 } | 
| Chris@0 | 46 | 
| Chris@0 | 47 FeatureExtractionPluginFactory * | 
| Chris@0 | 48 FeatureExtractionPluginFactory::instanceFor(QString identifier) | 
| Chris@0 | 49 { | 
| Chris@0 | 50     QString type, soName, label; | 
| Chris@0 | 51     PluginIdentifier::parseIdentifier(identifier, type, soName, label); | 
| Chris@0 | 52     return instance(type); | 
| Chris@0 | 53 } | 
| Chris@0 | 54 | 
| Chris@0 | 55 std::vector<QString> | 
| Chris@66 | 56 FeatureExtractionPluginFactory::getPluginPath() | 
| Chris@66 | 57 { | 
| Chris@117 | 58     if (!m_pluginPath.empty()) return m_pluginPath; | 
| Chris@117 | 59 | 
| Chris@66 | 60     std::vector<QString> path; | 
| Chris@66 | 61     std::string envPath; | 
| Chris@66 | 62 | 
| Chris@78 | 63     char *cpath = getenv("VAMP_PATH"); | 
| Chris@66 | 64     if (cpath) envPath = cpath; | 
| Chris@66 | 65 | 
| Chris@66 | 66     if (envPath == "") { | 
| Chris@78 | 67         envPath = DEFAULT_VAMP_PATH; | 
| Chris@66 | 68         char *chome = getenv("HOME"); | 
| Chris@66 | 69         if (chome) { | 
| Chris@78 | 70             std::string home(chome); | 
| Chris@78 | 71             int f; | 
| Chris@78 | 72             while ((f = envPath.find("$HOME")) >= 0 && f < envPath.length()) { | 
| Chris@78 | 73                 envPath.replace(f, 5, home); | 
| Chris@78 | 74             } | 
| Chris@66 | 75         } | 
| Chris@83 | 76 #ifdef Q_WS_WIN32 | 
| Chris@83 | 77         char *cpfiles = getenv("ProgramFiles"); | 
| Chris@83 | 78         if (!cpfiles) cpfiles = "C:\\Program Files"; | 
| Chris@83 | 79         std::string pfiles(cpfiles); | 
| Chris@83 | 80         int f; | 
| Chris@83 | 81         while ((f = envPath.find("%ProgramFiles%")) >= 0 && f < envPath.length()) { | 
| Chris@83 | 82             envPath.replace(f, 14, pfiles); | 
| Chris@83 | 83         } | 
| Chris@83 | 84 #endif | 
| Chris@66 | 85     } | 
| Chris@66 | 86 | 
| Chris@78 | 87     std::cerr << "VAMP path is: \"" << envPath << "\"" << std::endl; | 
| Chris@78 | 88 | 
| Chris@66 | 89     std::string::size_type index = 0, newindex = 0; | 
| Chris@66 | 90 | 
| Chris@78 | 91     while ((newindex = envPath.find(PATH_SEPARATOR, index)) < envPath.size()) { | 
| Chris@66 | 92 	path.push_back(envPath.substr(index, newindex - index).c_str()); | 
| Chris@66 | 93 	index = newindex + 1; | 
| Chris@66 | 94     } | 
| Chris@66 | 95 | 
| Chris@66 | 96     path.push_back(envPath.substr(index).c_str()); | 
| Chris@66 | 97 | 
| Chris@117 | 98     m_pluginPath = path; | 
| Chris@66 | 99     return path; | 
| Chris@66 | 100 } | 
| Chris@66 | 101 | 
| Chris@66 | 102 std::vector<QString> | 
| Chris@0 | 103 FeatureExtractionPluginFactory::getAllPluginIdentifiers() | 
| Chris@0 | 104 { | 
| Chris@0 | 105     FeatureExtractionPluginFactory *factory; | 
| Chris@0 | 106     std::vector<QString> rv; | 
| Chris@0 | 107 | 
| Chris@66 | 108     factory = instance("vamp"); | 
| Chris@0 | 109     if (factory) { | 
| Chris@0 | 110 	std::vector<QString> tmp = factory->getPluginIdentifiers(); | 
| Chris@0 | 111 	for (size_t i = 0; i < tmp.size(); ++i) { | 
| Chris@0 | 112 	    rv.push_back(tmp[i]); | 
| Chris@0 | 113 	} | 
| Chris@0 | 114     } | 
| Chris@0 | 115 | 
| Chris@0 | 116     // Plugins can change the locale, revert it to default. | 
| Chris@0 | 117     setlocale(LC_ALL, "C"); | 
| Chris@0 | 118     return rv; | 
| Chris@0 | 119 } | 
| Chris@0 | 120 | 
| Chris@0 | 121 std::vector<QString> | 
| Chris@0 | 122 FeatureExtractionPluginFactory::getPluginIdentifiers() | 
| Chris@0 | 123 { | 
| Chris@0 | 124     std::vector<QString> rv; | 
| Chris@66 | 125     std::vector<QString> path = getPluginPath(); | 
| Chris@66 | 126 | 
| Chris@66 | 127     for (std::vector<QString>::iterator i = path.begin(); i != path.end(); ++i) { | 
| Chris@66 | 128 | 
| Chris@117 | 129 //        std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: scanning directory " << i->toStdString() << std::endl; | 
| Chris@66 | 130 | 
| Chris@66 | 131 	QDir pluginDir(*i, PLUGIN_GLOB, | 
| Chris@66 | 132                        QDir::Name | QDir::IgnoreCase, | 
| Chris@66 | 133                        QDir::Files | QDir::Readable); | 
| Chris@66 | 134 | 
| Chris@66 | 135 	for (unsigned int j = 0; j < pluginDir.count(); ++j) { | 
| Chris@66 | 136 | 
| Chris@66 | 137             QString soname = pluginDir.filePath(pluginDir[j]); | 
| Chris@66 | 138 | 
| Chris@66 | 139             void *libraryHandle = DLOPEN(soname, RTLD_LAZY); | 
| Chris@66 | 140 | 
| Chris@66 | 141             if (!libraryHandle) { | 
| Chris@71 | 142                 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to load library " << soname.toStdString() << ": " << DLERROR() << std::endl; | 
| Chris@66 | 143                 continue; | 
| Chris@66 | 144             } | 
| Chris@66 | 145 | 
| Chris@66 | 146             VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction) | 
| Chris@66 | 147                 DLSYM(libraryHandle, "vampGetPluginDescriptor"); | 
| Chris@66 | 148 | 
| Chris@66 | 149             if (!fn) { | 
| Chris@66 | 150                 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: No descriptor function in " << soname.toStdString() << std::endl; | 
| Chris@66 | 151                 if (DLCLOSE(libraryHandle) != 0) { | 
| Chris@66 | 152                     std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname.toStdString() << std::endl; | 
| Chris@66 | 153                 } | 
| Chris@66 | 154                 continue; | 
| Chris@66 | 155             } | 
| Chris@66 | 156 | 
| Chris@66 | 157             const VampPluginDescriptor *descriptor = 0; | 
| Chris@66 | 158             int index = 0; | 
| Chris@66 | 159 | 
| Chris@66 | 160             while ((descriptor = fn(index))) { | 
| Chris@82 | 161                 QString id = PluginIdentifier::createIdentifier | 
| Chris@82 | 162                     ("vamp", soname, descriptor->name); | 
| Chris@66 | 163                 rv.push_back(id); | 
| Chris@66 | 164                 std::cerr << "Found id " << id.toStdString() << std::endl; | 
| Chris@66 | 165                 ++index; | 
| Chris@66 | 166             } | 
| Chris@66 | 167 | 
| Chris@66 | 168             if (DLCLOSE(libraryHandle) != 0) { | 
| Chris@66 | 169                 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname.toStdString() << std::endl; | 
| Chris@66 | 170             } | 
| Chris@66 | 171 	} | 
| Chris@66 | 172     } | 
| Chris@66 | 173 | 
| Chris@0 | 174     return rv; | 
| Chris@0 | 175 } | 
| Chris@0 | 176 | 
| Chris@66 | 177 QString | 
| Chris@66 | 178 FeatureExtractionPluginFactory::findPluginFile(QString soname, QString inDir) | 
| Chris@66 | 179 { | 
| Chris@66 | 180     QString file = ""; | 
| Chris@66 | 181 | 
| Chris@66 | 182     if (inDir != "") { | 
| Chris@66 | 183 | 
| Chris@66 | 184         QDir dir(inDir, PLUGIN_GLOB, | 
| Chris@66 | 185                  QDir::Name | QDir::IgnoreCase, | 
| Chris@66 | 186                  QDir::Files | QDir::Readable); | 
| Chris@66 | 187         if (!dir.exists()) return ""; | 
| Chris@66 | 188 | 
| Chris@66 | 189         file = dir.filePath(QFileInfo(soname).fileName()); | 
| Chris@66 | 190         if (QFileInfo(file).exists()) { | 
| Chris@66 | 191             return file; | 
| Chris@66 | 192         } | 
| Chris@66 | 193 | 
| Chris@66 | 194 	for (unsigned int j = 0; j < dir.count(); ++j) { | 
| Chris@66 | 195             file = dir.filePath(dir[j]); | 
| Chris@66 | 196             if (QFileInfo(file).baseName() == QFileInfo(soname).baseName()) { | 
| Chris@66 | 197                 return file; | 
| Chris@66 | 198             } | 
| Chris@66 | 199         } | 
| Chris@66 | 200 | 
| Chris@66 | 201         return ""; | 
| Chris@66 | 202 | 
| Chris@66 | 203     } else { | 
| Chris@66 | 204 | 
| Chris@66 | 205         QFileInfo fi(soname); | 
| Chris@66 | 206         if (fi.exists()) return soname; | 
| Chris@66 | 207 | 
| Chris@66 | 208         if (fi.isAbsolute() && fi.absolutePath() != "") { | 
| Chris@66 | 209             file = findPluginFile(soname, fi.absolutePath()); | 
| Chris@66 | 210             if (file != "") return file; | 
| Chris@66 | 211         } | 
| Chris@66 | 212 | 
| Chris@66 | 213         std::vector<QString> path = getPluginPath(); | 
| Chris@66 | 214         for (std::vector<QString>::iterator i = path.begin(); | 
| Chris@66 | 215              i != path.end(); ++i) { | 
| Chris@66 | 216             if (*i != "") { | 
| Chris@66 | 217                 file = findPluginFile(soname, *i); | 
| Chris@66 | 218                 if (file != "") return file; | 
| Chris@66 | 219             } | 
| Chris@66 | 220         } | 
| Chris@66 | 221 | 
| Chris@66 | 222         return ""; | 
| Chris@66 | 223     } | 
| Chris@66 | 224 } | 
| Chris@66 | 225 | 
| Chris@66 | 226 Vamp::Plugin * | 
| Chris@0 | 227 FeatureExtractionPluginFactory::instantiatePlugin(QString identifier, | 
| Chris@0 | 228 						  float inputSampleRate) | 
| Chris@0 | 229 { | 
| Chris@66 | 230     Vamp::Plugin *rv = 0; | 
| Chris@66 | 231 | 
| Chris@66 | 232     const VampPluginDescriptor *descriptor = 0; | 
| Chris@66 | 233     int index = 0; | 
| Chris@66 | 234 | 
| Chris@66 | 235     QString type, soname, label; | 
| Chris@66 | 236     PluginIdentifier::parseIdentifier(identifier, type, soname, label); | 
| Chris@71 | 237     if (type != "vamp") { | 
| Chris@0 | 238 	std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type.toStdString() << std::endl; | 
| Chris@0 | 239 	return 0; | 
| Chris@0 | 240     } | 
| Chris@0 | 241 | 
| Chris@66 | 242     QString found = findPluginFile(soname); | 
| Chris@66 | 243 | 
| Chris@66 | 244     if (found == "") { | 
| Chris@66 | 245         std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find library file " << soname.toStdString() << std::endl; | 
| Chris@117 | 246         return 0; | 
| Chris@66 | 247     } else if (found != soname) { | 
| Chris@117 | 248 //        std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: WARNING: Given library name was " << soname.toStdString() << ", found at " << found.toStdString() << std::endl; | 
| Chris@117 | 249 //        std::cerr << soname.toStdString() << " -> " << found.toStdString() << std::endl; | 
| Chris@0 | 250     } | 
| Chris@0 | 251 | 
| Chris@66 | 252     soname = found; | 
| Chris@66 | 253 | 
| Chris@66 | 254     void *libraryHandle = DLOPEN(soname, RTLD_LAZY); | 
| Chris@66 | 255 | 
| Chris@66 | 256     if (!libraryHandle) { | 
| Chris@71 | 257         std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to load library " << soname.toStdString() << ": " << DLERROR() << std::endl; | 
| Chris@66 | 258         return 0; | 
| Chris@19 | 259     } | 
| Chris@19 | 260 | 
| Chris@66 | 261     VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction) | 
| Chris@66 | 262         DLSYM(libraryHandle, "vampGetPluginDescriptor"); | 
| Chris@66 | 263 | 
| Chris@66 | 264     if (!fn) { | 
| Chris@66 | 265         std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: No descriptor function in " << soname.toStdString() << std::endl; | 
| Chris@66 | 266         goto done; | 
| Chris@0 | 267     } | 
| Chris@0 | 268 | 
| Chris@66 | 269     while ((descriptor = fn(index))) { | 
| Chris@66 | 270         if (label == descriptor->name) break; | 
| Chris@66 | 271         ++index; | 
| Chris@47 | 272     } | 
| Chris@47 | 273 | 
| Chris@66 | 274     if (!descriptor) { | 
| Chris@66 | 275         std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find plugin \"" << label.toStdString() << "\" in library " << soname.toStdString() << std::endl; | 
| Chris@66 | 276         goto done; | 
| Martin@37 | 277     } | 
| Martin@37 | 278 | 
| Chris@66 | 279     rv = new Vamp::PluginHostAdapter(descriptor, inputSampleRate); | 
| Chris@66 | 280 | 
| Chris@117 | 281 //    std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Constructed Vamp plugin, rv is " << rv << std::endl; | 
| Chris@79 | 282 | 
| Chris@66 | 283     //!!! need to dlclose() when plugins from a given library are unloaded | 
| Chris@66 | 284 | 
| Chris@66 | 285 done: | 
| Chris@66 | 286     if (!rv) { | 
| Chris@66 | 287         if (DLCLOSE(libraryHandle) != 0) { | 
| Chris@66 | 288             std::cerr << "WARNING: FeatureExtractionPluginFactory::instantiatePlugin: Failed to unload library " << soname.toStdString() << std::endl; | 
| Chris@66 | 289         } | 
| Chris@66 | 290     } | 
| Chris@73 | 291 | 
| Chris@73 | 292 //    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 | 293 | 
| Chris@66 | 294     return rv; | 
| Chris@0 | 295 } | 
| Chris@0 | 296 |