diff host/vamp-simple-host.cpp @ 59:fa79c4ec847d host-factory-stuff

* Put hostext stuff in the HostExt sub-namespace * Tidy up system-specific stuff in PluginLoader * Make PluginLoader return a deletion-notifying wrapper which permits the library to be unloaded when no longer in use * Add PluginChannelAdapter * Make vamp-simple-host use PluginChannelAdapter, and use the PluginLoader for plugin-running task. Also some other enhancements to host
author cannam
date Thu, 24 May 2007 15:17:07 +0000
parents 09a1aac6c362
children 97c5ac99d725
line wrap: on
line diff
--- a/host/vamp-simple-host.cpp	Thu May 24 10:05:00 2007 +0000
+++ b/host/vamp-simple-host.cpp	Thu May 24 15:17:07 2007 +0000
@@ -35,10 +35,11 @@
     authorization.
 */
 
-#include "PluginHostAdapter.h"
-#include "PluginInputDomainAdapter.h"
-#include "PluginLoader.h"
-#include "vamp.h"
+#include "vamp-sdk/PluginHostAdapter.h"
+#include "vamp-sdk/hostext/PluginChannelAdapter.h"
+#include "vamp-sdk/hostext/PluginInputDomainAdapter.h"
+#include "vamp-sdk/hostext/PluginLoader.h"
+#include "vamp/vamp.h"
 
 #include <iostream>
 #include <sndfile.h>
@@ -53,61 +54,62 @@
 using std::string;
 using std::vector;
 
-#define HOST_VERSION "1.0"
+using Vamp::HostExt::PluginLoader;
+
+#define HOST_VERSION "1.1"
 
 void printFeatures(int, int, int, Vamp::Plugin::FeatureSet);
 void transformInput(float *, size_t);
 void fft(unsigned int, bool, double *, double *, double *, double *);
-void printPluginPath();
+void printPluginPath(bool verbose);
 void enumeratePlugins();
+void listPluginsInLibrary(string soname);
+int runPlugin(string myname, string soname, string id, string output,
+              int outputNo, string inputFile);
 
