changeset 61:97c5ac99d725 host-factory-stuff

* install hostext headers to vamp-sdk/hostext/ rather than vamp-sdk/ * adjust timestamps in input-domain adapter so as to centre them on block as required by Plugin documentation * better handling for frequency-domain plugins that want non-power-of-two blocksizes (can't handle them, but at least try offering them a power-of-two alternative) * couple of Plugin doc additions * make PluginLoader capable of returning ready-wrapped plugins
author cannam
date Fri, 01 Jun 2007 13:53:42 +0000
parents 087c16cca0d6
children fe5486ee1c70
files Makefile host/vamp-simple-host.cpp vamp-sdk/Plugin.h vamp-sdk/PluginBase.h vamp-sdk/hostext/PluginChannelAdapter.cpp vamp-sdk/hostext/PluginInputDomainAdapter.cpp vamp-sdk/hostext/PluginInputDomainAdapter.h vamp-sdk/hostext/PluginLoader.cpp vamp-sdk/hostext/PluginLoader.h vamp-sdk/hostext/PluginWrapper.cpp
diffstat 10 files changed, 207 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Fri May 25 13:26:59 2007 +0000
+++ b/Makefile	Fri Jun 01 13:53:42 2007 +0000
@@ -60,6 +60,7 @@
 INSTALL_PREFIX	 	  := /usr/local
 INSTALL_API_HEADERS	  := $(INSTALL_PREFIX)/include/vamp
 INSTALL_SDK_HEADERS	  := $(INSTALL_PREFIX)/include/vamp-sdk
+INSTALL_HOSTEXT_HEADERS	  := $(INSTALL_PREFIX)/include/vamp-sdk/hostext
 INSTALL_SDK_LIBS	  := $(INSTALL_PREFIX)/lib
 
 INSTALL_SDK_LIBNAME	  := libvamp-sdk.so.1.0.0
@@ -92,11 +93,13 @@
 		$(SDKDIR)/Plugin.h \
 		$(SDKDIR)/PluginBase.h \
 		$(SDKDIR)/PluginHostAdapter.h \
+		$(SDKDIR)/RealTime.h
+
+HOSTEXT_HEADERS = \
 		$(HOSTEXTDIR)/PluginChannelAdapter.h \
 		$(HOSTEXTDIR)/PluginInputDomainAdapter.h \
 		$(HOSTEXTDIR)/PluginLoader.h \
-		$(HOSTEXTDIR)/PluginWrapper.h \
-		$(SDKDIR)/RealTime.h
+		$(HOSTEXTDIR)/PluginWrapper.h
 
 SDK_OBJECTS	= \
 		$(SDKDIR)/PluginAdapter.o \
@@ -161,13 +164,13 @@
 $(SDK_STATIC):	$(SDK_OBJECTS) $(API_HEADERS) $(SDK_HEADERS)
 		$(AR) r $@ $(SDK_OBJECTS)
 
-$(HOSTSDK_STATIC):	$(HOSTSDK_OBJECTS) $(API_HEADERS) $(HOSTSDK_HEADERS)
+$(HOSTSDK_STATIC):	$(HOSTSDK_OBJECTS) $(API_HEADERS) $(HOSTSDK_HEADERS) $(HOSTEXT_HEADERS)
 		$(AR) r $@ $(HOSTSDK_OBJECTS)
 
 $(SDK_DYNAMIC):	$(SDK_OBJECTS) $(API_HEADERS) $(SDK_HEADERS)
 		$(CXX) $(LDFLAGS) $(PLUGIN_LDFLAGS) -o $@ $(SDK_OBJECTS)
 
-$(HOSTSDK_DYNAMIC):	$(HOSTSDK_OBJECTS) $(API_HEADERS) $(HOSTSDK_HEADERS)
+$(HOSTSDK_DYNAMIC):	$(HOSTSDK_OBJECTS) $(API_HEADERS) $(HOSTSDK_HEADERS) $(HOSTEXT_HEADERS)
 		$(CXX) $(LDFLAGS) $(PLUGIN_LDFLAGS) -o $@ $(HOSTSDK_OBJECTS)
 
 $(PLUGIN_TARGET):	$(PLUGIN_OBJECTS) $(SDK_STATIC) $(PLUGIN_HEADERS)
@@ -188,11 +191,13 @@
 install:	$(SDK_STATIC) $(SDK_DYNAMIC) $(HOSTSDK_STATIC) $(HOSTSDK_DYNAMIC) $(PLUGIN_TARGET) $(HOST_TARGET)
 		mkdir -p $(INSTALL_API_HEADERS)
 		mkdir -p $(INSTALL_SDK_HEADERS)
+		mkdir -p $(INSTALL_HOSTEXT_HEADERS)
 		mkdir -p $(INSTALL_SDK_LIBS)
 		mkdir -p $(INSTALL_PKGCONFIG)
 		cp $(API_HEADERS) $(INSTALL_API_HEADERS)
 		cp $(SDK_HEADERS) $(INSTALL_SDK_HEADERS)
 		cp $(HOSTSDK_HEADERS) $(INSTALL_SDK_HEADERS)
+		cp $(HOSTEXT_HEADERS) $(INSTALL_HOSTEXT_HEADERS)
 		cp $(SDK_STATIC) $(INSTALL_SDK_LIBS)
 		cp $(HOSTSDK_STATIC) $(INSTALL_SDK_LIBS)
 		cp $(SDK_DYNAMIC) $(INSTALL_SDK_LIBS)/$(INSTALL_SDK_LIBNAME)
--- a/host/vamp-simple-host.cpp	Fri May 25 13:26:59 2007 +0000
+++ b/host/vamp-simple-host.cpp	Fri Jun 01 13:53:42 2007 +0000
@@ -181,18 +181,15 @@
 	return 1;
     }
 
