Chris@49: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@0: 
Chris@0: /*
Chris@52:     Sonic Visualiser
Chris@52:     An audio file viewer and annotation editor.
Chris@52:     Centre for Digital Music, Queen Mary, University of London.
Chris@52:     This file copyright 2006 Chris Cannam.
Chris@0:     
Chris@52:     This program is free software; you can redistribute it and/or
Chris@52:     modify it under the terms of the GNU General Public License as
Chris@52:     published by the Free Software Foundation; either version 2 of the
Chris@52:     License, or (at your option) any later version.  See the file
Chris@52:     COPYING included with this distribution for more information.
Chris@0: */
Chris@0: 
Chris@0: #include "FeatureExtractionPluginFactory.h"
Chris@0: #include "PluginIdentifier.h"
Chris@0: 
Chris@66: #include "vamp/vamp.h"
Chris@66: #include "vamp-sdk/PluginHostAdapter.h"
Chris@66: 
Chris@66: #include "base/System.h"
Chris@66: 
Chris@66: #include <QDir>
Chris@66: #include <QFile>
Chris@66: #include <QFileInfo>
Chris@66: 
Chris@0: #include <iostream>
Chris@0: 
Chris@0: static FeatureExtractionPluginFactory *_nativeInstance = 0;
Chris@0: 
Chris@0: FeatureExtractionPluginFactory *
Chris@0: FeatureExtractionPluginFactory::instance(QString pluginType)
Chris@0: {
Chris@71:     if (pluginType == "vamp") {
Chris@0: 	if (!_nativeInstance) {
Chris@0: 	    std::cerr << "FeatureExtractionPluginFactory::instance(" << pluginType.toStdString()
Chris@0: 		      << "): creating new FeatureExtractionPluginFactory" << std::endl;
Chris@0: 	    _nativeInstance = new FeatureExtractionPluginFactory();
Chris@0: 	}
Chris@0: 	return _nativeInstance;
Chris@0:     }
Chris@0: 
Chris@0:     else return 0;
Chris@0: }
Chris@0: 
Chris@0: FeatureExtractionPluginFactory *
Chris@0: FeatureExtractionPluginFactory::instanceFor(QString identifier)
Chris@0: {
Chris@0:     QString type, soName, label;
Chris@0:     PluginIdentifier::parseIdentifier(identifier, type, soName, label);
Chris@0:     return instance(type);
Chris@0: }
Chris@0: 
Chris@0: std::vector<QString>
Chris@66: FeatureExtractionPluginFactory::getPluginPath()
Chris@66: {
Chris@117:     if (!m_pluginPath.empty()) return m_pluginPath;
Chris@117: 
Chris@66:     std::vector<QString> path;
Chris@66:     std::string envPath;
Chris@66: 
Chris@78:     char *cpath = getenv("VAMP_PATH");
Chris@66:     if (cpath) envPath = cpath;
Chris@66: 
Chris@66:     if (envPath == "") {
Chris@78:         envPath = DEFAULT_VAMP_PATH;
Chris@66:         char *chome = getenv("HOME");
Chris@66:         if (chome) {
Chris@78:             std::string home(chome);
Chris@78:             int f;
Chris@78:             while ((f = envPath.find("$HOME")) >= 0 && f < envPath.length()) {
Chris@78:                 envPath.replace(f, 5, home);
Chris@78:             }
Chris@66:         }
Chris@83: #ifdef Q_WS_WIN32
Chris@83:         char *cpfiles = getenv("ProgramFiles");
Chris@83:         if (!cpfiles) cpfiles = "C:\\Program Files";
Chris@83:         std::string pfiles(cpfiles);
Chris@83:         int f;
Chris@83:         while ((f = envPath.find("%ProgramFiles%")) >= 0 && f < envPath.length()) {
Chris@83:             envPath.replace(f, 14, pfiles);
Chris@83:         }
Chris@83: #endif
Chris@66:     }
Chris@66: 
Chris@78:     std::cerr << "VAMP path is: \"" << envPath << "\"" << std::endl;
Chris@78: 
Chris@66:     std::string::size_type index = 0, newindex = 0;
Chris@66: 
Chris@78:     while ((newindex = envPath.find(PATH_SEPARATOR, index)) < envPath.size()) {
Chris@66: 	path.push_back(envPath.substr(index, newindex - index).c_str());
Chris@66: 	index = newindex + 1;
Chris@66:     }
Chris@66:     
Chris@66:     path.push_back(envPath.substr(index).c_str());
Chris@66: 
Chris@117:     m_pluginPath = path;
Chris@66:     return path;
Chris@66: }
Chris@66: 
Chris@66: std::vector<QString>
Chris@0: FeatureExtractionPluginFactory::getAllPluginIdentifiers()
Chris@0: {
Chris@0:     FeatureExtractionPluginFactory *factory;
Chris@0:     std::vector<QString> rv;
Chris@0:     
Chris@66:     factory = instance("vamp");
Chris@0:     if (factory) {
Chris@0: 	std::vector<QString> tmp = factory->getPluginIdentifiers();
Chris@0: 	for (size_t i = 0; i < tmp.size(); ++i) {
Chris@0: 	    rv.push_back(tmp[i]);
Chris@0: 	}
Chris@0:     }
Chris@0: 
Chris@0:     // Plugins can change the locale, revert it to default.
Chris@0:     setlocale(LC_ALL, "C");
Chris@0:     return rv;
Chris@0: }
Chris@0: 
Chris@0: std::vector<QString>
Chris@0: FeatureExtractionPluginFactory::getPluginIdentifiers()
Chris@0: {
Chris@0:     std::vector<QString> rv;
Chris@66:     std::vector<QString> path = getPluginPath();
Chris@66:     
Chris@66:     for (std::vector<QString>::iterator i = path.begin(); i != path.end(); ++i) {
Chris@66: 
Chris@117: //        std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: scanning directory " << i->toStdString() << std::endl;
Chris@66: 
Chris@66: 	QDir pluginDir(*i, PLUGIN_GLOB,
Chris@66:                        QDir::Name | QDir::IgnoreCase,
Chris@66:                        QDir::Files | QDir::Readable);
Chris@66: 
Chris@66: 	for (unsigned int j = 0; j < pluginDir.count(); ++j) {
Chris@66: 
Chris@66:             QString soname = pluginDir.filePath(pluginDir[j]);
Chris@66: 
Chris@66:             void *libraryHandle = DLOPEN(soname, RTLD_LAZY);
Chris@66:             
Chris@66:             if (!libraryHandle) {
Chris@71:                 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to load library " << soname.toStdString() << ": " << DLERROR() << std::endl;
Chris@66:                 continue;
Chris@66:             }
Chris@66: 
Chris@66:             VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
Chris@66:                 DLSYM(libraryHandle, "vampGetPluginDescriptor");
Chris@66: 
Chris@66:             if (!fn) {
Chris@66:                 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: No descriptor function in " << soname.toStdString() << std::endl;
Chris@66:                 if (DLCLOSE(libraryHandle) != 0) {
Chris@66:                     std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname.toStdString() << std::endl;
Chris@66:                 }
Chris@66:                 continue;
Chris@66:             }
Chris@66: 
Chris@66:             const VampPluginDescriptor *descriptor = 0;
Chris@66:             int index = 0;
Chris@66: 
Chris@66:             while ((descriptor = fn(index))) {
Chris@82:                 QString id = PluginIdentifier::createIdentifier
Chris@82:                     ("vamp", soname, descriptor->name);
Chris@66:                 rv.push_back(id);
Chris@66:                 std::cerr << "Found id " << id.toStdString() << std::endl;
Chris@66:                 ++index;
Chris@66:             }
Chris@66:             
Chris@66:             if (DLCLOSE(libraryHandle) != 0) {
Chris@66:                 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname.toStdString() << std::endl;
Chris@66:             }
Chris@66: 	}
Chris@66:     }
Chris@66: 
Chris@0:     return rv;
Chris@0: }
Chris@0: 
Chris@66: QString
Chris@66: FeatureExtractionPluginFactory::findPluginFile(QString soname, QString inDir)
Chris@66: {
Chris@66:     QString file = "";
Chris@66: 
Chris@66:     if (inDir != "") {
Chris@66: 
Chris@66:         QDir dir(inDir, PLUGIN_GLOB,
Chris@66:                  QDir::Name | QDir::IgnoreCase,
Chris@66:                  QDir::Files | QDir::Readable);
Chris@66:         if (!dir.exists()) return "";
Chris@66: 
Chris@66:         file = dir.filePath(QFileInfo(soname).fileName());
Chris@66:         if (QFileInfo(file).exists()) {
Chris@66:             return file;
Chris@66:         }
Chris@66: 
Chris@66: 	for (unsigned int j = 0; j < dir.count(); ++j) {
Chris@66:             file = dir.filePath(dir[j]);
Chris@66:             if (QFileInfo(file).baseName() == QFileInfo(soname).baseName()) {
Chris@66:                 return file;
Chris@66:             }
Chris@66:         }
Chris@66: 
Chris@66:         return "";
Chris@66: 
Chris@66:     } else {
Chris@66: 
Chris@66:         QFileInfo fi(soname);
Chris@66:         if (fi.exists()) return soname;
Chris@66: 
Chris@66:         if (fi.isAbsolute() && fi.absolutePath() != "") {
Chris@66:             file = findPluginFile(soname, fi.absolutePath());
Chris@66:             if (file != "") return file;
Chris@66:         }
Chris@66: 
Chris@66:         std::vector<QString> path = getPluginPath();
Chris@66:         for (std::vector<QString>::iterator i = path.begin();
Chris@66:              i != path.end(); ++i) {
Chris@66:             if (*i != "") {
Chris@66:                 file = findPluginFile(soname, *i);
Chris@66:                 if (file != "") return file;
Chris@66:             }
Chris@66:         }
Chris@66: 
Chris@66:         return "";
Chris@66:     }
Chris@66: }
Chris@66: 
Chris@66: Vamp::Plugin *
Chris@0: FeatureExtractionPluginFactory::instantiatePlugin(QString identifier,
Chris@0: 						  float inputSampleRate)
Chris@0: {
Chris@66:     Vamp::Plugin *rv = 0;
Chris@66: 
Chris@66:     const VampPluginDescriptor *descriptor = 0;
Chris@66:     int index = 0;
Chris@66: 
Chris@66:     QString type, soname, label;
Chris@66:     PluginIdentifier::parseIdentifier(identifier, type, soname, label);
Chris@71:     if (type != "vamp") {
Chris@0: 	std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type.toStdString() << std::endl;
Chris@0: 	return 0;
Chris@0:     }
Chris@0: 
Chris@66:     QString found = findPluginFile(soname);
Chris@66: 
Chris@66:     if (found == "") {
Chris@66:         std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find library file " << soname.toStdString() << std::endl;
Chris@117:         return 0;
Chris@66:     } else if (found != soname) {
Chris@117: //        std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: WARNING: Given library name was " << soname.toStdString() << ", found at " << found.toStdString() << std::endl;
Chris@117: //        std::cerr << soname.toStdString() << " -> " << found.toStdString() << std::endl;
Chris@0:     }
Chris@0: 
Chris@66:     soname = found;
Chris@66: 
Chris@66:     void *libraryHandle = DLOPEN(soname, RTLD_LAZY);
Chris@66:             
Chris@66:     if (!libraryHandle) {
Chris@71:         std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to load library " << soname.toStdString() << ": " << DLERROR() << std::endl;
Chris@66:         return 0;
Chris@19:     }
Chris@19: 
Chris@66:     VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
Chris@66:         DLSYM(libraryHandle, "vampGetPluginDescriptor");
Chris@66:     
Chris@66:     if (!fn) {
Chris@66:         std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: No descriptor function in " << soname.toStdString() << std::endl;
Chris@66:         goto done;
Chris@0:     }
Chris@0: 
Chris@66:     while ((descriptor = fn(index))) {
Chris@66:         if (label == descriptor->name) break;
Chris@66:         ++index;
Chris@47:     }
Chris@47: 
Chris@66:     if (!descriptor) {
Chris@66:         std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find plugin \"" << label.toStdString() << "\" in library " << soname.toStdString() << std::endl;
Chris@66:         goto done;
Martin@37:     }
Martin@37: 
Chris@66:     rv = new Vamp::PluginHostAdapter(descriptor, inputSampleRate);
Chris@66: 
Chris@117: //    std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Constructed Vamp plugin, rv is " << rv << std::endl;
Chris@79: 
Chris@66:     //!!! need to dlclose() when plugins from a given library are unloaded
Chris@66: 
Chris@66: done:
Chris@66:     if (!rv) {
Chris@66:         if (DLCLOSE(libraryHandle) != 0) {
Chris@66:             std::cerr << "WARNING: FeatureExtractionPluginFactory::instantiatePlugin: Failed to unload library " << soname.toStdString() << std::endl;
Chris@66:         }
Chris@66:     }
Chris@73: 
Chris@73: //    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:     
Chris@66:     return rv;
Chris@0: }
Chris@0: