Mercurial > hg > vamp-plugin-sdk
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) {