-    Vamp::Plugin *basePlugin = loader->loadPlugin(key, sfinfo.samplerate);
-    if (!basePlugin) {
+    Vamp::Plugin *plugin = loader->loadPlugin
+        (key, sfinfo.samplerate, PluginLoader::ADAPT_ALL);
+    if (!plugin) {
         cerr << myname << ": ERROR: Failed to load plugin \"" << id
              << "\" from library \"" << soname << "\"" << endl;
         sf_close(sndfile);
         return 1;
     }
 
-    Vamp::Plugin *plugin =
-        new Vamp::HostExt::PluginChannelAdapter
-        (new Vamp::HostExt::PluginInputDomainAdapter(basePlugin));
-
     cerr << "Running plugin: \"" << plugin->getIdentifier() << "\"..." << endl;
 
     int blockSize = plugin->getPreferredBlockSize();
--- a/vamp-sdk/Plugin.h	Fri May 25 13:26:59 2007 +0000
+++ b/vamp-sdk/Plugin.h	Fri Jun 01 13:53:42 2007 +0000
@@ -358,6 +358,7 @@
      * real and imaginary component floats corresponding to bins
      * 0..(blockSize/2) of the FFT output, where bin 0 contains the DC
      * output and bin blockSize/2 corresponds to the Nyquist output.
+     * There will therefore be blockSize+2 floats per channel in total.
      * The timestamp will be the real time in seconds of the centre of
      * the FFT input window (i.e. the very first block passed to
      * process might contain the FFT of half a block of zero samples
--- a/vamp-sdk/PluginBase.h	Fri May 25 13:26:59 2007 +0000
+++ b/vamp-sdk/PluginBase.h	Fri Jun 01 13:53:42 2007 +0000
@@ -217,7 +217,13 @@
     typedef std::vector<std::string> ProgramList;
 
     /**
-     * Get the program settings available in this plugin.
+     * Get the program settings available in this plugin.  A program
+     * is a named shorthand for a set of parameter values; changing
+     * the program may cause the plugin to alter the values of its
+     * published parameters (and/or non-public internal processing
+     * parameters).  The host should re-read the plugin's parameter
+     * values after setting a new program.
+     *
      * The programs must have unique names.
      */
     virtual ProgramList getPrograms() const { return ProgramList(); }
--- a/vamp-sdk/hostext/PluginChannelAdapter.cpp	Fri May 25 13:26:59 2007 +0000
+++ b/vamp-sdk/hostext/PluginChannelAdapter.cpp	Fri Jun 01 13:53:42 2007 +0000
@@ -80,8 +80,10 @@
 
     m_inputChannels = channels;
 
-    if (channels < minch) {
+    if (m_inputChannels < minch) {
+
         m_forwardPtrs = new const float *[minch];
+
         if (m_inputChannels > 1) {
             // We need a set of zero-valued buffers to add to the
             // forwarded pointers
@@ -93,30 +95,45 @@
                 }
             }
         }
+
         m_pluginChannels = minch;
-        return m_plugin->initialise(minch, stepSize, blockSize);
-    }
 
-    if (channels > maxch) {
+        std::cerr << "PluginChannelAdapter::initialise: expanding " << m_inputChannels << " to " << m_pluginChannels << " for plugin" << std::endl;
+
+    } else if (m_inputChannels > maxch) {
+
         // We only need m_buffer if we are mixing down to a single
         // channel -- otherwise we can just forward the same float* as
         // passed in to process(), expecting the excess to be ignored
+
         if (maxch == 1) {
             m_buffer = new float *[1];
             m_buffer[0] = new float[blockSize];
+
+            std::cerr << "PluginChannelAdapter::initialise: mixing " << m_inputChannels << " to mono for plugin" << std::endl;
+
+        } else {
+            
+            std::cerr << "PluginChannelAdapter::initialise: reducing " << m_inputChannels << " to " << m_pluginChannels << " for plugin" << std::endl;
         }
+
         m_pluginChannels = maxch;
-        return m_plugin->initialise(maxch, stepSize, blockSize);
+
+    } else {
+ 
+        std::cerr << "PluginChannelAdapter::initialise: accepting given number of channels (" << m_inputChannels << ")" << std::endl;
+        m_pluginChannels = m_inputChannels;
     }
 
-    m_pluginChannels = channels;
-    return m_plugin->initialise(channels, stepSize, blockSize);
+    return m_plugin->initialise(m_pluginChannels, stepSize, blockSize);
 }
 
 PluginChannelAdapter::FeatureSet
 PluginChannelAdapter::process(const float *const *inputBuffers,
                               RealTime timestamp)
 {
+    std::cerr << "PluginChannelAdapter::process: " << m_inputChannels << " -> " << m_pluginChannels << " channels" << std::endl;
+
     if (m_inputChannels < m_pluginChannels) {
 
         if (m_inputChannels == 1) {
@@ -133,9 +150,8 @@
         }
 
         return m_plugin->process(m_forwardPtrs, timestamp);
-    }
 
-    if (m_inputChannels > m_pluginChannels) {
+    } else if (m_inputChannels > m_pluginChannels) {
 
         if (m_pluginChannels == 1) {
             for (size_t j = 0; j < m_blockSize; ++j) {
@@ -153,9 +169,11 @@
         } else {
             return m_plugin->process(inputBuffers, timestamp);
         }
+
+    } else {
+
+        return m_plugin->process(inputBuffers, timestamp);
     }
-
-    return m_plugin->process(inputBuffers, timestamp);
 }
 
 }
