changeset 16:61887dda7fe0

* Make the host capable of doing something useful!
author cannam
date Fri, 07 Apr 2006 15:32:10 +0000
parents 6c5466fbea90
children a67eeb9d6341
files Makefile README host/simplehost.cpp vamp-sdk/PluginAdapter.cpp
diffstat 4 files changed, 354 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Thu Apr 06 17:23:41 2006 +0000
+++ b/Makefile	Fri Apr 07 15:32:10 2006 +0000
@@ -17,7 +17,7 @@
 
 # Libraries required for the host at link time
 #
-HOST_LIBS	= -Lvamp-sdk -lvamp-sdk -ldl
+HOST_LIBS	= -Lvamp-sdk -lvamp-sdk -lsndfile -ldl
 
 # Libraries required for the plugin.  Note that we can (and actively
 # want to) statically link with libstdc++, because our plugin exposes
--- a/README	Thu Apr 06 17:23:41 2006 +0000
+++ b/README	Fri Apr 07 15:32:10 2006 +0000
@@ -72,8 +72,9 @@
 
  * host
 
-The simplest possible Vamp host -- this doesn't even process anything
-yet, just loads the plugins and prints out their names.
+A simple command-line Vamp host, capable of loading a plugin and using
+it to process a complete audio file, with its default parameters.
+Requires libsndfile.
 
 
 Building the SDK
--- a/host/simplehost.cpp	Thu Apr 06 17:23:41 2006 +0000
+++ b/host/simplehost.cpp	Fri Apr 07 15:32:10 2006 +0000
@@ -7,6 +7,7 @@
 
     Centre for Digital Music, Queen Mary, University of London.
     Copyright 2006 Chris Cannam.
+    FFT code from Don Cross's public domain FFT implementation.
   
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation
@@ -34,68 +35,390 @@
     authorization.
 */
 
-#include <iostream>
-
+#include "PluginHostAdapter.h"
 #include "vamp.h"
 
