changeset 40:ae3e47e76d2d

* Add plugin path traversal and plugin listing option to vamp-simple-host * Add more notes on plugin lookup and categorisation
author cannam
date Mon, 09 Oct 2006 12:45:14 +0000
parents 46c39790588b
children 31cd55174467
files README host/system.h host/vamp-simple-host.cpp vamp-sdk/PluginAdapter.cpp
diffstat 4 files changed, 160 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/README	Thu Sep 21 16:41:10 2006 +0000
+++ b/README	Mon Oct 09 12:45:14 2006 +0000
@@ -94,13 +94,36 @@
 it to process a complete audio file, with its default parameters.
 Requires libsndfile.
 
-The Vamp API doesn't officially specify how to load plugin libraries
-or where to find them.  However, good practice for a host is to use
-the Vamp path returned by Vamp::PluginHostAdapter::getPluginPath() and
-search each directory in this path for .so, .dll or .dylib files
-(depending on platform), loading each one and testing for the
-vampGetPluginDescriptor function to enumerate the plugins in this
-object.  The example host has some code that may help.
+
+Plugin Lookup and Categorisation
+================================
+
+The Vamp API does not officially specify how to load plugin libraries
+or where to find them.  However, the SDK does include a function
+(Vamp::PluginHostAdapter::getPluginPath()) that returns a recommended
+directory search path that hosts may use for plugin libraries.
+
+Our suggestion for a host is to search each directory in this path for
+.DLL (on Windows), .so (on Linux, Solaris, BSD etc) or .dylib (on
+OS/X) files, then to load each one and perform a dynamic name lookup
+on the vampGetPluginDescriptor function to enumerate the plugins in
+the library.  The example host has some code that may help, but this
+operation will necessarily be system-dependent.
+
+Vamp also has an informal convention for sorting plugins into
+functional categories.  In addition to the library file itself, a
+plugin library may install a category file with the same name as the
+library but .cat extension.  The existence and format of this file are
+not specified by the Vamp API, but by convention the file may contain
+lines of the format
+
+vamp:pluginlibrary:pluginname::General Category > Specific Category
+
+which a host may read and use to assign plugins a location within a
+category tree for display to the user.  The expectation is that
+advanced users may also choose to set up their own preferred category
+trees, which is why this information is not queried as part of the
+Vamp API itself.
 
 
 Building the SDK
--- a/host/system.h	Thu Sep 21 16:41:10 2006 +0000
+++ b/host/system.h	Mon Oct 09 12:45:14 2006 +0000
@@ -46,7 +46,7 @@
 #define DLCLOSE(a)   FreeLibrary((HINSTANCE)(a))
 #define DLERROR()    ""
 
-#define PLUGIN_GLOB  "*.dll"
+#define PLUGIN_SUFFIX "dll"
 
 #else
 
@@ -59,11 +59,13 @@
 
 #ifdef __APPLE__
 
-#define PLUGIN_GLOB  "*.dylib"
+#define PLUGIN_SUFFIX  "dylib"
+#define HAVE_OPENDIR 1
 
 #else 
 
-#define PLUGIN_GLOB  "*.so"
+#define PLUGIN_SUFFIX  "so"
+#define HAVE_OPENDIR 1
 
 #endif /* __APPLE__ */
 
--- a/host/vamp-simple-host.cpp	Thu Sep 21 16:41:10 2006 +0000
+++ b/host/vamp-simple-host.cpp	Mon Oct 09 12:45:14 2006 +0000
@@ -40,6 +40,7 @@
 
 #include <iostream>
 #include <sndfile.h>
+#include <dirent.h> // POSIX directory open and read
 
 #include "system.h"
 
@@ -51,9 +52,16 @@
 using std::string;
 using std::vector;
 
+
+
 void printFeatures(int, int, int, Vamp::Plugin::FeatureSet);
 void transformInput(float *, size_t);
 void fft(unsigned int, bool, double *, double *, double *, double *);
