diff options
author | David Robillard <d@drobilla.net> | 2008-02-02 03:57:35 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2008-02-02 03:57:35 +0000 |
commit | 9f63ab9931e6478472853bdda58da47ea29ac125 (patch) | |
tree | 7edfb1d16f580e93501c24fa9f9648fe415f3745 /libs/vamp-sdk | |
parent | 85ea9028b52eefb34184deb0fbd4d3c7632a2c38 (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/SConscript | 9 | ||||
-rw-r--r-- | libs/vamp-sdk/vamp-sdk/RealTime.h | 28 | ||||
-rw-r--r-- | libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp | 490 | ||||
-rw-r--r-- | libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.h | 97 | ||||
-rw-r--r-- | libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.cpp | 147 | ||||
-rw-r--r-- | libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.cpp | 41 | ||||
-rw-r--r-- | libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.h | 28 |
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 }; |