-#include "PluginHostAdapter.h"
+#include <iostream>
+#include <sndfile.h>
 
 #include "system.h"
 
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::string;
+
+void printFeatures(int, int, int, Vamp::Plugin::FeatureSet);
+void transformInput(float *, size_t);
+void fft(unsigned int, bool, double *, double *, double *, double *);
+
 /*
-    A very simple Vamp plugin host.  So far all this does is load the
-    plugin library given on the command line and dump out the names of
-    the plugins found in it.
+    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.
 */
 
 int main(int argc, char **argv)
 {
-    if (argc != 2) {
-        std::cerr << "Usage: " << argv[0] << " pluginlibrary.so" << std::endl;
+    if (argc < 2 || argc > 4) {
+        cerr << "Usage: " << argv[0] << " pluginlibrary.so[:plugin] [file.wav] [outputno]" << endl;
         return 2;
     }
 
-    std::cerr << std::endl << argv[0] << ": Running..." << std::endl;
+    cerr << endl << argv[0] << ": Running..." << endl;
 
-    std::string soname = argv[1];
+    string soname = argv[1];
+    string plugname = "";
+    string wavname;
+    if (argc >= 3) wavname = argv[2];
+
+    if (soname.find(":") >= 0) {
+        plugname = soname.substr(soname.find(":") + 1);
+        soname = soname.substr(0, soname.find(":"));
+    }
 
     void *libraryHandle = DLOPEN(soname, RTLD_LAZY);
 
     if (!libraryHandle) {
-        std::cerr << argv[0] << ": Failed to open plugin library " 
-                  << soname << ": " << DLERROR() << std::endl;
+        cerr << argv[0] << ": Failed to open plugin library " 
+                  << soname << ": " << DLERROR() << endl;
         return 1;
     }
 
-    std::cout << argv[0] << ": Opened plugin library " << soname << std::endl;
+    cerr << argv[0] << ": Opened plugin library " << soname << endl;
 
     VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
         DLSYM(libraryHandle, "vampGetPluginDescriptor");
     
     if (!fn) {
-        std::cerr << argv[0] << ": No Vamp descriptor function in library "
-                  << soname << std::endl;
+        cerr << argv[0] << ": No Vamp descriptor function in library "
+                  << soname << endl;
         DLCLOSE(libraryHandle);
         return 1;
     }
 
-    std::cout << argv[0] << ": Found plugin descriptor function" << std::endl;
+    cerr << argv[0] << ": Found plugin descriptor function" << endl;
 
     int index = 0;
+    int plugnumber = -1;
     const VampPluginDescriptor *descriptor = 0;
 
     while ((descriptor = fn(index))) {
 
-        Vamp::PluginHostAdapter adapter(descriptor, 48000);
-        std::cout << argv[0] << ": Plugin " << (index+1)
-                  << " is \"" << adapter.getName() << "\"" << std::endl;
+        Vamp::PluginHostAdapter plugin(descriptor, 48000);
+        cerr << argv[0] << ": Plugin " << (index+1)
+                  << " is \"" << plugin.getName() << "\"" << endl;
+
+        if (plugin.getName() == plugname) plugnumber = index;
         
         ++index;
     }
 
-    std::cout << argv[0] << ": Done\n" << std::endl;
+    cerr << argv[0] << ": Done\n" << endl;
+
+    if (wavname == "") {
+        DLCLOSE(libraryHandle);
+        return 0;
+    }
+
+    if (plugnumber < 0) {
+        if (plugname != "") {
+            cerr << "ERROR: No such plugin as " << plugname << " in library"
+                 << endl;
+            DLCLOSE(libraryHandle);
+            return 0;
+        } else {
+            plugnumber = 0;
+        }
+    }
+
+    descriptor = fn(plugnumber);
+    if (!descriptor) {
+        DLCLOSE(libraryHandle);
+        return 0;
+    }
+    
+    SNDFILE *sndfile;
+    SF_INFO sfinfo;
+    memset(&sfinfo, 0, sizeof(SF_INFO));
+
+    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);
+	return 1;
+    }
+
+    Vamp::PluginHostAdapter *plugin =
+        new Vamp::PluginHostAdapter(descriptor, sfinfo.samplerate);
+
+    cerr << "Running " << plugin->getName() << "..." << endl;
+
+    int blockSize = plugin->getPreferredBlockSize();
+    int stepSize = plugin->getPreferredStepSize();
+
+    cerr << "Preferred block size = " << blockSize << ", step size = "
+              << stepSize << endl;
+
+    if (blockSize == 0) blockSize = 1024;
+    if (stepSize == 0) stepSize = blockSize;
+
+    int channels = sfinfo.channels;
+
+    float *filebuf = new float[blockSize * channels];
+    float **plugbuf = new float*[channels];
+    for (int c = 0; c < channels; ++c) plugbuf[c] = new float[blockSize];
+
+    cerr << "Using block size = " << blockSize << ", step size = "
+              << stepSize << endl;
+
+    int minch = plugin->getMinChannelCount();
+    int maxch = plugin->getMaxChannelCount();
+    cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl;
+
+    Vamp::Plugin::OutputList outputs = plugin->getOutputDescriptors();
+    Vamp::Plugin::OutputDescriptor od;
+
+    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;
+        goto done;
+    }
+
+    if (int(outputs.size()) <= output) {
+	cerr << "Output " << output << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
+        goto done;
+    }        
+
+    od = outputs[output];
+    cerr << "Output is " << od.name << endl;
+
+    plugin->initialise(channels, stepSize, blockSize);
+
+    for (size_t i = 0; i < sfinfo.frames; i += stepSize) {
+
+        int count;
+
+        if (sf_seek(sndfile, i, SEEK_SET) < 0) {
+            cerr << "ERROR: sf_seek failed: " << sf_strerror(sndfile) << endl;
+            break;
+        }
+        
+        if ((count = sf_readf_float(sndfile, filebuf, blockSize)) < 0) {
+            cerr << "ERROR: sf_readf_float failed: " << sf_strerror(sndfile) << endl;
+            break;
+        }
+
+        for (int c = 0; c < channels; ++c) {
+            for (int j = 0; j < blockSize; ++j) {
+                plugbuf[c][j] = 0.0f;
+            }
+        }
+
+        for (int c = 0; c < sfinfo.channels; ++c) {
+            int tc = c;
+            if (mix) tc = 0;
+            for (int j = 0; j < blockSize && j < count; ++j) {
+                plugbuf[tc][j] += filebuf[j * channels + c];
+            }
+
+            if (plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
+                transformInput(plugbuf[tc], blockSize);
+            }
+        }
+
+        printFeatures
+            (i, sfinfo.samplerate, output, plugin->process
+             (plugbuf, Vamp::RealTime::frame2RealTime(i, sfinfo.samplerate)));
+    }
+
+    printFeatures(sfinfo.frames, sfinfo.samplerate, output,
+                  plugin->getRemainingFeatures());
+
+done:
+    delete plugin;
 
     DLCLOSE(libraryHandle);