+void printPluginPath();
+
+#ifdef HAVE_OPENDIR
+void enumeratePlugins();
+#endif
 
 /*
     A very simple Vamp plugin host.  Given the name of a plugin
@@ -65,26 +73,56 @@
 int main(int argc, char **argv)
 {
     if (argc < 2 || argc > 4) {
-        cerr << "Usage: " << argv[0] << " pluginlibrary.so[:plugin] [file.wav] [outputno]" << endl;
+        char *scooter = argv[0];
+        char *name = 0;
+        while (scooter && *scooter) {
+            if (*scooter == '/' || *scooter == '\\') name = ++scooter;
+            else ++scooter;
+        }
+        if (!name || !*name) name = argv[0];
+        cerr << "\n"
+             << name << ": A simple Vamp plugin host.\n\n"
+            "Centre for Digital Music, Queen Mary, University of London.\n"
+            "Copyright 2006 Chris Cannam and QMUL.\n"
+            "Freely redistributable; published under a BSD-style license.\n\n"
+            "Usage:\n\n"
+            "  " << name << " pluginlibrary." << PLUGIN_SUFFIX << "\n\n"
+            "    -- Load \"pluginlibrary\" and list the Vamp plugins it contains.\n\n"
+            "  " << name << " pluginlibrary." << PLUGIN_SUFFIX << ":plugin file.wav [outputno]\n\n"
+            "    -- Load plugin id \"plugin\" from \"pluginlibrary\" and run it on the\n"
+            "       audio data in \"file.wav\", dumping the output from \"outputno\"\n"
+            "       (default 0) to standard output.\n\n"
+#ifdef HAVE_OPENDIR
+            "  " << name << " -l\n\n"
+            "    -- List the plugin libraries and Vamp plugins in the plugin search path.\n\n"
+#endif
+            "  " << name << " -p\n\n"
+            "    -- Print out the Vamp plugin search path.\n\n"
+            "Note that this host does not use the plugin search path when loading a plugin.\nIf a plugin library is specified, it should be with a full file path.\n"
+             << endl;
         return 2;
     }
 
+    if (argc == 2 && !strcmp(argv[1], "-l")) {
+#ifdef HAVE_OPENDIR
+        enumeratePlugins();
+#endif
+        return 0;
+    }
+    if (argc == 2 && !strcmp(argv[1], "-p")) {
+        printPluginPath();
+        return 0;
+    }
+
     cerr << endl << argv[0] << ": Running..." << endl;
 
-    cerr << endl << "Vamp path is set to:" << endl;
-    vector<string> path = Vamp::PluginHostAdapter::getPluginPath();
-    for (size_t i = 0; i < path.size(); ++i) {
-        cerr << "\t" << path[i] << endl;
-    }
-    cerr << "(This program doesn't use the path; just printing it for information)" << endl << endl;
-
     string soname = argv[1];
     string plugname = "";
     string wavname;
     if (argc >= 3) wavname = argv[2];
 
     int sep = soname.find(":");
-    if (sep >= 0 && sep < soname.length()) {
+    if (sep >= 0 && sep < int(soname.length())) {
         plugname = soname.substr(sep + 1);
         soname = soname.substr(0, sep);
     }
@@ -300,6 +338,78 @@
 }
 
 void
+printPluginPath()
+{
+    vector<string> path = Vamp::PluginHostAdapter::getPluginPath();
+    for (size_t i = 0; i < path.size(); ++i) {
+        cerr << path[i] << endl;
+    }
+}
+
+#ifdef HAVE_OPENDIR
+
+void
+enumeratePlugins()
+{
+    cerr << endl << "Vamp plugin libraries found in search path:" << endl;
+    vector<string> path = Vamp::PluginHostAdapter::getPluginPath();
+    for (size_t i = 0; i < path.size(); ++i) {
+        cerr << "\n" << path[i] << ":" << endl;
+        DIR *d = opendir(path[i].c_str());
+        if (!d) {
+            perror("Failed to open directory");
+            continue;
+        }
+        struct dirent *e = 0;
+        while ((e = readdir(d))) {
+            if (!(e->d_type & DT_REG)) continue;
+            int len = strlen(e->d_name);
+            if (len < int(strlen(PLUGIN_SUFFIX) + 2) ||
+                e->d_name[len - strlen(PLUGIN_SUFFIX) - 1] != '.' ||
+                strcmp(e->d_name + len - strlen(PLUGIN_SUFFIX), PLUGIN_SUFFIX)) {
+                continue;
+            }
+            char *fp = new char[path[i].length() + len + 3];
+            sprintf(fp, "%s/%s", path[i].c_str(), e->d_name);
+            void *handle = DLOPEN(string(fp), RTLD_LAZY);
+            if (handle) {
+                VampGetPluginDescriptorFunction fn =
+                    (VampGetPluginDescriptorFunction)DLSYM
+                    (handle, "vampGetPluginDescriptor");
+                if (fn) {
+                    cerr << "\n  " << e->d_name << ":" << endl;
+                    int index = 0;
+                    const VampPluginDescriptor *descriptor = 0;
+                    while ((descriptor = fn(index))) {
+                        Vamp::PluginHostAdapter plugin(descriptor, 48000);
+                        cerr << "    [" << char('A' + index) << "] "
+                             << plugin.getDescription()
+                             << ", \"" << plugin.getName() << "\""
+                             << " [" << plugin.getMaker()
+                             << "]" << std::endl;
+                        Vamp::Plugin::OutputList outputs =
+                            plugin.getOutputDescriptors();
+                        if (outputs.size() > 1) {
+                            for (size_t j = 0; j < outputs.size(); ++j) {
+                                cerr << "         (" << j << ") "
+                                     << outputs[j].description << endl;
+                            }
+                        }
+                        ++index;
+                    }
+                }
+                DLCLOSE(handle);
+            }
+        }
+        closedir(d);
+    }
+    cerr << endl;
+}
+
+#endif
+
+
+void
 printFeatures(int frame, int sr, int output, Vamp::Plugin::FeatureSet features)
 {
     for (unsigned int i = 0; i < features[output].size(); ++i) {
--- a/vamp-sdk/PluginAdapter.cpp	Thu Sep 21 16:41:10 2006 +0000
+++ b/vamp-sdk/PluginAdapter.cpp	Mon Oct 09 12:45:14 2006 +0000
@@ -410,8 +410,12 @@
     if (desc->name) free((void *)desc->name);
     if (desc->description) free((void *)desc->description);
     if (desc->unit) free((void *)desc->unit);
-    for (unsigned int i = 0; i < desc->binCount; ++i) {
-        free((void *)desc->binNames[i]);
+    if (desc->hasFixedBinCount && desc->binNames) {
+        for (unsigned int i = 0; i < desc->binCount; ++i) {
+            if (desc->binNames[i]) {
+                free((void *)desc->binNames[i]);
+            }
+        }
     }
     if (desc->binNames) free((void *)desc->binNames);
     free((void *)desc);