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 #include <cstring>
00046 
00047 #ifdef _WIN32
00048 
00049 #include <windows.h>
00050 #include <tchar.h>
00051 #define PLUGIN_SUFFIX "dll"
00052 
00053 #else /* ! _WIN32 */
00054 
00055 #include <dirent.h>
00056 #include <dlfcn.h>
00057 
00058 #ifdef __APPLE__
00059 #define PLUGIN_SUFFIX "dylib"
00060 #else /* ! __APPLE__ */
00061 #define PLUGIN_SUFFIX "so"
00062 #endif /* ! __APPLE__ */
00063 
00064 #endif /* ! _WIN32 */
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         // The cleaner doesn't own the instance, because we leave the
00155         // instance pointer in the base class for binary backwards
00156         // compatibility reasons and to avoid waste
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                 // libraryName is lowercased and lacking an extension,
00247                 // as it came from the plugin key
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 //                std::cerr << "enumerate: " << key << " (path: " << fullPath << ")" << std::endl;
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 //    cerr << "PluginLoader::Impl::generateTaxonomy" << endl;
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         // It doesn't matter that we're using literal forward-slash in
00435         // this bit, as it's only relevant if the path contains
00436         // "/lib/", which is only meaningful and only plausible on
00437         // systems with forward-slash delimiters
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 //                cerr << "failed to open: " << filepath << endl;
00467                 continue;
00468             }
00469 
00470 //            cerr << "opened: " << filepath << endl;
00471 
00472             while (!!is.getline(buffer, 1024)) {
00473 
00474                 string line(buffer);
00475 
00476 //                cerr << "line = " << line << endl;
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 //                cerr << "id = " << id << ", cat = " << encodedCat << endl;
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     // We need to delete the plugin before calling pluginDeleted, as
00624     // the delete call may require calling through to the descriptor
00625     // (for e.g. cleanup) but pluginDeleted may unload the required
00626     // library for the call.  To prevent a double deletion when our
00627     // parent's destructor runs (after this one), be sure to set
00628     // m_plugin to 0 after deletion.
00629     delete m_plugin;
00630     m_plugin = 0;
00631 
00632     if (m_loader) m_loader->pluginDeleted(this);
00633 }
00634 
00635 }
00636 
00637 }

Generated on Tue Jun 17 21:12:15 2008 for VampPluginSDK by  doxygen 1.5.6