summaryrefslogtreecommitdiff
path: root/libs/vamp-sdk
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2008-02-02 03:57:35 +0000
committerDavid Robillard <d@drobilla.net>2008-02-02 03:57:35 +0000
commit9f63ab9931e6478472853bdda58da47ea29ac125 (patch)
tree7edfb1d16f580e93501c24fa9f9648fe415f3745 /libs/vamp-sdk
parent85ea9028b52eefb34184deb0fbd4d3c7632a2c38 (diff)
Merge with trunk R2978.
git-svn-id: svn://localhost/ardour2/branches/3.0@2988 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/vamp-sdk')
-rw-r--r--libs/vamp-sdk/SConscript9
-rw-r--r--libs/vamp-sdk/vamp-sdk/RealTime.h28
-rw-r--r--libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp490
-rw-r--r--libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.h97
-rw-r--r--libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.cpp147
-rw-r--r--libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.cpp41
-rw-r--r--libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.h28
7 files changed, 794 insertions, 46 deletions
diff --git a/libs/vamp-sdk/SConscript b/libs/vamp-sdk/SConscript
index ddd3d8ebbc..abf9d86534 100644
--- a/libs/vamp-sdk/SConscript
+++ b/libs/vamp-sdk/SConscript
@@ -11,6 +11,7 @@ vamp-sdk/RealTime.cpp
vamphostsdk_files = Split ("""
vamp-sdk/PluginHostAdapter.cpp
+vamp-sdk/hostext/PluginBufferingAdapter.cpp
vamp-sdk/hostext/PluginChannelAdapter.cpp
vamp-sdk/hostext/PluginInputDomainAdapter.cpp
vamp-sdk/hostext/PluginLoader.cpp
@@ -21,7 +22,11 @@ vamp-sdk/RealTime.cpp
Import('env install_prefix libraries')
vampsdk = env.Copy()
-vampsdk.Append (CPPATH='#libs/vamp-sdk/vamp', CXXFLAGS="-Ilibs/vamp-sdk")
+vampsdk.Merge ([libraries['fftw3'], libraries['fftw3f']])
+
+# HAVE_FFTW3 is used to help improve some performance aspects of VAMP's InputDomainAdapter
+
+vampsdk.Append (CPPATH='#libs/vamp-sdk/vamp', CXXFLAGS="-Ilibs/vamp-sdk -DHAVE_FFTW3")
libvampsdk = vampsdk.SharedLibrary('vampsdk', vampsdk_files)
libvamphostsdk = vampsdk.SharedLibrary('vamphostsdk', vamphostsdk_files)
@@ -30,6 +35,8 @@ Default(libvampsdk)
Default(libvamphostsdk)
env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libvampsdk))
+env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libvamphostsdk))
+
env.Alias('tarball', env.Distribute (env['DISTTREE'],
[ 'SConscript', 'COPYING', 'README' ] +
vampsdk_files +
diff --git a/libs/vamp-sdk/vamp-sdk/RealTime.h b/libs/vamp-sdk/vamp-sdk/RealTime.h
index 4dd78fd209..6b88ed53f0 100644
--- a/libs/vamp-sdk/vamp-sdk/RealTime.h
+++ b/libs/vamp-sdk/vamp-sdk/RealTime.h
@@ -125,23 +125,31 @@ struct RealTime
RealTime operator/(int d) const;
- // Find the fractional difference between times
- //
+ /**
+ * Return the ratio of two times.
+ */
double operator/(const RealTime &r) const;
- // Return a human-readable debug-type string to full precision
- // (probably not a format to show to a user directly)
- //
+ /**
+ * Return a human-readable debug-type string to full precision
+ * (probably not a format to show to a user directly)
+ */
std::string toString() const;
- // Return a user-readable string to the nearest millisecond
- // in a form like HH:MM:SS.mmm
- //
+ /**
+ * Return a user-readable string to the nearest millisecond
+ * in a form like HH:MM:SS.mmm
+ */
std::string toText(bool fixedDp = false) const;
- // Convenience functions for handling sample frames
- //
+ /**
+ * Convert a RealTime into a sample frame at the given sample rate.
+ */
static long realTime2Frame(const RealTime &r, unsigned int sampleRate);
+
+ /**
+ * Convert a sample frame at the given sample rate into a RealTime.
+ */
static RealTime frame2RealTime(long frame, unsigned int sampleRate);
static const RealTime zeroTime;
diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp b/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp
new file mode 100644
index 0000000000..406d4978c4
--- /dev/null
+++ b/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp
@@ -0,0 +1,490 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Vamp
+
+ An API for audio analysis and feature extraction plugins.
+
+ Centre for Digital Music, Queen Mary, University of London.
+ Copyright 2006-2007 Chris Cannam and QMUL.
+ This file by Mark Levy and Chris Cannam.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use, copy,
+ modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+ ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ Except as contained in this notice, the names of the Centre for
+ Digital Music; Queen Mary, University of London; and Chris Cannam
+ shall not be used in advertising or otherwise to promote the sale,
+ use or other dealings in this Software without prior written
+ authorization.
+*/
+
+#include <vector>
+#include <map>
+
+#include "PluginBufferingAdapter.h"
+
+using std::vector;
+using std::map;
+
+namespace Vamp {
+
+namespace HostExt {
+
+class PluginBufferingAdapter::Impl
+{
+public:
+ Impl(Plugin *plugin, float inputSampleRate);
+ ~Impl();
+
+ bool initialise(size_t channels, size_t stepSize, size_t blockSize);
+
+ OutputList getOutputDescriptors() const;
+
+ FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
+
+ FeatureSet getRemainingFeatures();
+
+protected:
+ class RingBuffer
+ {
+ public:
+ RingBuffer(int n) :
+ m_buffer(new float[n+1]), m_writer(0), m_reader(0), m_size(n+1) { }
+ virtual ~RingBuffer() { delete[] m_buffer; }
+
+ int getSize() const { return m_size-1; }
+ void reset() { m_writer = 0; m_reader = 0; }
+
+ int getReadSpace() const {
+ int writer = m_writer, reader = m_reader, space;
+ if (writer > reader) space = writer - reader;
+ else if (writer < reader) space = (writer + m_size) - reader;
+ else space = 0;
+ return space;
+ }
+
+ int getWriteSpace() const {
+ int writer = m_writer;
+ int reader = m_reader;
+ int space = (reader + m_size - writer - 1);
+ if (space >= m_size) space -= m_size;
+ return space;
+ }
+
+ int peek(float *destination, int n) const {
+
+ int available = getReadSpace();
+
+ if (n > available) {
+ for (int i = available; i < n; ++i) {
+ destination[i] = 0.f;
+ }
+ n = available;
+ }
+ if (n == 0) return n;
+
+ int reader = m_reader;
+ int here = m_size - reader;
+ const float *const bufbase = m_buffer + reader;
+
+ if (here >= n) {
+ for (int i = 0; i < n; ++i) {
+ destination[i] = bufbase[i];
+ }
+ } else {
+ for (int i = 0; i < here; ++i) {
+ destination[i] = bufbase[i];
+ }
+ float *const destbase = destination + here;
+ const int nh = n - here;
+ for (int i = 0; i < nh; ++i) {
+ destbase[i] = m_buffer[i];
+ }
+ }
+
+ return n;
+ }
+
+ int skip(int n) {
+
+ int available = getReadSpace();
+ if (n > available) {
+ n = available;
+ }
+ if (n == 0) return n;
+
+ int reader = m_reader;
+ reader += n;
+ while (reader >= m_size) reader -= m_size;
+ m_reader = reader;
+ return n;
+ }
+
+ int write(const float *source, int n) {
+
+ int available = getWriteSpace();
+ if (n > available) {
+ n = available;
+ }
+ if (n == 0) return n;
+
+ int writer = m_writer;
+ int here = m_size - writer;
+ float *const bufbase = m_buffer + writer;
+
+ if (here >= n) {
+ for (int i = 0; i < n; ++i) {
+ bufbase[i] = source[i];
+ }
+ } else {
+ for (int i = 0; i < here; ++i) {
+ bufbase[i] = source[i];
+ }
+ const int nh = n - here;
+ const float *const srcbase = source + here;
+ float *const buf = m_buffer;
+ for (int i = 0; i < nh; ++i) {
+ buf[i] = srcbase[i];
+ }
+ }
+
+ writer += n;
+ while (writer >= m_size) writer -= m_size;
+ m_writer = writer;
+
+ return n;
+ }
+
+ int zero(int n) {
+
+ int available = getWriteSpace();
+ if (n > available) {
+ n = available;
+ }
+ if (n == 0) return n;
+
+ int writer = m_writer;
+ int here = m_size - writer;
+ float *const bufbase = m_buffer + writer;
+
+ if (here >= n) {
+ for (int i = 0; i < n; ++i) {
+ bufbase[i] = 0.f;
+ }
+ } else {
+ for (int i = 0; i < here; ++i) {
+ bufbase[i] = 0.f;
+ }
+ const int nh = n - here;
+ for (int i = 0; i < nh; ++i) {
+ m_buffer[i] = 0.f;
+ }
+ }
+
+ writer += n;
+ while (writer >= m_size) writer -= m_size;
+ m_writer = writer;
+
+ return n;
+ }
+
+ protected:
+ float *m_buffer;
+ int m_writer;
+ int m_reader;
+ int m_size;
+
+ private:
+ RingBuffer(const RingBuffer &); // not provided
+ RingBuffer &operator=(const RingBuffer &); // not provided
+ };
+
+ Plugin *m_plugin;
+ size_t m_inputStepSize;
+ size_t m_inputBlockSize;
+ size_t m_stepSize;
+ size_t m_blockSize;
+ size_t m_channels;
+ vector<RingBuffer *> m_queue;
+ float **m_buffers;
+ float m_inputSampleRate;
+ RealTime m_timestamp;
+ OutputList m_outputs;
+
+ void processBlock(FeatureSet& allFeatureSets, RealTime timestamp);
+};
+
+PluginBufferingAdapter::PluginBufferingAdapter(Plugin *plugin) :
+ PluginWrapper(plugin)
+{
+ m_impl = new Impl(plugin, m_inputSampleRate);
+}
+
+PluginBufferingAdapter::~PluginBufferingAdapter()
+{
+ delete m_impl;
+}
+
+bool
+PluginBufferingAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
+{
+ return m_impl->initialise(channels, stepSize, blockSize);
+}
+
+PluginBufferingAdapter::OutputList
+PluginBufferingAdapter::getOutputDescriptors() const
+{
+ return m_impl->getOutputDescriptors();
+}
+
+PluginBufferingAdapter::FeatureSet
+PluginBufferingAdapter::process(const float *const *inputBuffers,
+ RealTime timestamp)
+{
+ return m_impl->process(inputBuffers, timestamp);
+}
+
+PluginBufferingAdapter::FeatureSet
+PluginBufferingAdapter::getRemainingFeatures()
+{
+ return m_impl->getRemainingFeatures();
+}
+
+PluginBufferingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
+ m_plugin(plugin),
+ m_inputStepSize(0),
+ m_inputBlockSize(0),
+ m_stepSize(0),
+ m_blockSize(0),
+ m_channels(0),
+ m_queue(0),
+ m_buffers(0),
+ m_inputSampleRate(inputSampleRate),
+ m_timestamp()
+{
+ m_outputs = plugin->getOutputDescriptors();
+}
+
+PluginBufferingAdapter::Impl::~Impl()
+{
+ // the adapter will delete the plugin
+
+ for (size_t i = 0; i < m_channels; ++i) {
+ delete m_queue[i];
+ delete[] m_buffers[i];
+ }
+ delete[] m_buffers;
+}
+
+size_t
+PluginBufferingAdapter::getPreferredStepSize() const
+{
+ return getPreferredBlockSize();
+}
+
+bool
+PluginBufferingAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
+{
+ if (stepSize != blockSize) {
+ std::cerr << "PluginBufferingAdapter::initialise: input stepSize must be equal to blockSize for this adapter (stepSize = " << stepSize << ", blockSize = " << blockSize << ")" << std::endl;
+ return false;
+ }
+
+ m_channels = channels;
+ m_inputStepSize = stepSize;
+ m_inputBlockSize = blockSize;
+
+ // use the step and block sizes which the plugin prefers
+ m_stepSize = m_plugin->getPreferredStepSize();
+ m_blockSize = m_plugin->getPreferredBlockSize();
+
+ // or sensible defaults if it has no preference
+ if (m_blockSize == 0) {
+ m_blockSize = 1024;
+ }
+ if (m_stepSize == 0) {
+ if (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
+ m_stepSize = m_blockSize/2;
+ } else {
+ m_stepSize = m_blockSize;
+ }
+ } else if (m_stepSize > m_blockSize) {
+ if (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
+ m_blockSize = m_stepSize * 2;
+ } else {
+ m_blockSize = m_stepSize;
+ }
+ }
+
+ std::cerr << "PluginBufferingAdapter::initialise: stepSize " << m_inputStepSize << " -> " << m_stepSize
+ << ", blockSize " << m_inputBlockSize << " -> " << m_blockSize << std::endl;
+
+ // current implementation breaks if step is greater than block
+ if (m_stepSize > m_blockSize) {
+ std::cerr << "PluginBufferingAdapter::initialise: plugin's preferred stepSize greater than blockSize, giving up!" << std::endl;
+ return false;
+ }
+
+ m_buffers = new float *[m_channels];
+
+ for (size_t i = 0; i < m_channels; ++i) {
+ m_queue.push_back(new RingBuffer(m_blockSize + m_inputBlockSize));
+ m_buffers[i] = new float[m_blockSize];
+ }
+
+ return m_plugin->initialise(m_channels, m_stepSize, m_blockSize);
+}
+
+PluginBufferingAdapter::OutputList
+PluginBufferingAdapter::Impl::getOutputDescriptors() const
+{
+ OutputList outs = m_plugin->getOutputDescriptors();
+ for (size_t i = 0; i < outs.size(); ++i) {
+ if (outs[i].sampleType == OutputDescriptor::OneSamplePerStep) {
+ outs[i].sampleRate = 1.f / m_stepSize;
+ }
+ outs[i].sampleType = OutputDescriptor::VariableSampleRate;
+ }
+ return outs;
+}
+
+PluginBufferingAdapter::FeatureSet
+PluginBufferingAdapter::Impl::process(const float *const *inputBuffers,
+ RealTime timestamp)
+{
+ FeatureSet allFeatureSets;
+
+ // queue the new input
+
+ for (size_t i = 0; i < m_channels; ++i) {
+ int written = m_queue[i]->write(inputBuffers[i], m_inputBlockSize);
+ if (written < int(m_inputBlockSize) && i == 0) {
+ std::cerr << "WARNING: PluginBufferingAdapter::Impl::process: "
+ << "Buffer overflow: wrote " << written
+ << " of " << m_inputBlockSize
+ << " input samples (for plugin step size "
+ << m_stepSize << ", block size " << m_blockSize << ")"
+ << std::endl;
+ }
+ }
+
+ // process as much as we can
+
+ while (m_queue[0]->getReadSpace() >= int(m_blockSize)) {
+ processBlock(allFeatureSets, timestamp);
+ }
+
+ return allFeatureSets;
+}
+
+PluginBufferingAdapter::FeatureSet
+PluginBufferingAdapter::Impl::getRemainingFeatures()
+{
+ FeatureSet allFeatureSets;
+
+ // process remaining samples in queue
+ while (m_queue[0]->getReadSpace() >= int(m_blockSize)) {
+ processBlock(allFeatureSets, m_timestamp);
+ }
+
+ // pad any last samples remaining and process
+ if (m_queue[0]->getReadSpace() > 0) {
+ for (size_t i = 0; i < m_channels; ++i) {
+ m_queue[i]->zero(m_blockSize - m_queue[i]->getReadSpace());
+ }
+ processBlock(allFeatureSets, m_timestamp);
+ }
+
+ // get remaining features
+
+ FeatureSet featureSet = m_plugin->getRemainingFeatures();
+
+ for (map<int, FeatureList>::iterator iter = featureSet.begin();
+ iter != featureSet.end(); ++iter) {
+ FeatureList featureList = iter->second;
+ for (size_t i = 0; i < featureList.size(); ++i) {
+ allFeatureSets[iter->first].push_back(featureList[i]);
+ }
+ }
+
+ return allFeatureSets;
+}
+
+void
+PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets,
+ RealTime timestamp)
+{
+ for (size_t i = 0; i < m_channels; ++i) {
+ m_queue[i]->peek(m_buffers[i], m_blockSize);
+ }
+
+ FeatureSet featureSet = m_plugin->process(m_buffers, m_timestamp);
+
+ for (map<int, FeatureList>::iterator iter = featureSet.begin();
+ iter != featureSet.end(); ++iter) {
+
+ FeatureList featureList = iter->second;
+ int outputNo = iter->first;
+
+ for (size_t i = 0; i < featureList.size(); ++i) {
+
+ // make sure the timestamp is set
+ switch (m_outputs[outputNo].sampleType) {
+
+ case OutputDescriptor::OneSamplePerStep:
+ // use our internal timestamp - OK????
+ featureList[i].timestamp = m_timestamp;
+ break;
+
+ case OutputDescriptor::FixedSampleRate:
+ // use our internal timestamp
+ featureList[i].timestamp = m_timestamp;
+ break;
+
+ case OutputDescriptor::VariableSampleRate:
+ break; // plugin must set timestamp
+
+ default:
+ break;
+ }
+
+ allFeatureSets[outputNo].push_back(featureList[i]);
+ }
+ }
+
+ // step forward
+
+ for (size_t i = 0; i < m_channels; ++i) {
+ m_queue[i]->skip(m_stepSize);
+ }
+
+ // fake up the timestamp each time we step forward
+
+ long frame = RealTime::realTime2Frame(m_timestamp,
+ int(m_inputSampleRate + 0.5));
+ m_timestamp = RealTime::frame2RealTime(frame + m_stepSize,
+ int(m_inputSampleRate + 0.5));
+}
+
+}
+
+}
+
+
diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.h b/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.h
new file mode 100644
index 0000000000..96a958b9e5
--- /dev/null
+++ b/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.h
@@ -0,0 +1,97 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Vamp
+
+ An API for audio analysis and feature extraction plugins.
+
+ Centre for Digital Music, Queen Mary, University of London.
+ Copyright 2006-2007 Chris Cannam and QMUL.
+ This file by Mark Levy, Copyright 2007 QMUL.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use, copy,
+ modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+ ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ Except as contained in this notice, the names of the Centre for
+ Digital Music; Queen Mary, University of London; and Chris Cannam
+ shall not be used in advertising or otherwise to promote the sale,
+ use or other dealings in this Software without prior written
+ authorization.
+*/
+
+#ifndef _VAMP_PLUGIN_BUFFERING_ADAPTER_H_
+#define _VAMP_PLUGIN_BUFFERING_ADAPTER_H_
+
+#include "PluginWrapper.h"
+
+namespace Vamp {
+
+namespace HostExt {
+
+/**
+ * \class PluginBufferingAdapter PluginBufferingAdapter.h <vamp-sdk/hostext/PluginBufferingAdapter.h>
+ *
+ * PluginBufferingAdapter is a Vamp plugin adapter that allows plugins
+ * to be used by a host supplying an audio stream in non-overlapping
+ * buffers of arbitrary size.
+ *
+ * A host using PluginBufferingAdapter may ignore the preferred step
+ * and block size reported by the plugin, and still expect the plugin
+ * to run. The value of blockSize and stepSize passed to initialise
+ * should be the size of the buffer which the host will supply; the
+ * stepSize should be equal to the blockSize.
+ *
+ * If the internal step size used for the plugin differs from that
+ * supplied by the host, the adapter will modify the sample rate
+ * specifications for the plugin outputs (setting them all to
+ * VariableSampleRate) and set timestamps on the output features for
+ * outputs that formerly used a different sample rate specification.
+ * This is necessary in order to obtain correct time stamping.
+ *
+ * In other respects, the PluginBufferingAdapter behaves identically
+ * to the plugin that it wraps. The wrapped plugin will be deleted
+ * when the wrapper is deleted.
+ */
+
+class PluginBufferingAdapter : public PluginWrapper
+{
+public:
+ PluginBufferingAdapter(Plugin *plugin); // I take ownership of plugin
+ virtual ~PluginBufferingAdapter();
+
+ bool initialise(size_t channels, size_t stepSize, size_t blockSize);
+
+ size_t getPreferredStepSize() const;
+
+ OutputList getOutputDescriptors() const;
+
+ FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
+
+ FeatureSet getRemainingFeatures();
+
+protected:
+ class Impl;
+ Impl *m_impl;
+};
+
+}
+
+}
+
+#endif
diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.cpp b/libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.cpp
index 3fc0d74720..e706414ae2 100644
--- a/libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.cpp
+++ b/libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.cpp
@@ -38,6 +38,28 @@
#include <cmath>
+
+/**
+ * If you want to compile using FFTW instead of the built-in FFT
+ * implementation for the PluginInputDomainAdapter, define HAVE_FFTW3
+ * in the Makefile.
+ *
+ * Remember that FFTW is licensed under the GPL (unlike this SDK, which
+ * is licensed liberally in order to permit closed-source usage), so
+ * you should not define this symbol unless your code is also under the
+ * GPL. Also, parties redistributing this SDK for use in other
+ * programs should be careful _not_ to define this symbol in order not
+ * to affect the stated license of this SDK.
+ *
+ * Note: This code uses FFTW_MEASURE, and will perform badly on its
+ * first invocation unless the host has saved and restored FFTW wisdom
+ * (see the FFTW documentation).
+ */
+#ifdef HAVE_FFTW3
+#include <fftw3.h>
+#endif
+
+
namespace Vamp {
namespace HostExt {
@@ -58,15 +80,22 @@ public:
protected:
Plugin *m_plugin;
float m_inputSampleRate;
- size_t m_channels;
- size_t m_blockSize;
+ int m_channels;
+ int m_blockSize;
float **m_freqbuf;
+
double *m_ri;
+ double *m_window;
+
+#ifdef HAVE_FFTW3
+ fftw_plan m_plan;
+ fftw_complex *m_cbuf;
+#else
double *m_ro;
double *m_io;
-
void fft(unsigned int n, bool inverse,
double *ri, double *ii, double *ro, double *io);
+#endif
size_t makeBlockSizeAcceptable(size_t) const;
};
@@ -112,12 +141,21 @@ PluginInputDomainAdapter::process(const float *const *inputBuffers, RealTime tim
return m_impl->process(inputBuffers, timestamp);
}
- PluginInputDomainAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
+PluginInputDomainAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
m_plugin(plugin),
m_inputSampleRate(inputSampleRate),
m_channels(0),
m_blockSize(0),
- m_freqbuf(0)
+ m_freqbuf(0),
+ m_ri(0),
+ m_window(0),
+#ifdef HAVE_FFTW3
+ m_plan(0),
+ m_cbuf(0)
+#else
+ m_ro(0),
+ m_io(0)
+#endif
{
}
@@ -126,23 +164,38 @@ PluginInputDomainAdapter::Impl::~Impl()
// the adapter will delete the plugin
if (m_channels > 0) {
- for (size_t c = 0; c < m_channels; ++c) {
+ for (int c = 0; c < m_channels; ++c) {
delete[] m_freqbuf[c];
}
delete[] m_freqbuf;
+#ifdef HAVE_FFTW3
+ if (m_plan) {
+ fftw_destroy_plan(m_plan);
+ fftw_free(m_ri);
+ fftw_free(m_cbuf);
+ m_plan = 0;
+ }
+#else
delete[] m_ri;
delete[] m_ro;
delete[] m_io;
+#endif
+ delete[] m_window;
}
}
+
+// for some visual studii apparently
+#ifndef M_PI
+#define M_PI 3.14159265358979232846
+#endif
bool
PluginInputDomainAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
if (m_plugin->getInputDomain() == TimeDomain) {
- m_blockSize = blockSize;
- m_channels = channels;
+ m_blockSize = int(blockSize);
+ m_channels = int(channels);
return m_plugin->initialise(channels, stepSize, blockSize);
}
@@ -158,25 +211,48 @@ PluginInputDomainAdapter::Impl::initialise(size_t channels, size_t stepSize, siz
}
if (m_channels > 0) {
- for (size_t c = 0; c < m_channels; ++c) {
+ for (int c = 0; c < m_channels; ++c) {
delete[] m_freqbuf[c];
}
delete[] m_freqbuf;
+#ifdef HAVE_FFTW3
+ if (m_plan) {
+ fftw_destroy_plan(m_plan);
+ fftw_free(m_ri);
+ fftw_free(m_cbuf);
+ m_plan = 0;
+ }
+#else
delete[] m_ri;
delete[] m_ro;
delete[] m_io;
+#endif
+ delete[] m_window;
}
- m_blockSize = blockSize;
- m_channels = channels;
+ m_blockSize = int(blockSize);
+ m_channels = int(channels);
m_freqbuf = new float *[m_channels];
- for (size_t c = 0; c < m_channels; ++c) {
+ for (int c = 0; c < m_channels; ++c) {
m_freqbuf[c] = new float[m_blockSize + 2];
}
+ m_window = new double[m_blockSize];
+
+ for (int i = 0; i < m_blockSize; ++i) {
+ // Hanning window
+ m_window[i] = (0.50 - 0.50 * cos((2.0 * M_PI * i) / m_blockSize));
+ }
+
+#ifdef HAVE_FFTW3
+ m_ri = (double *)fftw_malloc(blockSize * sizeof(double));
+ m_cbuf = (fftw_complex *)fftw_malloc((blockSize/2 + 1) * sizeof(fftw_complex));
+ m_plan = fftw_plan_dft_r2c_1d(blockSize, m_ri, m_cbuf, FFTW_MEASURE);
+#else
m_ri = new double[m_blockSize];
m_ro = new double[m_blockSize];
m_io = new double[m_blockSize];
+#endif
return m_plugin->initialise(channels, stepSize, blockSize);
}
@@ -220,7 +296,11 @@ PluginInputDomainAdapter::Impl::makeBlockSizeAcceptable(size_t blockSize) const
} else if (blockSize & (blockSize-1)) {
- // not a power of two, can't handle that with our current fft
+#ifdef HAVE_FFTW3
+ // not an issue with FFTW
+#else
+
+ // not a power of two, can't handle that with our built-in FFT
// implementation
size_t nearest = blockSize;
@@ -241,16 +321,13 @@ PluginInputDomainAdapter::Impl::makeBlockSizeAcceptable(size_t blockSize) const
std::cerr << "WARNING: Vamp::HostExt::PluginInputDomainAdapter::Impl::initialise: non-power-of-two\nblocksize " << blockSize << " not supported, using blocksize " << nearest << " instead" << std::endl;
blockSize = nearest;
+
+#endif
}
return blockSize;
}
-// for some visual studii apparently
-#ifndef M_PI
-#define M_PI 3.14159265358979232846
-#endif
-
Plugin::FeatureSet
PluginInputDomainAdapter::Impl::process(const float *const *inputBuffers,
RealTime timestamp)
@@ -308,33 +385,45 @@ PluginInputDomainAdapter::Impl::process(const float *const *inputBuffers,
// std::cerr << " to " << timestamp << std::endl;
- for (size_t c = 0; c < m_channels; ++c) {
+ for (int c = 0; c < m_channels; ++c) {
- for (size_t i = 0; i < m_blockSize; ++i) {
- // Hanning window
- m_ri[i] = double(inputBuffers[c][i])
- * (0.50 - 0.50 * cos((2 * M_PI * i)
- / m_blockSize));
+ for (int i = 0; i < m_blockSize; ++i) {
+ m_ri[i] = double(inputBuffers[c][i]) * m_window[i];
}
- for (size_t i = 0; i < m_blockSize/2; ++i) {
+ for (int i = 0; i < m_blockSize/2; ++i) {
// FFT shift
double value = m_ri[i];
m_ri[i] = m_ri[i + m_blockSize/2];
m_ri[i + m_blockSize/2] = value;
}
+#ifdef HAVE_FFTW3
+
+ fftw_execute(m_plan);
+
+ for (int i = 0; i <= m_blockSize/2; ++i) {
+ m_freqbuf[c][i * 2] = float(m_cbuf[i][0]);
+ m_freqbuf[c][i * 2 + 1] = float(m_cbuf[i][1]);
+ }
+
+#else
+
fft(m_blockSize, false, m_ri, 0, m_ro, m_io);
- 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];
+ for (int i = 0; i <= m_blockSize/2; ++i) {
+ m_freqbuf[c][i * 2] = float(m_ro[i]);
+ m_freqbuf[c][i * 2 + 1] = float(m_io[i]);
}
+
+#endif
}
return m_plugin->process(m_freqbuf, timestamp);
}
+#ifndef HAVE_FFTW3
+
void
PluginInputDomainAdapter::Impl::fft(unsigned int n, bool inverse,
double *ri, double *ii, double *ro, double *io)
@@ -452,6 +541,8 @@ PluginInputDomainAdapter::Impl::fft(unsigned int n, bool inverse,
}
}
+#endif
+
}
}
diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.cpp b/libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.cpp
index cb71fc4750..99baac3b72 100644
--- a/libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.cpp
+++ b/libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.cpp
@@ -38,6 +38,7 @@
#include "PluginLoader.h"
#include "PluginInputDomainAdapter.h"
#include "PluginChannelAdapter.h"
+#include "PluginBufferingAdapter.h"
#include <fstream>
#include <cctype> // tolower
@@ -85,6 +86,8 @@ public:
string getLibraryPathForPlugin(PluginKey key);
+ static void setInstanceToClean(PluginLoader *instance);
+
protected:
class PluginDeletionNotifyAdapter : public PluginWrapper {
public:
@@ -94,6 +97,15 @@ protected:
Impl *m_loader;
};
+ class InstanceCleaner {
+ public:
+ InstanceCleaner() : m_instance(0) { }
+ ~InstanceCleaner() { delete m_instance; }
+ void setInstance(PluginLoader *instance) { m_instance = instance; }
+ protected:
+ PluginLoader *m_instance;
+ };
+
virtual void pluginDeleted(PluginDeletionNotifyAdapter *adapter);
map<PluginKey, string> m_pluginLibraryNameMap;
@@ -114,11 +126,16 @@ protected:
string splicePath(string a, string b);
vector<string> listFiles(string dir, string ext);
+
+ static InstanceCleaner m_cleaner;
};
PluginLoader *
PluginLoader::m_instance = 0;
+PluginLoader::Impl::InstanceCleaner
+PluginLoader::Impl::m_cleaner;
+
PluginLoader::PluginLoader()
{
m_impl = new Impl();
@@ -132,7 +149,13 @@ PluginLoader::~PluginLoader()
PluginLoader *
PluginLoader::getInstance()
{
- if (!m_instance) m_instance = new PluginLoader();
+ if (!m_instance) {
+ // The cleaner doesn't own the instance, because we leave the
+ // instance pointer in the base class for binary backwards
+ // compatibility reasons and to avoid waste
+ m_instance = new PluginLoader();
+ Impl::setInstanceToClean(m_instance);
+ }
return m_instance;
}
@@ -177,6 +200,12 @@ PluginLoader::Impl::~Impl()
{
}
+void
+PluginLoader::Impl::setInstanceToClean(PluginLoader *instance)
+{
+ m_cleaner.setInstance(instance);
+}
+
vector<PluginLoader::PluginKey>
PluginLoader::Impl::listPlugins()
{
@@ -366,6 +395,10 @@ PluginLoader::Impl::loadPlugin(PluginKey key,
}
}
+ if (adapterFlags & ADAPT_BUFFER_SIZE) {
+ adapter = new PluginBufferingAdapter(adapter);
+ }
+
if (adapterFlags & ADAPT_CHANNEL_COUNT) {
adapter = new PluginChannelAdapter(adapter);
}
@@ -549,9 +582,11 @@ PluginLoader::Impl::listFiles(string dir, string extension)
struct dirent *e = 0;
while ((e = readdir(d))) {
+
+ if (!(e->d_type & DT_REG) && (e->d_type != DT_UNKNOWN)) continue;
- if (!(e->d_type & DT_REG) || !e->d_name) continue;
-
+ if (!e->d_name) continue;
+
size_t len = strlen(e->d_name);
if (len < extlen + 2 ||
e->d_name + len - extlen - 1 != "." + extension) {
diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.h b/libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.h
index 82ae22b930..f48143c11e 100644
--- a/libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.h
+++ b/libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.h
@@ -142,15 +142,35 @@ public:
* to be mixed down to mono, etc., without having to worry about
* doing that itself.
*
- * ADAPT_ALL - Perform all available adaptations, where meaningful.
+ * ADAPT_BUFFER_SIZE - Wrap the plugin in a PluginBufferingAdapter
+ * permitting the host to provide audio input using any block
+ * size, with no overlap, regardless of the plugin's preferred
+ * block size (suitable for hosts that read from non-seekable
+ * streaming media, for example). This adapter introduces some
+ * run-time overhead and also changes the semantics of the plugin
+ * slightly (see the PluginBufferingAdapter header documentation
+ * for details).
+ *
+ * ADAPT_ALL_SAFE - Perform all available adaptations that are
+ * meaningful for the plugin and "safe". Currently this means to
+ * ADAPT_INPUT_DOMAIN if the plugin wants FrequencyDomain input;
+ * ADAPT_CHANNEL_COUNT always; and ADAPT_BUFFER_SIZE never.
+ *
+ * ADAPT_ALL - Perform all available adaptations that are
+ * meaningful for the plugin.
*
- * See PluginInputDomainAdapter and PluginChannelAdapter for more
- * details of the classes that the loader may use if these flags
- * are set.
+ * See PluginInputDomainAdapter, PluginChannelAdapter and
+ * PluginBufferingAdapter for more details of the classes that the
+ * loader may use if these flags are set.
*/
enum AdapterFlags {
+
ADAPT_INPUT_DOMAIN = 0x01,
ADAPT_CHANNEL_COUNT = 0x02,
+ ADAPT_BUFFER_SIZE = 0x04,
+
+ ADAPT_ALL_SAFE = 0x03,
+
ADAPT_ALL = 0xff
};