00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 #include "vamp-sdk/PluginHostAdapter.h"
00038 #include "PluginLoader.h"
00039 #include "PluginInputDomainAdapter.h"
00040 #include "PluginChannelAdapter.h"
00041 #include "PluginBufferingAdapter.h"
00042
00043 #include <fstream>
00044 #include <cctype>
00045
00046 #include <cstring>
00047
00048 #ifdef _WIN32
00049
00050 #include <windows.h>
00051 #include <tchar.h>
00052 #define PLUGIN_SUFFIX "dll"
00053
00054 #else
00055
00056 #include <dirent.h>
00057 #include <dlfcn.h>
00058
00059 #ifdef __APPLE__
00060 #define PLUGIN_SUFFIX "dylib"
00061 #else
00062 #define PLUGIN_SUFFIX "so"
00063 #endif
00064
00065 #endif
00066
00067 using namespace std;
00068
00069 namespace Vamp {
00070
00071 namespace HostExt {
00072
00073 class PluginLoader::Impl
00074 {
00075 public:
00076 Impl();
00077 virtual ~Impl();
00078
00079 PluginKeyList listPlugins();
00080
00081 Plugin *loadPlugin(PluginKey key,
00082 float inputSampleRate,
00083 int adapterFlags);
00084
00085 PluginKey composePluginKey(string libraryName, string identifier);
00086
00087 PluginCategoryHierarchy getPluginCategory(PluginKey key);
00088
00089 string getLibraryPathForPlugin(PluginKey key);
00090
00091 static void setInstanceToClean(PluginLoader *instance);
00092
00093 protected:
00094 class PluginDeletionNotifyAdapter : public PluginWrapper {
00095 public:
00096 PluginDeletionNotifyAdapter(Plugin *plugin, Impl *loader);
00097 virtual ~PluginDeletionNotifyAdapter();
00098 protected:
00099 Impl *m_loader;
00100 };
00101
00102 class InstanceCleaner {
00103 public:
00104 InstanceCleaner() : m_instance(0) { }
00105 ~InstanceCleaner() { delete m_instance; }
00106 void setInstance(PluginLoader *instance) { m_instance = instance; }
00107 protected:
00108 PluginLoader *m_instance;
00109 };
00110
00111 virtual void pluginDeleted(PluginDeletionNotifyAdapter *adapter);
00112
00113 map<PluginKey, string> m_pluginLibraryNameMap;
00114 bool m_allPluginsEnumerated;
00115 void enumeratePlugins(PluginKey forPlugin = "");
00116
00117 map<PluginKey, PluginCategoryHierarchy> m_taxonomy;
00118 void generateTaxonomy();
00119
00120 map<Plugin *, void *> m_pluginLibraryHandleMap;
00121
00122 bool decomposePluginKey(PluginKey key,
00123 string &libraryName, string &identifier);
00124
00125 void *loadLibrary(string path);
00126 void unloadLibrary(void *handle);
00127 void *lookupInLibrary(void *handle, const char *symbol);
00128
00129 string splicePath(string a, string b);
00130 vector<string> listFiles(string dir, string ext);
00131
00132 static InstanceCleaner m_cleaner;
00133 };
00134
00135 PluginLoader *
00136 PluginLoader::m_instance = 0;
00137
00138 PluginLoader::Impl::InstanceCleaner
00139 PluginLoader::Impl::m_cleaner;
00140
00141 PluginLoader::PluginLoader()
00142 {
00143 m_impl = new Impl();
00144 }
00145
00146 PluginLoader::~PluginLoader()
00147 {
00148 delete m_impl;
00149 }
00150
00151 PluginLoader *
00152 PluginLoader::getInstance()
00153 {
00154 if (!m_instance) {
00155
00156
00157
00158 m_instance = new PluginLoader();
00159 Impl::setInstanceToClean(m_instance);
00160 }
00161 return m_instance;
00162 }
00163
00164 vector<PluginLoader::PluginKey>
00165 PluginLoader::listPlugins()
00166 {
00167 return m_impl->listPlugins();
00168 }
00169
00170 Plugin *
00171 PluginLoader::loadPlugin(PluginKey key,
00172 float inputSampleRate,
00173 int adapterFlags)
00174 {
00175 return m_impl->loadPlugin(key, inputSampleRate, adapterFlags);
00176 }
00177
00178 PluginLoader::PluginKey
00179 PluginLoader::composePluginKey(string libraryName, string identifier)
00180 {
00181 return m_impl->composePluginKey(libraryName, identifier);
00182 }
00183
00184 PluginLoader::PluginCategoryHierarchy
00185 PluginLoader::getPluginCategory(PluginKey key)
00186 {
00187 return m_impl->getPluginCategory(key);
00188 }
00189
00190 string
00191 PluginLoader::getLibraryPathForPlugin(PluginKey key)
00192 {
00193 return m_impl->getLibraryPathForPlugin(key);
00194 }
00195
00196 PluginLoader::Impl::Impl() :
00197 m_allPluginsEnumerated(false)
00198 {
00199 }
00200
00201 PluginLoader::Impl::~Impl()
00202 {
00203 }
00204
00205 void
00206 PluginLoader::Impl::setInstanceToClean(PluginLoader *instance)
00207 {
00208 m_cleaner.setInstance(instance);
00209 }
00210
00211 vector<PluginLoader::PluginKey>
00212 PluginLoader::Impl::listPlugins()
00213 {
00214 if (!m_allPluginsEnumerated) enumeratePlugins();
00215
00216 vector<PluginKey> plugins;
00217 for (map<PluginKey, string>::iterator mi = m_pluginLibraryNameMap.begin();
00218 mi != m_pluginLibraryNameMap.end(); ++mi) {
00219 plugins.push_back(mi->first);
00220 }
00221
00222 return plugins;
00223 }
00224
00225 void
00226 PluginLoader::Impl::enumeratePlugins(PluginKey forPlugin)
00227 {
00228 vector<string> path = PluginHostAdapter::getPluginPath();
00229
00230 string libraryName, identifier;
00231 if (forPlugin != "") {
00232 if (!decomposePluginKey(forPlugin, libraryName, identifier)) {
00233 std::cerr << "WARNING: Vamp::HostExt::PluginLoader: Invalid plugin key \""
00234 << forPlugin << "\" in enumerate" << std::endl;
00235 return;
00236 }
00237 }
00238
00239 for (size_t i = 0; i < path.size(); ++i) {
00240
00241 vector<string> files = listFiles(path[i], PLUGIN_SUFFIX);
00242
00243 for (vector<string>::iterator fi = files.begin();
00244 fi != files.end(); ++fi) {
00245
00246 if (libraryName != "") {
00247
00248
00249 string temp = *fi;
00250 for (size_t i = 0; i < temp.length(); ++i) {
00251 temp[i] = tolower(temp[i]);
00252 }
00253 string::size_type pi = temp.find('.');
00254 if (pi == string::npos) {
00255 if (libraryName != temp) continue;
00256 } else {
00257 if (libraryName != temp.substr(0, pi)) continue;
00258 }
00259 }
00260
00261 string fullPath = path[i];
00262 fullPath = splicePath(fullPath, *fi);
00263 void *handle = loadLibrary(fullPath);
00264 if (!handle) continue;
00265
00266 VampGetPluginDescriptorFunction fn =
00267 (VampGetPluginDescriptorFunction)lookupInLibrary
00268 (handle, "vampGetPluginDescriptor");
00269
00270 if (!fn) {
00271 unloadLibrary(handle);
00272 continue;
00273 }
00274
00275 int index = 0;
00276 const VampPluginDescriptor *descriptor = 0;
00277
00278 while ((descriptor = fn(VAMP_API_VERSION, index))) {
00279 ++index;
00280 if (identifier != "") {
00281 if (descriptor->identifier != identifier) continue;
00282 }
00283 PluginKey key = composePluginKey(*fi, descriptor->identifier);
00284
00285 if (m_pluginLibraryNameMap.find(key) ==
00286 m_pluginLibraryNameMap.end()) {
00287 m_pluginLibraryNameMap[key] = fullPath;
00288 }
00289 }
00290
00291 unloadLibrary(handle);
00292 }
00293 }
00294
00295 if (forPlugin == "") m_allPluginsEnumerated = true;
00296 }
00297
00298 PluginLoader::PluginKey
00299 PluginLoader::Impl::composePluginKey(string libraryName, string identifier)
00300 {
00301 string basename = libraryName;
00302
00303 string::size_type li = basename.rfind('/');
00304 if (li != string::npos) basename = basename.substr(li + 1);
00305
00306 li = basename.find('.');
00307 if (li != string::npos) basename = basename.substr(0, li);
00308
00309 for (size_t i = 0; i < basename.length(); ++i) {
00310 basename[i] = tolower(basename[i]);
00311 }
00312
00313 return basename + ":" + identifier;
00314 }
00315
00316 bool
00317 PluginLoader::Impl::decomposePluginKey(PluginKey key,
00318 string &libraryName,
00319 string &identifier)
00320 {
00321 string::size_type ki = key.find(':');
00322 if (ki == string::npos) {
00323 return false;
00324 }
00325
00326 libraryName = key.substr(0, ki);
00327 identifier = key.substr(ki + 1);
00328 return true;
00329 }
00330
00331 PluginLoader::PluginCategoryHierarchy
00332 PluginLoader::Impl::getPluginCategory(PluginKey plugin)
00333 {
00334 if (m_taxonomy.empty()) generateTaxonomy();
00335 if (m_taxonomy.find(plugin) == m_taxonomy.end()) {
00336 return PluginCategoryHierarchy();
00337 }
00338 return m_taxonomy[plugin];
00339 }
00340
00341 string
00342 PluginLoader::Impl::getLibraryPathForPlugin(PluginKey plugin)
00343 {
00344 if (m_pluginLibraryNameMap.find(plugin) == m_pluginLibraryNameMap.end()) {
00345 if (m_allPluginsEnumerated) return "";
00346 enumeratePlugins(plugin);
00347 }
00348 if (m_pluginLibraryNameMap.find(plugin) == m_pluginLibraryNameMap.end()) {
00349 return "";
00350 }
00351 return m_pluginLibraryNameMap[plugin];
00352 }
00353
00354 Plugin *
00355 PluginLoader::Impl::loadPlugin(PluginKey key,
00356 float inputSampleRate, int adapterFlags)
00357 {
00358 string libname, identifier;
00359 if (!decomposePluginKey(key, libname, identifier)) {
00360 std::cerr << "Vamp::HostExt::PluginLoader: Invalid plugin key \""
00361 << key << "\" in loadPlugin" << std::endl;
00362 return 0;
00363 }
00364
00365 string fullPath = getLibraryPathForPlugin(key);
00366 if (fullPath == "") return 0;
00367
00368 void *handle = loadLibrary(fullPath);
00369 if (!handle) return 0;
00370
00371 VampGetPluginDescriptorFunction fn =
00372 (VampGetPluginDescriptorFunction)lookupInLibrary
00373 (handle, "vampGetPluginDescriptor");
00374
00375 if (!fn) {
00376 unloadLibrary(handle);
00377 return 0;
00378 }
00379
00380 int index = 0;
00381 const VampPluginDescriptor *descriptor = 0;
00382
00383 while ((descriptor = fn(VAMP_API_VERSION, index))) {
00384
00385 if (string(descriptor->identifier) == identifier) {
00386
00387 Vamp::PluginHostAdapter *plugin =
00388 new Vamp::PluginHostAdapter(descriptor, inputSampleRate);
00389
00390 Plugin *adapter = new PluginDeletionNotifyAdapter(plugin, this);
00391
00392 m_pluginLibraryHandleMap[adapter] = handle;
00393
00394 if (adapterFlags & ADAPT_INPUT_DOMAIN) {
00395 if (adapter->getInputDomain() == Plugin::FrequencyDomain) {
00396 adapter = new PluginInputDomainAdapter(adapter);
00397 }
00398 }
00399
00400 if (adapterFlags & ADAPT_BUFFER_SIZE) {
00401 adapter = new PluginBufferingAdapter(adapter);
00402 }
00403
00404 if (adapterFlags & ADAPT_CHANNEL_COUNT) {
00405 adapter = new PluginChannelAdapter(adapter);
00406 }
00407
00408 return adapter;
00409 }
00410
00411 ++index;
00412 }
00413
00414 cerr << "Vamp::HostExt::PluginLoader: Plugin \""
00415 << identifier << "\" not found in library \""
00416 << fullPath << "\"" << endl;
00417
00418 return 0;
00419 }
00420
00421 void
00422 PluginLoader::Impl::generateTaxonomy()
00423 {
00424
00425
00426 vector<string> path = PluginHostAdapter::getPluginPath();
00427 string libfragment = "/lib/";
00428 vector<string> catpath;
00429
00430 string suffix = "cat";
00431
00432 for (vector<string>::iterator i = path.begin();
00433 i != path.end(); ++i) {
00434
00435
00436
00437
00438
00439
00440 string dir = *i;
00441 string::size_type li = dir.find(libfragment);
00442
00443 if (li != string::npos) {
00444 catpath.push_back
00445 (dir.substr(0, li)
00446 + "/share/"
00447 + dir.substr(li + libfragment.length()));
00448 }
00449
00450 catpath.push_back(dir);
00451 }
00452
00453 char buffer[1024];
00454
00455 for (vector<string>::iterator i = catpath.begin();
00456 i != catpath.end(); ++i) {
00457
00458 vector<string> files = listFiles(*i, suffix);
00459
00460 for (vector<string>::iterator fi = files.begin();
00461 fi != files.end(); ++fi) {
00462
00463 string filepath = splicePath(*i, *fi);
00464 ifstream is(filepath.c_str(), ifstream::in | ifstream::binary);
00465
00466 if (is.fail()) {
00467
00468 continue;
00469 }
00470
00471
00472
00473 while (!!is.getline(buffer, 1024)) {
00474
00475 string line(buffer);
00476
00477
00478
00479 string::size_type di = line.find("::");
00480 if (di == string::npos) continue;
00481
00482 string id = line.substr(0, di);
00483 string encodedCat = line.substr(di + 2);
00484
00485 if (id.substr(0, 5) != "vamp:") continue;
00486 id = id.substr(5);
00487
00488 while (encodedCat.length() >= 1 &&
00489 encodedCat[encodedCat.length()-1] == '\r') {
00490 encodedCat = encodedCat.substr(0, encodedCat.length()-1);
00491 }
00492
00493
00494
00495 PluginCategoryHierarchy category;
00496 string::size_type ai;
00497 while ((ai = encodedCat.find(" > ")) != string::npos) {
00498 category.push_back(encodedCat.substr(0, ai));
00499 encodedCat = encodedCat.substr(ai + 3);
00500 }
00501 if (encodedCat != "") category.push_back(encodedCat);
00502
00503 m_taxonomy[id] = category;
00504 }
00505 }
00506 }
00507 }
00508
00509 void *
00510 PluginLoader::Impl::loadLibrary(string path)
00511 {
00512 void *handle = 0;
00513 #ifdef _WIN32
00514 handle = LoadLibrary(path.c_str());
00515 if (!handle) {
00516 cerr << "Vamp::HostExt::PluginLoader: Unable to load library \""
00517 << path << "\"" << endl;
00518 }
00519 #else
00520 handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
00521 if (!handle) {
00522 cerr << "Vamp::HostExt::PluginLoader: Unable to load library \""
00523 << path << "\": " << dlerror() << endl;
00524 }
00525 #endif
00526 return handle;
00527 }
00528
00529 void
00530 PluginLoader::Impl::unloadLibrary(void *handle)
00531 {
00532 #ifdef _WIN32
00533 FreeLibrary((HINSTANCE)handle);
00534 #else
00535 dlclose(handle);
00536 #endif
00537 }
00538
00539 void *
00540 PluginLoader::Impl::lookupInLibrary(void *handle, const char *symbol)
00541 {
00542 #ifdef _WIN32
00543 return (void *)GetProcAddress((HINSTANCE)handle, symbol);
00544 #else
00545 return (void *)dlsym(handle, symbol);
00546 #endif
00547 }
00548
00549 string
00550 PluginLoader::Impl::splicePath(string a, string b)
00551 {
00552 #ifdef _WIN32
00553 return a + "\\" + b;
00554 #else
00555 return a + "/" + b;
00556 #endif
00557 }
00558
00559 vector<string>
00560 PluginLoader::Impl::listFiles(string dir, string extension)
00561 {
00562 vector<string> files;
00563
00564 #ifdef _WIN32
00565
00566 string expression = dir + "\\*." + extension;
00567 WIN32_FIND_DATA data;
00568 HANDLE fh = FindFirstFile(expression.c_str(), &data);
00569 if (fh == INVALID_HANDLE_VALUE) return files;
00570
00571 bool ok = true;
00572 while (ok) {
00573 files.push_back(data.cFileName);
00574 ok = FindNextFile(fh, &data);
00575 }
00576
00577 FindClose(fh);
00578
00579 #else
00580
00581 size_t extlen = extension.length();
00582 DIR *d = opendir(dir.c_str());
00583 if (!d) return files;
00584
00585 struct dirent *e = 0;
00586 while ((e = readdir(d))) {
00587
00588 if (!e->d_name) continue;
00589
00590 size_t len = strlen(e->d_name);
00591 if (len < extlen + 2 ||
00592 e->d_name + len - extlen - 1 != "." + extension) {
00593 continue;
00594 }
00595
00596 files.push_back(e->d_name);
00597 }
00598
00599 closedir(d);
00600 #endif
00601
00602 return files;
00603 }
00604
00605 void
00606 PluginLoader::Impl::pluginDeleted(PluginDeletionNotifyAdapter *adapter)
00607 {
00608 void *handle = m_pluginLibraryHandleMap[adapter];
00609 if (handle) unloadLibrary(handle);
00610 m_pluginLibraryHandleMap.erase(adapter);
00611 }
00612
00613 PluginLoader::Impl::PluginDeletionNotifyAdapter::PluginDeletionNotifyAdapter(Plugin *plugin,
00614 Impl *loader) :
00615 PluginWrapper(plugin),
00616 m_loader(loader)
00617 {
00618 }
00619
00620 PluginLoader::Impl::PluginDeletionNotifyAdapter::~PluginDeletionNotifyAdapter()
00621 {
00622
00623
00624
00625
00626
00627
00628 delete m_plugin;
00629 m_plugin = 0;
00630
00631 if (m_loader) m_loader->pluginDeleted(this);
00632 }
00633
00634 }
00635
00636 }