-/*
-    A very simple Vamp plugin host.  Given the name of a plugin
-    library and the name of a sound file on the command line, it loads
-    the first plugin in the library and runs it on the sound file,
-    dumping the plugin's first output to stdout.
-*/
+void usage(const char *name)
+{
+    cerr << "\n"
+         << name << ": A simple Vamp plugin host.\n\n"
+        "Centre for Digital Music, Queen Mary, University of London.\n"
+        "Copyright 2006-2007 Chris Cannam and QMUL.\n"
+        "Freely redistributable; published under a BSD-style license.\n\n"
+        "Usage:\n\n"
+        "  " << name << " pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin[:output] file.wav\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\"; retrieve the named \"output\", or output\n"
+        "       number \"outputno\" (the first output by default) and dump it to\n"
+        "       standard output.\n\n"
+        "  " << name << " -l\n\n"
+        "    -- List the plugin libraries and Vamp plugins in the plugin search path.\n\n"
+        "  " << name << " -p\n\n"
+        "    -- Print out the Vamp plugin search path.\n\n"
+        "  " << name << " -v\n\n"
+        "    -- Display version information only.\n\n"
+         << endl;
+    exit(2);
+}
 
 int main(int argc, char **argv)
 {
+    char *scooter = argv[0];
+    char *name = 0;
+    while (scooter && *scooter) {
+        if (*scooter == '/' || *scooter == '\\') name = ++scooter;
+        else ++scooter;
+    }
+    if (!name || !*name) name = argv[0];
+    
     if (argc < 2 || argc > 4 ||
         (argc == 2 &&
          (!strcmp(argv[1], "-?") ||
           !strcmp(argv[1], "-h") ||
           !strcmp(argv[1], "--help")))) {
 
-        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"
-	    "  " << name << " -v\n\n"
-	    "    -- Display version information only.\n\n"
-	    "Note that this host does not use the plugin search path when loadinga plugin.\nIf a plugin library is specified, it should be with a full file path.\n"
-             << endl;
-        return 2;
+        usage(name); // does not return
     }
-    
+
     if (argc == 2 && !strcmp(argv[1], "-v")) {
 	cout << "Simple Vamp plugin host version: " << HOST_VERSION << endl
 	     << "Vamp API version: " << VAMP_API_VERSION << endl
@@ -116,87 +118,57 @@
     }
     
     if (argc == 2 && !strcmp(argv[1], "-l")) {
+        printPluginPath(true);
         enumeratePlugins();
         return 0;
     }
     if (argc == 2 && !strcmp(argv[1], "-p")) {
-        printPluginPath();
+        printPluginPath(false);
         return 0;
     }
 
-    cerr << endl << argv[0] << ": Running..." << endl;
+    cerr << endl << name << ": Running..." << endl;
 
     string soname = argv[1];
     string plugid = "";
+    string output = "";
+    int outputNo = -1;
     string wavname;
     if (argc >= 3) wavname = argv[2];
 
-    int sep = soname.find(":");
-    if (sep >= 0 && sep < int(soname.length())) {
+    string::size_type sep = soname.find(':');
+
+    if (sep != string::npos) {
         plugid = soname.substr(sep + 1);
         soname = soname.substr(0, sep);
-    }
 
-    void *libraryHandle = DLOPEN(soname, RTLD_LAZY);
-
-    if (!libraryHandle) {
-        cerr << argv[0] << ": Failed to open plugin library " 
-                  << soname << ": " << DLERROR() << endl;
-        return 1;
-    }
-
-    cerr << argv[0] << ": Opened plugin library " << soname << endl;
-
-    VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
-        DLSYM(libraryHandle, "vampGetPluginDescriptor");
-    
-    if (!fn) {
-        cerr << argv[0] << ": No Vamp descriptor function in library "
-                  << soname << endl;
-        DLCLOSE(libraryHandle);
-        return 1;
-    }
-
-    cerr << argv[0] << ": Found plugin descriptor function" << endl;
-
-    int index = 0;
-    int plugnumber = -1;
-    const VampPluginDescriptor *descriptor = 0;
-
-    while ((descriptor = fn(VAMP_API_VERSION, index))) {
-
-        Vamp::PluginHostAdapter plugin(descriptor, 48000);
-        cerr << argv[0] << ": Plugin " << (index+1)
-                  << " is \"" << plugin.getIdentifier() << "\"" << endl;
-
-        if (plugin.getIdentifier() == plugid) plugnumber = index;
-        
-        ++index;
-    }
-
-    cerr << argv[0] << ": Done\n" << endl;
-
-    if (wavname == "") {
-        DLCLOSE(libraryHandle);
-        return 0;
-    }
-
-    if (plugnumber < 0) {
-        if (plugid != "") {
-            cerr << "ERROR: No such plugin as " << plugid << " in library"
-                 << endl;
-            DLCLOSE(libraryHandle);
-            return 0;
-        } else {
-            plugnumber = 0;
+        sep = plugid.find(':');
+        if (sep != string::npos) {
+            output = plugid.substr(sep + 1);
+            plugid = plugid.substr(0, sep);
         }
     }
 
-    descriptor = fn(VAMP_API_VERSION, plugnumber);
-    if (!descriptor) {
-        DLCLOSE(libraryHandle);
-        return 0;
+    if (plugid == "") {
+        usage(name);
     }
+
+    if (argc == 4) outputNo = atoi(argv[3]);
+
+    if (output != "" && outputNo != -1) {
+        usage(name);
+    }
+
+    return runPlugin(name, soname, plugid, output, outputNo, wavname);
+}
+
+
+int runPlugin(string myname, string soname, string id,
+              string output, int outputNo, string wavname)
+{
+    PluginLoader *loader = PluginLoader::getInstance();
+
+    PluginLoader::PluginKey key = loader->composePluginKey(soname, id);
     
     SNDFILE *sndfile;
     SF_INFO sfinfo;
@@ -204,49 +176,28 @@
 
     sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo);
     if (!sndfile) {
-	cerr << "ERROR: Failed to open input file \"" << wavname << "\": "
-	     << sf_strerror(sndfile) << endl;
-        DLCLOSE(libraryHandle);
+	cerr << myname << ": ERROR: Failed to open input file \""
+             << wavname << "\": " << sf_strerror(sndfile) << endl;
 	return 1;
     }
 
+    Vamp::Plugin *basePlugin = loader->loadPlugin(key, sfinfo.samplerate);
+    if (!basePlugin) {
+        cerr << myname << ": ERROR: Failed to load plugin \"" << id
+             << "\" from library \"" << soname << "\"" << endl;
+        sf_close(sndfile);
+        return 1;
+    }
+
     Vamp::Plugin *plugin =
-        new Vamp::PluginInputDomainAdapter
-        (new Vamp::PluginHostAdapter(descriptor, sfinfo.samplerate));
+        new Vamp::HostExt::PluginChannelAdapter
+        (new Vamp::HostExt::PluginInputDomainAdapter(basePlugin));
 
-    cerr << "Running " << plugin->getIdentifier() << "..." << endl;
+    cerr << "Running plugin: \"" << plugin->getIdentifier() << "\"..." << endl;
 
     int blockSize = plugin->getPreferredBlockSize();
     int stepSize = plugin->getPreferredStepSize();
 
-    cerr << "Preferred block size = " << blockSize << ", step size = "
-         << stepSize << endl;
-
-    if (blockSize == 0) blockSize = 1024;
-
-    bool rightBlockSize = true;
-
-    if (plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
-
-        int p = 1, b = blockSize;
-        while (b) {
-            p <<= 1;
-            b >>= 1;
-        }
-        if (p != blockSize * 2) {
-            cerr << "WARNING: Plugin requested non-power-of-two block size of "
-                 << blockSize << ",\nwhich is not supported by this host.  ";
-            blockSize = p;
-            cerr << "Rounding up to " << blockSize << "." << endl;
-            rightBlockSize = false;
-        }
-        if (stepSize == 0) stepSize = blockSize / 2;
-
-    } else {
-
-        if (stepSize == 0) stepSize = blockSize;
-    }
-
     int channels = sfinfo.channels;
 
     float *filebuf = new float[blockSize * channels];
@@ -259,48 +210,47 @@
     int minch = plugin->getMinChannelCount();
     int maxch = plugin->getMaxChannelCount();
     cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl;
+    cerr << "Sound file has " << channels << " (will mix/augment if necessary)" << endl;
 
     Vamp::Plugin::OutputList outputs = plugin->getOutputDescriptors();
     Vamp::Plugin::OutputDescriptor od;
 
     int returnValue = 1;
 
-    int output = 0;
-    if (argc == 4) output = atoi(argv[3]);
-
-    bool mix = false;
-
-    if (minch > channels || maxch < channels) {
-        if (minch == 1) {
-            cerr << "WARNING: Sound file has " << channels << " channels, mixing down to 1" << endl;
-            mix = true;
-            channels = 1;
-        } else {
-            cerr << "ERROR: Sound file has " << channels << " channels, out of range for plugin" << endl;
-            goto done;
-        }
-    }
-
     if (outputs.empty()) {
-	cerr << "Plugin has no outputs!" << endl;
+	cerr << "ERROR: Plugin has no outputs!" << endl;
         goto done;
     }
 
-    if (int(outputs.size()) <= output) {
-	cerr << "Output " << output << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
-        goto done;
-    }        
+    if (outputNo < 0) {
 
-    od = outputs[output];
-    cerr << "Output is " << od.identifier << endl;
+        for (size_t oi = 0; oi < outputs.size(); ++oi) {
+            if (outputs[oi].identifier == output) {
+                outputNo = oi;
+                break;
+            }
+        }
+
+        if (outputNo < 0) {
+            cerr << "ERROR: Non-existent output \"" << output << "\" requested" << endl;
+            goto done;
+        }
+
+    } else {
+
+        if (int(outputs.size()) <= outputNo) {
+            cerr << "ERROR: Output " << outputNo << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
+            goto done;
+        }        
+    }
+
+    od = outputs[outputNo];
+    cerr << "Output is: \"" << od.identifier << "\"" << endl;
 
     if (!plugin->initialise(channels, stepSize, blockSize)) {
         cerr << "ERROR: Plugin initialise (channels = " << channels
              << ", stepSize = " << stepSize << ", blockSize = "
              << blockSize << ") failed." << endl;
-        if (!rightBlockSize) {
-            cerr << "(Probably because I couldn't provide the plugin's preferred block size.)" << endl;
-        }
         goto done;
     }
 
@@ -319,64 +269,66 @@
         }
 
         for (int c = 0; c < channels; ++c) {
-            for (int j = 0; j < blockSize; ++j) {
+            int j = 0;
+            while (j < count) {
+                plugbuf[c][j] = filebuf[j * sfinfo.channels + c];
+                ++j;
+            }
+            while (j < blockSize) {
                 plugbuf[c][j] = 0.0f;
-            }
-        }
-
-        for (int j = 0; j < blockSize && j < count; ++j) {
-            int tc = 0;
-            for (int c = 0; c < sfinfo.channels; ++c) {
-                tc = c;
-                if (mix) tc = 0;
-                plugbuf[tc][j] += filebuf[j * sfinfo.channels + c];
-            }
-            if (mix) {
-                plugbuf[0][j] /= sfinfo.channels;
+                ++j;
             }
         }
 
         printFeatures
-            (i, sfinfo.samplerate, output, plugin->process
+            (i, sfinfo.samplerate, outputNo, plugin->process
              (plugbuf, Vamp::RealTime::frame2RealTime(i, sfinfo.samplerate)));
     }
 
-    printFeatures(sfinfo.frames, sfinfo.samplerate, output,
+    printFeatures(sfinfo.frames, sfinfo.samplerate, outputNo,
                   plugin->getRemainingFeatures());
 
     returnValue = 0;
 
 done:
     delete plugin;
-
-    DLCLOSE(libraryHandle);
     sf_close(sndfile);
     return returnValue;
 }
 
 void
-printPluginPath()
+printPluginPath(bool verbose)
 {
+    if (verbose) {
+        cout << "\nVamp plugin search path: ";
+    }
+
     vector<string> path = Vamp::PluginHostAdapter::getPluginPath();
     for (size_t i = 0; i < path.size(); ++i) {
-        cerr << path[i] << endl;
+        if (verbose) {
+            cout << "[" << path[i] << "]";
+        } else {
+            cout << path[i] << endl;
+        }
     }
+
+    if (verbose) cout << endl;
 }
 
 void
 enumeratePlugins()
 {
-    Vamp::PluginLoader loader;
+    PluginLoader *loader = PluginLoader::getInstance();
 
-    cerr << endl << "Vamp plugin libraries found in search path:" << endl;
+    cout << "\nVamp plugin libraries found in search path:" << endl;
 
-    std::vector<Vamp::PluginLoader::PluginKey> plugins = loader.listPlugins();
-    typedef std::multimap<std::string, Vamp::PluginLoader::PluginKey>
+    std::vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
+    typedef std::multimap<std::string, PluginLoader::PluginKey>
         LibraryMap;
     LibraryMap libraryMap;
 
     for (size_t i = 0; i < plugins.size(); ++i) {
-        std::string path = loader.getLibraryPathForPlugin(plugins[i]);
+        std::string path = loader->getLibraryPathForPlugin(plugins[i]);
         libraryMap.insert(LibraryMap::value_type(path, plugins[i]));
     }
 
@@ -387,39 +339,38 @@
          i != libraryMap.end(); ++i) {
         
         std::string path = i->first;
-        Vamp::PluginLoader::PluginKey key = i->second;
+        PluginLoader::PluginKey key = i->second;
 
         if (path != prevPath) {
             prevPath = path;
             index = 0;
-            cerr << "\n  " << path << ":" << endl;
+            cout << "\n  " << path << ":" << endl;
         }
 
-        Vamp::Plugin *plugin = loader.load(key, 48000);
+        Vamp::Plugin *plugin = loader->loadPlugin(key, 48000);
         if (plugin) {
 
             char c = char('A' + index);
             if (c > 'Z') c = char('a' + (index - 26));
 
-            cerr << "    [" << c << "] [v"
+            cout << "    [" << c << "] [v"
                  << plugin->getVampApiVersion() << "] "
                  << plugin->getName() << ", \""
                  << plugin->getIdentifier() << "\"" << " ["
                  << plugin->getMaker() << "]" << endl;
 
-//            cerr << "looking up category for " << key << " ..." << endl;
-            Vamp::PluginLoader::PluginCategoryHierarchy category =
-                loader.getPluginCategory(key);
+            PluginLoader::PluginCategoryHierarchy category =
+                loader->getPluginCategory(key);
             if (!category.empty()) {
-                cerr << "       ";
+                cout << "       ";
                 for (size_t ci = 0; ci < category.size(); ++ci) {
-                    cerr << " > " << category[ci];
+                    cout << " > " << category[ci];
                 }
-                cerr << endl;
+                cout << endl;
             }
 
             if (plugin->getDescription() != "") {
-                cerr << "        - " << plugin->getDescription() << endl;
+                cout << "        - " << plugin->getDescription() << endl;
             }
 
             Vamp::Plugin::OutputList outputs =
@@ -427,21 +378,23 @@
 
             if (outputs.size() > 1) {
                 for (size_t j = 0; j < outputs.size(); ++j) {
-                    cerr << "         (" << j << ") "
+                    cout << "         (" << j << ") "
                          << outputs[j].name << ", \""
                          << outputs[j].identifier << "\"" << endl;
                     if (outputs[j].description != "") {
-                        cerr << "             - " 
+                        cout << "             - " 
                              << outputs[j].description << endl;
                     }
                 }
             }
 
             ++index;
+
+            delete plugin;
         }
     }
 
-    cerr << endl;
+    cout << endl;
 }
 
 void