--- a/vamp-sdk/hostext/PluginInputDomainAdapter.cpp	Fri May 25 13:26:59 2007 +0000
+++ b/vamp-sdk/hostext/PluginInputDomainAdapter.cpp	Fri Jun 01 13:53:42 2007 +0000
@@ -57,32 +57,44 @@
 bool
 PluginInputDomainAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
 {
-    //!!! complain and adapt-or-die if blocksize is not a power of 2
+    if (m_plugin->getInputDomain() == TimeDomain) {
 
-    if (m_plugin->getInputDomain() == FrequencyDomain) {
-        if (m_channels > 0) {
-            for (size_t c = 0; c < m_channels; ++c) {
-                delete[] m_freqbuf[c];
-            }
-            delete[] m_freqbuf;
-            delete[] m_ri;
-            delete[] m_ro;
-            delete[] m_io;
-        }
+        m_blockSize = blockSize;
+        m_channels = channels;
+
+        return m_plugin->initialise(channels, stepSize, blockSize);
     }
 
+    if (blockSize < 2) {
+        std::cerr << "ERROR: Vamp::HostExt::PluginInputDomainAdapter::initialise: blocksize < 2 not supported" << std::endl;
+        return false;
+    }                
+        
+    if (blockSize & (blockSize-1)) {
+        std::cerr << "ERROR: Vamp::HostExt::PluginInputDomainAdapter::initialise: non-power-of-two\nblocksize " << blockSize << " not supported" << std::endl;
+        return false;
+    }
+
+    if (m_channels > 0) {
+        for (size_t c = 0; c < m_channels; ++c) {
+            delete[] m_freqbuf[c];
+        }
+        delete[] m_freqbuf;
+        delete[] m_ri;
+        delete[] m_ro;
+        delete[] m_io;
+    }
+
+    m_blockSize = blockSize;
     m_channels = channels;
-    m_blockSize = blockSize;
 
-    if (m_plugin->getInputDomain() == FrequencyDomain) {
-        m_freqbuf = new float *[m_channels];
-        for (size_t c = 0; c < m_channels; ++c) {
-            m_freqbuf[c] = new float[m_blockSize + 2];
-        }
-        m_ri = new double[m_blockSize];
-        m_ro = new double[m_blockSize];
-        m_io = new double[m_blockSize];
+    m_freqbuf = new float *[m_channels];
+    for (size_t c = 0; c < m_channels; ++c) {
+        m_freqbuf[c] = new float[m_blockSize + 2];
     }
+    m_ri = new double[m_blockSize];
+    m_ro = new double[m_blockSize];
+    m_io = new double[m_blockSize];
 
     return m_plugin->initialise(channels, stepSize, blockSize);
 }
@@ -108,17 +120,56 @@
 size_t
 PluginInputDomainAdapter::getPreferredBlockSize() const
 {
-    //!!! complain and adapt-or-die if blocksize is not a power of 2
-
     size_t block = m_plugin->getPreferredBlockSize();
 
-    if (block == 0 && (m_plugin->getInputDomain() == FrequencyDomain)) {
-        block = 1024;
+    if (m_plugin->getInputDomain() == FrequencyDomain) {
+        if (block == 0) {
+            block = 1024;
+        } else {
+            block = makeBlockSizeAcceptable(block);
+        }
     }
 
     return block;
 }
 
+size_t
+PluginInputDomainAdapter::makeBlockSizeAcceptable(size_t blockSize) const
+{
+    if (blockSize < 2) {
+
+        std::cerr << "WARNING: Vamp::HostExt::PluginInputDomainAdapter::initialise: blocksize < 2 not" << std::endl
+                  << "supported, increasing from " << blockSize << " to 2" << std::endl;
+        blockSize = 2;
+        
+    } else if (blockSize & (blockSize-1)) {
+            
+        // not a power of two, can't handle that with our current fft
+        // implementation
+
+        size_t nearest = blockSize;
+        size_t power = 0;
+        while (nearest > 1) {
+            nearest >>= 1;
+            ++power;
+        }
+        nearest = 1;
+        while (power) {
+            nearest <<= 1;
+            --power;
+        }
+        
+        if (blockSize - nearest > (nearest*2) - blockSize) {
+            nearest = nearest*2;
+        }
+        
+        std::cerr << "WARNING: Vamp::HostExt::PluginInputDomainAdapter::initialise: non-power-of-two\nblocksize " << blockSize << " not supported, using blocksize " << nearest << " instead" << std::endl;
+        blockSize = nearest;
+    }
+
+    return blockSize;
+}
+
 Plugin::FeatureSet
 PluginInputDomainAdapter::process(const float *const *inputBuffers, RealTime timestamp)
 {
@@ -126,9 +177,54 @@
         return m_plugin->process(inputBuffers, timestamp);
     }
 
-    //!!! need to compensate for the fact that the first block is aligned
-    // with the zero frame but for frequency domain we want it to be
-    // centred on the zero frame 
+    // The timestamp supplied should be (according to the Vamp::Plugin
+    // spec) the time of the start of the time-domain input block.
+    // However, we want to pass to the plugin an FFT output calculated
+    // from the block of samples _centred_ on that timestamp.
+    // 
+    // We have two options:
+    // 
+    // 1. Buffer the input, calculating the fft of the values at the
+    // passed-in block minus blockSize/2 rather than starting at the
+    // passed-in block.  So each time we call process on the plugin,
+    // we are passing in the same timestamp as was passed to our own
+    // process plugin, but not (the frequency domain representation
+    // of) the same set of samples.  Advantages: avoids confusion in
+    // the host by ensuring the returned values have timestamps
+    // comparable with that passed in to this function (in fact this
+    // is pretty much essential for one-value-per-block outputs);
+    // consistent with hosts such as SV that deal with the
+    // frequency-domain transform themselves.  Disadvantages: means
+    // making the not necessarily correct assumption that the samples
+    // preceding the first official block are all zero (or some other
+    // known value).
+    //
+    // 2. Increase the passed-in timestamps by half the blocksize.  So
+    // when we call process, we are passing in the frequency domain
+    // representation of the same set of samples as passed to us, but
+    // with a different timestamp.  Advantages: simplicity; avoids
+    // iffy assumption mentioned above.  Disadvantages: inconsistency
+    // with SV in cases where stepSize != blockSize/2; potential
+    // confusion arising from returned timestamps being calculated
+    // from the adjusted input timestamps rather than the original
+    // ones (and inaccuracy where the returned timestamp is implied,
+    // as in one-value-per-block).
+    //
+    // Neither way is ideal, but I don't think either is strictly
+    // incorrect either.  I think this is just a case where the same
+    // plugin can legitimately produce differing results from the same
+    // input data, depending on how that data is packaged.
+    // 
+    // We'll go for option 2, adjusting the timestamps.  Note in
+    // particular that this means some results can differ from those
+    // produced by SV.
+
+    std::cerr << "PluginInputDomainAdapter: sampleRate " << m_inputSampleRate << ", blocksize " << m_blockSize << ", adjusting time from " << timestamp;
+
+    timestamp = timestamp + RealTime::frame2RealTime(m_blockSize/2,
+                                                     m_inputSampleRate);
+
+    std::cerr << " to " << timestamp << std::endl;
 
     for (size_t c = 0; c < m_channels; ++c) {
 
@@ -148,15 +244,12 @@
 
         fft(m_blockSize, false, m_ri, 0, m_ro, m_io);
 
-        for (size_t i = 0; i < m_blockSize/2; ++i) {
+        for (size_t i = 0; i <= m_blockSize/2; ++i) {
             m_freqbuf[c][i * 2] = m_ro[i];
             m_freqbuf[c][i * 2 + 1] = m_io[i];
         }
     }
 
-    //!!! do we want to adjust the timestamp or anything so as to
-    // effectively centre the frame?
-
     return m_plugin->process(m_freqbuf, timestamp);
 }
 
--- a/vamp-sdk/hostext/PluginInputDomainAdapter.h	Fri May 25 13:26:59 2007 +0000
+++ b/vamp-sdk/hostext/PluginInputDomainAdapter.h	Fri Jun 01 13:53:42 2007 +0000
@@ -90,6 +90,8 @@
 
     void fft(unsigned int n, bool inverse,
              double *ri, double *ii, double *ro, double *io);
+
+    size_t makeBlockSizeAcceptable(size_t) const;
 };
 
 }
--- a/vamp-sdk/hostext/PluginLoader.cpp	Fri May 25 13:26:59 2007 +0000
+++ b/vamp-sdk/hostext/PluginLoader.cpp	Fri Jun 01 13:53:42 2007 +0000
@@ -36,6 +36,8 @@
 
 #include "vamp-sdk/PluginHostAdapter.h"
 #include "PluginLoader.h"
+#include "PluginInputDomainAdapter.h"
+#include "PluginChannelAdapter.h"
 
 #include <fstream>
 
@@ -171,7 +173,7 @@
 }    
 
 Plugin *