+    sf_close(sndfile);
     return 0;
 }
 
+void
+printFeatures(int frame, int sr, int output, Vamp::Plugin::FeatureSet features)
+{
+    for (unsigned int i = 0; i < features[output].size(); ++i) {
+        Vamp::RealTime rt = Vamp::RealTime::frame2RealTime(frame, sr);
+        if (features[output][i].hasTimestamp) {
+            rt = features[output][i].timestamp;
+        }
+        cout << rt.toString() << ":";
+        for (unsigned int j = 0; j < features[output][i].values.size(); ++j) {
+            cout << " " << features[output][i].values[j];
+        }
+        cout << endl;
+    }
+}
+
+void
+transformInput(float *buffer, size_t size)
+{
+    double *inbuf = new double[size * 2];
+    double *outbuf = new double[size * 2];
+
+    // Copy across with Hanning window
+    for (size_t i = 0; i < size; ++i) {
+        inbuf[i] = double(buffer[i]) * (0.50 - 0.50 * cos(2 * M_PI * i / size));
+        inbuf[i + size] = 0.0;
+    }
+    
+    for (size_t i = 0; i < size/2; ++i) {
+        double temp = inbuf[i];
+        inbuf[i] = inbuf[i + size/2];
+        inbuf[i + size/2] = temp;
+    }
+
+    fft(size, false, inbuf, inbuf + size, outbuf, outbuf + size);
+
+    for (size_t i = 0; i < size/2; ++i) {
+        buffer[i * 2] = outbuf[i];
+        buffer[i * 2 + 1] = outbuf[i + size];
+    }
+    
+    delete inbuf;
+    delete outbuf;
+}
+
+void
+fft(unsigned int n, bool inverse, double *ri, double *ii, double *ro, double *io)
+{
+    if (!ri || !ro || !io) return;
+
+    unsigned int bits;
+    unsigned int i, j, k, m;
+    unsigned int blockSize, blockEnd;
+
+    double tr, ti;
+
+    if (n < 2) return;
+    if (n & (n-1)) return;
+
+    double angle = 2.0 * M_PI;
+    if (inverse) angle = -angle;
+
+    for (i = 0; ; ++i) {
+	if (n & (1 << i)) {
+	    bits = i;
+	    break;
+	}
+    }
+
+    static unsigned int tableSize = 0;
+    static int *table = 0;
+
+    if (tableSize != n) {
+
+	delete[] table;
+
+	table = new int[n];
+
+	for (i = 0; i < n; ++i) {
+	
+	    m = i;
+
+	    for (j = k = 0; j < bits; ++j) {
+		k = (k << 1) | (m & 1);
+		m >>= 1;
+	    }
+
+	    table[i] = k;
+	}
+
+	tableSize = n;
+    }
+
+    if (ii) {
+	for (i = 0; i < n; ++i) {
+	    ro[table[i]] = ri[i];
+	    io[table[i]] = ii[i];
+	}
+    } else {
+	for (i = 0; i < n; ++i) {
+	    ro[table[i]] = ri[i];
+	    io[table[i]] = 0.0;
+	}
+    }
+
+    blockEnd = 1;
+
+    for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
+
+	double delta = angle / (double)blockSize;
+	double sm2 = -sin(-2 * delta);
+	double sm1 = -sin(-delta);
+	double cm2 = cos(-2 * delta);
+	double cm1 = cos(-delta);
+	double w = 2 * cm1;
+	double ar[3], ai[3];
+
+	for (i = 0; i < n; i += blockSize) {
+
+	    ar[2] = cm2;
+	    ar[1] = cm1;
+
+	    ai[2] = sm2;
+	    ai[1] = sm1;
+
+	    for (j = i, m = 0; m < blockEnd; j++, m++) {
+
+		ar[0] = w * ar[1] - ar[2];
+		ar[2] = ar[1];
+		ar[1] = ar[0];
+
+		ai[0] = w * ai[1] - ai[2];
+		ai[2] = ai[1];
+		ai[1] = ai[0];
+
+		k = j + blockEnd;
+		tr = ar[0] * ro[k] - ai[0] * io[k];
+		ti = ar[0] * io[k] + ai[0] * ro[k];
+
+		ro[k] = ro[j] - tr;
+		io[k] = io[j] - ti;
+
+		ro[j] += tr;
+		io[j] += ti;
+	    }
+	}
+
+	blockEnd = blockSize;
+    }
+
+    if (inverse) {
+
+	double denom = (double)n;
+
+	for (i = 0; i < n; i++) {
+	    ro[i] /= denom;
+	    io[i] /= denom;
+	}
+    }
+}
+
+        
--- a/vamp-sdk/PluginAdapter.cpp	Thu Apr 06 17:23:41 2006 +0000
+++ b/vamp-sdk/PluginAdapter.cpp	Fri Apr 07 15:32:10 2006 +0000
@@ -578,7 +578,7 @@
     int i = m_fsizes[plugin].size();
     if (i >= n) return;
 
