PluginLoader.cpp

Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Vamp
00005 
00006     An API for audio analysis and feature extraction plugins.
00007 
00008     Centre for Digital Music, Queen Mary, University of London.
00009     Copyright 2006-2007 Chris Cannam and QMUL.
00010   
00011     Permission is hereby granted, free of charge, to any person
00012     obtaining a copy of this software and associated documentation
00013     files (the "Software"), to deal in the Software without
00014     restriction, including without limitation the rights to use, copy,
00015     modify, merge, publish, distribute, sublicense, and/or sell copies
00016     of the Software, and to permit persons to whom the Software is
00017     furnished to do so, subject to the following conditions:
00018 
00019     The above copyright notice and this permission notice shall be
00020     included in all copies or substantial portions of the Software.
00021 
00022     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00023     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00024     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00025     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
00026     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
00027     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00028     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00029 
00030     Except as contained in this notice, the names of the Centre for
00031     Digital Music; Queen Mary, University of London; and Chris Cannam
00032     shall not be used in advertising or otherwise to promote the sale,
00033     use or other dealings in this Software without prior written
00034     authorization.
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> // tolower
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 /* ! _WIN32 */
00055 
00056 #include <dirent.h>
00057 #include <dlfcn.h>
00058 
00059 #ifdef __APPLE__
00060 #define PLUGIN_SUFFIX "dylib"
00061 #else /* ! __APPLE__ */
00062 #define PLUGIN_SUFFIX "so"
00063 #endif /* ! __APPLE__ */
00064 
00065 #endif /* ! _WIN32 */
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         // The cleaner doesn't own the instance, because we leave the
00156         // instance pointer in the base class for binary backwards
00157         // compatibility reasons and to avoid waste
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                 // libraryName is lowercased and lacking an extension,
00248                 // as it came from the plugin key
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 //                std::cerr << "enumerate: " << key << " (path: " << fullPath << ")" << std::endl;
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 //    cerr << "PluginLoader::Impl::generateTaxonomy" << endl;
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         // It doesn't matter that we're using literal forward-slash in
00436         // this bit, as it's only relevant if the path contains
00437         // "/lib/", which is only meaningful and only plausible on
00438         // systems with forward-slash delimiters
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 //                cerr << "failed to open: " << filepath << endl;
00468                 continue;
00469             }
00470 
00471 //            cerr << "opened: " << filepath << endl;
00472 
00473             while (!!is.getline(buffer, 1024)) {
00474 
00475                 string line(buffer);
00476 
00477 //                cerr << "line = " << line << endl;
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 //                cerr << "id = " << id << ", cat = " << encodedCat << endl;
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     // We need to delete the plugin before calling pluginDeleted, as
00623     // the delete call may require calling through to the descriptor
00624     // (for e.g. cleanup) but pluginDeleted may unload the required
00625     // library for the call.  To prevent a double deletion when our
00626     // parent's destructor runs (after this one), be sure to set
00627     // m_plugin to 0 after deletion.
00628     delete m_plugin;
00629     m_plugin = 0;
00630 
00631     if (m_loader) m_loader->pluginDeleted(this);
00632 }
00633 
00634 }
00635 
00636 }

Generated on Fri Nov 7 13:10:34 2008 for VampPluginSDK by  doxygen 1.5.6