-PluginLoader::loadPlugin(PluginKey key, float inputSampleRate)
+PluginLoader::loadPlugin(PluginKey key, float inputSampleRate, int adapterFlags)
 {
     string fullPath = getLibraryPathForPlugin(key);
     if (fullPath == "") return 0;
@@ -206,10 +208,20 @@
             Vamp::PluginHostAdapter *plugin =
                 new Vamp::PluginHostAdapter(descriptor, inputSampleRate);
 
-            PluginDeletionNotifyAdapter *adapter =
-                new PluginDeletionNotifyAdapter(plugin, this);
+            Plugin *adapter = new PluginDeletionNotifyAdapter(plugin, this);
 
             m_pluginLibraryHandleMap[adapter] = handle;
+
+            if (adapterFlags & ADAPT_INPUT_DOMAIN) {
+                if (adapter->getInputDomain() == Plugin::FrequencyDomain) {
+                    adapter = new PluginInputDomainAdapter(adapter);
+                }
+            }
+
+            if (adapterFlags & ADAPT_CHANNEL_COUNT) {
+                adapter = new PluginChannelAdapter(adapter);
+            }
+
             return adapter;
         }
 
--- a/vamp-sdk/hostext/PluginLoader.h	Fri May 25 13:26:59 2007 +0000
+++ b/vamp-sdk/hostext/PluginLoader.h	Fri Jun 01 13:53:42 2007 +0000
@@ -62,7 +62,15 @@
 
     PluginKey composePluginKey(std::string libraryName, std::string identifier);
 
