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