summaryrefslogtreecommitdiff
path: root/libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.cpp')
-rw-r--r--libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.cpp601
1 files changed, 601 insertions, 0 deletions
diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.cpp b/libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.cpp
new file mode 100644
index 0000000000..cb71fc4750
--- /dev/null
+++ b/libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.cpp
@@ -0,0 +1,601 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Vamp
+
+ An API for audio analysis and feature extraction plugins.
+
+ Centre for Digital Music, Queen Mary, University of London.
+ Copyright 2006-2007 Chris Cannam and QMUL.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use, copy,
+ modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+ ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ Except as contained in this notice, the names of the Centre for
+ Digital Music; Queen Mary, University of London; and Chris Cannam
+ shall not be used in advertising or otherwise to promote the sale,
+ use or other dealings in this Software without prior written
+ authorization.
+*/
+
+#include "vamp-sdk/PluginHostAdapter.h"
+#include "PluginLoader.h"
+#include "PluginInputDomainAdapter.h"
+#include "PluginChannelAdapter.h"
+
+#include <fstream>
+#include <cctype> // tolower
+
+#ifdef _WIN32
+
+#include <windows.h>
+#include <tchar.h>
+#define PLUGIN_SUFFIX "dll"
+
+#else /* ! _WIN32 */
+
+#include <dirent.h>
+#include <dlfcn.h>
+
+#ifdef __APPLE__
+#define PLUGIN_SUFFIX "dylib"
+#else /* ! __APPLE__ */
+#define PLUGIN_SUFFIX "so"
+#endif /* ! __APPLE__ */
+
+#endif /* ! _WIN32 */
+
+using namespace std;
+
+namespace Vamp {
+
+namespace HostExt {
+
+class PluginLoader::Impl
+{
+public:
+ Impl();
+ virtual ~Impl();
+
+ PluginKeyList listPlugins();
+
+ Plugin *loadPlugin(PluginKey key,
+ float inputSampleRate,
+ int adapterFlags);
+
+ PluginKey composePluginKey(string libraryName, string identifier);
+
+ PluginCategoryHierarchy getPluginCategory(PluginKey key);
+
+ string getLibraryPathForPlugin(PluginKey key);
+
+protected:
+ class PluginDeletionNotifyAdapter : public PluginWrapper {
+ public:
+ PluginDeletionNotifyAdapter(Plugin *plugin, Impl *loader);
+ virtual ~PluginDeletionNotifyAdapter();
+ protected:
+ Impl *m_loader;
+ };
+
+ virtual void pluginDeleted(PluginDeletionNotifyAdapter *adapter);
+
+ map<PluginKey, string> m_pluginLibraryNameMap;
+ bool m_allPluginsEnumerated;
+ void enumeratePlugins(PluginKey forPlugin = "");
+
+ map<PluginKey, PluginCategoryHierarchy> m_taxonomy;
+ void generateTaxonomy();
+
+ map<Plugin *, void *> m_pluginLibraryHandleMap;
+
+ bool decomposePluginKey(PluginKey key,
+ string &libraryName, string &identifier);
+
+ void *loadLibrary(string path);
+ void unloadLibrary(void *handle);
+ void *lookupInLibrary(void *handle, const char *symbol);
+
+ string splicePath(string a, string b);
+ vector<string> listFiles(string dir, string ext);
+};
+
+PluginLoader *
+PluginLoader::m_instance = 0;
+
+PluginLoader::PluginLoader()
+{
+ m_impl = new Impl();
+}
+
+PluginLoader::~PluginLoader()
+{
+ delete m_impl;
+}
+
+PluginLoader *
+PluginLoader::getInstance()
+{
+ if (!m_instance) m_instance = new PluginLoader();
+ return m_instance;
+}
+
+vector<PluginLoader::PluginKey>
+PluginLoader::listPlugins()
+{
+ return m_impl->listPlugins();
+}
+
+Plugin *
+PluginLoader::loadPlugin(PluginKey key,
+ float inputSampleRate,
+ int adapterFlags)
+{
+ return m_impl->loadPlugin(key, inputSampleRate, adapterFlags);
+}
+
+PluginLoader::PluginKey
+PluginLoader::composePluginKey(string libraryName, string identifier)
+{
+ return m_impl->composePluginKey(libraryName, identifier);
+}
+
+PluginLoader::PluginCategoryHierarchy
+PluginLoader::getPluginCategory(PluginKey key)
+{
+ return m_impl->getPluginCategory(key);
+}
+
+string
+PluginLoader::getLibraryPathForPlugin(PluginKey key)
+{
+ return m_impl->getLibraryPathForPlugin(key);
+}
+
+PluginLoader::Impl::Impl() :
+ m_allPluginsEnumerated(false)
+{
+}
+
+PluginLoader::Impl::~Impl()
+{
+}
+
+vector<PluginLoader::PluginKey>
+PluginLoader::Impl::listPlugins()
+{
+ if (!m_allPluginsEnumerated) enumeratePlugins();
+
+ vector<PluginKey> plugins;
+ for (map<PluginKey, string>::iterator mi = m_pluginLibraryNameMap.begin();
+ mi != m_pluginLibraryNameMap.end(); ++mi) {
+ plugins.push_back(mi->first);
+ }
+
+ return plugins;
+}
+
+void
+PluginLoader::Impl::enumeratePlugins(PluginKey forPlugin)
+{
+ vector<string> path = PluginHostAdapter::getPluginPath();
+
+ string libraryName, identifier;
+ if (forPlugin != "") {
+ if (!decomposePluginKey(forPlugin, libraryName, identifier)) {
+ std::cerr << "WARNING: Vamp::HostExt::PluginLoader: Invalid plugin key \""
+ << forPlugin << "\" in enumerate" << std::endl;
+ return;
+ }
+ }
+
+ for (size_t i = 0; i < path.size(); ++i) {
+
+ vector<string> files = listFiles(path[i], PLUGIN_SUFFIX);
+
+ for (vector<string>::iterator fi = files.begin();
+ fi != files.end(); ++fi) {
+
+ if (libraryName != "") {
+ // libraryName is lowercased and lacking an extension,
+ // as it came from the plugin key
+ string temp = *fi;
+ for (size_t i = 0; i < temp.length(); ++i) {
+ temp[i] = tolower(temp[i]);
+ }
+ string::size_type pi = temp.find('.');
+ if (pi == string::npos) {
+ if (libraryName != temp) continue;
+ } else {
+ if (libraryName != temp.substr(0, pi)) continue;
+ }
+ }
+
+ string fullPath = path[i];
+ fullPath = splicePath(fullPath, *fi);
+ void *handle = loadLibrary(fullPath);
+ if (!handle) continue;
+
+ VampGetPluginDescriptorFunction fn =
+ (VampGetPluginDescriptorFunction)lookupInLibrary
+ (handle, "vampGetPluginDescriptor");
+
+ if (!fn) {
+ unloadLibrary(handle);
+ continue;
+ }
+
+ int index = 0;
+ const VampPluginDescriptor *descriptor = 0;
+
+ while ((descriptor = fn(VAMP_API_VERSION, index))) {
+ ++index;
+ if (identifier != "") {
+ if (descriptor->identifier != identifier) continue;
+ }
+ PluginKey key = composePluginKey(*fi, descriptor->identifier);
+// std::cerr << "enumerate: " << key << " (path: " << fullPath << ")" << std::endl;
+ if (m_pluginLibraryNameMap.find(key) ==
+ m_pluginLibraryNameMap.end()) {
+ m_pluginLibraryNameMap[key] = fullPath;
+ }
+ }
+
+ unloadLibrary(handle);
+ }
+ }
+
+ if (forPlugin == "") m_allPluginsEnumerated = true;
+}
+
+PluginLoader::PluginKey
+PluginLoader::Impl::composePluginKey(string libraryName, string identifier)
+{
+ string basename = libraryName;
+
+ string::size_type li = basename.rfind('/');
+ if (li != string::npos) basename = basename.substr(li + 1);
+
+ li = basename.find('.');
+ if (li != string::npos) basename = basename.substr(0, li);
+
+ for (size_t i = 0; i < basename.length(); ++i) {
+ basename[i] = tolower(basename[i]);
+ }
+
+ return basename + ":" + identifier;
+}
+
+bool
+PluginLoader::Impl::decomposePluginKey(PluginKey key,
+ string &libraryName,
+ string &identifier)
+{
+ string::size_type ki = key.find(':');
+ if (ki == string::npos) {
+ return false;
+ }
+
+ libraryName = key.substr(0, ki);
+ identifier = key.substr(ki + 1);
+ return true;
+}
+
+PluginLoader::PluginCategoryHierarchy
+PluginLoader::Impl::getPluginCategory(PluginKey plugin)
+{
+ if (m_taxonomy.empty()) generateTaxonomy();
+ if (m_taxonomy.find(plugin) == m_taxonomy.end()) {
+ return PluginCategoryHierarchy();
+ }
+ return m_taxonomy[plugin];
+}
+
+string
+PluginLoader::Impl::getLibraryPathForPlugin(PluginKey plugin)
+{
+ if (m_pluginLibraryNameMap.find(plugin) == m_pluginLibraryNameMap.end()) {
+ if (m_allPluginsEnumerated) return "";
+ enumeratePlugins(plugin);
+ }
+ if (m_pluginLibraryNameMap.find(plugin) == m_pluginLibraryNameMap.end()) {
+ return "";
+ }
+ return m_pluginLibraryNameMap[plugin];
+}
+
+Plugin *
+PluginLoader::Impl::loadPlugin(PluginKey key,
+ float inputSampleRate, int adapterFlags)
+{
+ string libname, identifier;
+ if (!decomposePluginKey(key, libname, identifier)) {
+ std::cerr << "Vamp::HostExt::PluginLoader: Invalid plugin key \""
+ << key << "\" in loadPlugin" << std::endl;
+ return 0;
+ }
+
+ string fullPath = getLibraryPathForPlugin(key);
+ if (fullPath == "") return 0;
+
+ void *handle = loadLibrary(fullPath);
+ if (!handle) return 0;
+
+ VampGetPluginDescriptorFunction fn =
+ (VampGetPluginDescriptorFunction)lookupInLibrary
+ (handle, "vampGetPluginDescriptor");
+
+ if (!fn) {
+ unloadLibrary(handle);
+ return 0;
+ }
+
+ int index = 0;
+ const VampPluginDescriptor *descriptor = 0;
+
+ while ((descriptor = fn(VAMP_API_VERSION, index))) {
+
+ if (string(descriptor->identifier) == identifier) {
+
+ Vamp::PluginHostAdapter *plugin =
+ new Vamp::PluginHostAdapter(descriptor, inputSampleRate);
+
+ Plugin *adapter = new PluginDeletionNotifyAdapter(plugin, this);
+
+ m_pluginLibraryHandleMap[adapter] = handle;
+
+ if (adapterFlags & ADAPT_INPUT_DOMAIN) {
+ if (adapter->getInputDomain() == Plugin::FrequencyDomain) {
+ adapter = new PluginInputDomainAdapter(adapter);
+ }
+ }
+
+ if (adapterFlags & ADAPT_CHANNEL_COUNT) {
+ adapter = new PluginChannelAdapter(adapter);
+ }
+
+ return adapter;
+ }
+
+ ++index;
+ }
+
+ cerr << "Vamp::HostExt::PluginLoader: Plugin \""
+ << identifier << "\" not found in library \""
+ << fullPath << "\"" << endl;
+
+ return 0;
+}
+
+void
+PluginLoader::Impl::generateTaxonomy()
+{
+// cerr << "PluginLoader::Impl::generateTaxonomy" << endl;
+
+ vector<string> path = PluginHostAdapter::getPluginPath();
+ string libfragment = "/lib/";
+ vector<string> catpath;
+
+ string suffix = "cat";
+
+ for (vector<string>::iterator i = path.begin();
+ i != path.end(); ++i) {
+
+ // It doesn't matter that we're using literal forward-slash in
+ // this bit, as it's only relevant if the path contains
+ // "/lib/", which is only meaningful and only plausible on
+ // systems with forward-slash delimiters
+
+ string dir = *i;
+ string::size_type li = dir.find(libfragment);
+
+ if (li != string::npos) {
+ catpath.push_back
+ (dir.substr(0, li)
+ + "/share/"
+ + dir.substr(li + libfragment.length()));
+ }
+
+ catpath.push_back(dir);
+ }
+
+ char buffer[1024];
+
+ for (vector<string>::iterator i = catpath.begin();
+ i != catpath.end(); ++i) {
+
+ vector<string> files = listFiles(*i, suffix);
+
+ for (vector<string>::iterator fi = files.begin();
+ fi != files.end(); ++fi) {
+
+ string filepath = splicePath(*i, *fi);
+ ifstream is(filepath.c_str(), ifstream::in | ifstream::binary);
+
+ if (is.fail()) {
+// cerr << "failed to open: " << filepath << endl;
+ continue;
+ }
+
+// cerr << "opened: " << filepath << endl;
+
+ while (!!is.getline(buffer, 1024)) {
+
+ string line(buffer);
+
+// cerr << "line = " << line << endl;
+
+ string::size_type di = line.find("::");
+ if (di == string::npos) continue;
+
+ string id = line.substr(0, di);
+ string encodedCat = line.substr(di + 2);
+
+ if (id.substr(0, 5) != "vamp:") continue;
+ id = id.substr(5);
+
+ while (encodedCat.length() >= 1 &&
+ encodedCat[encodedCat.length()-1] == '\r') {
+ encodedCat = encodedCat.substr(0, encodedCat.length()-1);
+ }
+
+// cerr << "id = " << id << ", cat = " << encodedCat << endl;
+
+ PluginCategoryHierarchy category;
+ string::size_type ai;
+ while ((ai = encodedCat.find(" > ")) != string::npos) {
+ category.push_back(encodedCat.substr(0, ai));
+ encodedCat = encodedCat.substr(ai + 3);
+ }
+ if (encodedCat != "") category.push_back(encodedCat);
+
+ m_taxonomy[id] = category;
+ }
+ }
+ }
+}
+
+void *
+PluginLoader::Impl::loadLibrary(string path)
+{
+ void *handle = 0;
+#ifdef _WIN32
+ handle = LoadLibrary(path.c_str());
+ if (!handle) {
+ cerr << "Vamp::HostExt::PluginLoader: Unable to load library \""
+ << path << "\"" << endl;
+ }
+#else
+ handle = dlopen(path.c_str(), RTLD_LAZY);
+ if (!handle) {
+ cerr << "Vamp::HostExt::PluginLoader: Unable to load library \""
+ << path << "\": " << dlerror() << endl;
+ }
+#endif
+ return handle;
+}
+
+void
+PluginLoader::Impl::unloadLibrary(void *handle)
+{
+#ifdef _WIN32
+ FreeLibrary((HINSTANCE)handle);
+#else
+ dlclose(handle);
+#endif
+}
+
+void *
+PluginLoader::Impl::lookupInLibrary(void *handle, const char *symbol)
+{
+#ifdef _WIN32
+ return (void *)GetProcAddress((HINSTANCE)handle, symbol);
+#else
+ return (void *)dlsym(handle, symbol);
+#endif
+}
+
+string
+PluginLoader::Impl::splicePath(string a, string b)
+{
+#ifdef _WIN32
+ return a + "\\" + b;
+#else
+ return a + "/" + b;
+#endif
+}
+
+vector<string>
+PluginLoader::Impl::listFiles(string dir, string extension)
+{
+ vector<string> files;
+
+#ifdef _WIN32
+
+ string expression = dir + "\\*." + extension;
+ WIN32_FIND_DATA data;
+ HANDLE fh = FindFirstFile(expression.c_str(), &data);
+ if (fh == INVALID_HANDLE_VALUE) return files;
+
+ bool ok = true;
+ while (ok) {
+ files.push_back(data.cFileName);
+ ok = FindNextFile(fh, &data);
+ }
+
+ FindClose(fh);
+
+#else
+
+ size_t extlen = extension.length();
+ DIR *d = opendir(dir.c_str());
+ if (!d) return files;
+
+ struct dirent *e = 0;
+ while ((e = readdir(d))) {
+
+ if (!(e->d_type & DT_REG) || !e->d_name) continue;
+
+ size_t len = strlen(e->d_name);
+ if (len < extlen + 2 ||
+ e->d_name + len - extlen - 1 != "." + extension) {
+ continue;
+ }
+
+ files.push_back(e->d_name);
+ }
+
+ closedir(d);
+#endif
+
+ return files;
+}
+
+void
+PluginLoader::Impl::pluginDeleted(PluginDeletionNotifyAdapter *adapter)
+{
+ void *handle = m_pluginLibraryHandleMap[adapter];
+ if (handle) unloadLibrary(handle);
+ m_pluginLibraryHandleMap.erase(adapter);
+}
+
+PluginLoader::Impl::PluginDeletionNotifyAdapter::PluginDeletionNotifyAdapter(Plugin *plugin,
+ Impl *loader) :
+ PluginWrapper(plugin),
+ m_loader(loader)
+{
+}
+
+PluginLoader::Impl::PluginDeletionNotifyAdapter::~PluginDeletionNotifyAdapter()
+{
+ // We need to delete the plugin before calling pluginDeleted, as
+ // the delete call may require calling through to the descriptor
+ // (for e.g. cleanup) but pluginDeleted may unload the required
+ // library for the call. To prevent a double deletion when our
+ // parent's destructor runs (after this one), be sure to set
+ // m_plugin to 0 after deletion.
+ delete m_plugin;
+ m_plugin = 0;
+
+ if (m_loader) m_loader->pluginDeleted(this);
+}
+
+}
+
+}