-    Plugin *loadPlugin(PluginKey plugin, float inputSampleRate);
+    enum AdapterFlags {
+        ADAPT_INPUT_DOMAIN  = 0x01,
+        ADAPT_CHANNEL_COUNT = 0x02,
+        ADAPT_ALL           = 0xff
+    };
+
+    Plugin *loadPlugin(PluginKey plugin,
+                       float inputSampleRate,
+                       int adapterFlags = 0);
 
     PluginCategoryHierarchy getPluginCategory(PluginKey plugin);
 
--- a/vamp-sdk/hostext/PluginWrapper.cpp	Fri May 25 13:26:59 2007 +0000
+++ b/vamp-sdk/hostext/PluginWrapper.cpp	Fri Jun 01 13:53:42 2007 +0000
@@ -40,8 +40,14 @@
 
 namespace HostExt {
 
+class PluginRateExtractor : public Plugin
+{
+public:
+    float getRate() const { return m_inputSampleRate; }
+};
+
 PluginWrapper::PluginWrapper(Plugin *plugin) :
-    Plugin(0),
+    Plugin(((PluginRateExtractor *)plugin)->getRate()),
     m_plugin(plugin)
 {
 }
@@ -50,7 +56,7 @@
 {
     delete m_plugin;
 }
-    
+
 bool
 PluginWrapper::initialise(size_t channels, size_t stepSize, size_t blockSize)
 {