-    std::cerr << "resizing from " << i << std::endl;
+//    std::cerr << "resizing from " << i << std::endl;
 
     m_fs[plugin] = (VampFeatureList *)realloc
         (m_fs[plugin], n * sizeof(VampFeatureList));
@@ -595,13 +595,13 @@
 void
 PluginAdapterBase::resizeFL(Plugin *plugin, int n, size_t sz)
 {
-    std::cerr << "PluginAdapterBase::resizeFL(" << plugin << ", " << n << ", "
-              << sz << ")" << std::endl;
+//    std::cerr << "PluginAdapterBase::resizeFL(" << plugin << ", " << n << ", "
+//              << sz << ")" << std::endl;
 
     size_t i = m_fsizes[plugin][n];
     if (i >= sz) return;
 
-    std::cerr << "resizing from " << i << std::endl;
+//    std::cerr << "resizing from " << i << std::endl;
 
     m_fs[plugin][n].features = (VampFeature *)realloc
         (m_fs[plugin][n].features, sz * sizeof(VampFeature));
@@ -618,14 +618,13 @@
 void
 PluginAdapterBase::resizeFV(Plugin *plugin, int n, int j, size_t sz)
 {
-
-    std::cerr << "PluginAdapterBase::resizeFV(" << plugin << ", " << n << ", "
-              << j << ", " << sz << ")" << std::endl;
+//    std::cerr << "PluginAdapterBase::resizeFV(" << plugin << ", " << n << ", "
+//              << j << ", " << sz << ")" << std::endl;
 
     size_t i = m_fvsizes[plugin][n][j];
     if (i >= sz) return;
 
-    std::cerr << "resizing from " << i << std::endl;
+//    std::cerr << "resizing from " << i << std::endl;
 
     m_fs[plugin][n].features[j].values = (float *)realloc
         (m_fs[plugin][n].features[j].values, sz * sizeof(float));