From: Paul Davis Date: Tue, 29 Jan 2008 14:53:08 +0000 (+0000) Subject: merge to 2967 in vamp vendor branch X-Git-Tag: 2.8.16~1651 X-Git-Url: https://git.carlh.net/gitweb/?a=commitdiff_plain;h=3b0e89d43c6e261b7a9c50b07f714f8130bef325;p=ardour.git merge to 2967 in vamp vendor branch git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2975 d708f5d6-7413-0410-9779-e7cbd77b26cf --- diff --git a/libs/vamp-sdk/SConscript b/libs/vamp-sdk/SConscript index f6a27288f9..a6130d3fae 100644 --- a/libs/vamp-sdk/SConscript +++ b/libs/vamp-sdk/SConscript @@ -22,7 +22,9 @@ vamp-sdk/RealTime.cpp Import('env install_prefix libraries') vampsdk = env.Copy() -vampsdk.Append (CPPATH='#libs/vamp-sdk/vamp', CXXFLAGS="-Ilibs/vamp-sdk") +# 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) diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp b/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp index 7ef8540921..406d4978c4 100644 --- a/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp +++ b/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp @@ -7,7 +7,7 @@ Centre for Digital Music, Queen Mary, University of London. Copyright 2006-2007 Chris Cannam and QMUL. - This file by Mark Levy, Copyright 2007 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 @@ -62,15 +62,168 @@ public: 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 > m_queue; - float **m_buffers; // in fact an array of pointers into the queue - size_t m_inputPos; // start position in the queue of next input block + vector m_queue; + float **m_buffers; float m_inputSampleRate; RealTime m_timestamp; OutputList m_outputs; @@ -121,8 +274,8 @@ PluginBufferingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) : m_stepSize(0), m_blockSize(0), m_channels(0), + m_queue(0), m_buffers(0), - m_inputPos(0), m_inputSampleRate(inputSampleRate), m_timestamp() { @@ -132,8 +285,12 @@ PluginBufferingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) : PluginBufferingAdapter::Impl::~Impl() { // the adapter will delete the plugin - - delete [] m_buffers; + + for (size_t i = 0; i < m_channels; ++i) { + delete m_queue[i]; + delete[] m_buffers[i]; + } + delete[] m_buffers; } size_t @@ -184,9 +341,13 @@ PluginBufferingAdapter::Impl::initialise(size_t channels, size_t stepSize, size_ std::cerr << "PluginBufferingAdapter::initialise: plugin's preferred stepSize greater than blockSize, giving up!" << std::endl; return false; } - - m_queue.resize(m_channels); - m_buffers = new float*[m_channels]; + + 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); } @@ -212,23 +373,22 @@ PluginBufferingAdapter::Impl::process(const float *const *inputBuffers, // queue the new input - //std::cerr << "unread " << m_queue[0].size() - m_inputPos << " samples" << std::endl; - //std::cerr << "queueing " << m_inputBlockSize - (m_queue[0].size() - m_inputPos) << " samples" << std::endl; - - for (size_t i = 0; i < m_channels; ++i) - for (size_t j = m_queue[0].size() - m_inputPos; j < m_inputBlockSize; ++j) - m_queue[i].push_back(inputBuffers[i][j]); - - m_inputPos += m_inputStepSize; + 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].size() >= m_blockSize) - { + + while (m_queue[0]->getReadSpace() >= int(m_blockSize)) { processBlock(allFeatureSets, timestamp); - m_inputPos -= m_stepSize; - - //std::cerr << m_queue[0].size() << " samples still left in queue" << std::endl; - //std::cerr << "inputPos = " << m_inputPos << std::endl; } return allFeatureSets; @@ -240,67 +400,67 @@ PluginBufferingAdapter::Impl::getRemainingFeatures() FeatureSet allFeatureSets; // process remaining samples in queue - while (m_queue[0].size() >= m_blockSize) - { + while (m_queue[0]->getReadSpace() >= int(m_blockSize)) { processBlock(allFeatureSets, m_timestamp); } // pad any last samples remaining and process - if (m_queue[0].size() > 0) - { - for (size_t i = 0; i < m_channels; ++i) - while (m_queue[i].size() < m_blockSize) - m_queue[i].push_back(0.0); + 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::iterator iter = featureSet.begin(); - iter != featureSet.end(); ++iter) - { + iter != featureSet.end(); ++iter) { FeatureList featureList = iter->second; - for (size_t i = 0; i < featureList.size(); ++i) - allFeatureSets[iter->first].push_back(featureList[i]); + 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) +PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets, + RealTime timestamp) { - //std::cerr << m_queue[0].size() << " samples left in queue" << std::endl; - - // point the buffers to the head of the queue - for (size_t i = 0; i < m_channels; ++i) - m_buffers[i] = &m_queue[i][0]; - + 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::iterator iter = featureSet.begin(); - iter != featureSet.end(); ++iter) - { + iter != featureSet.end(); ++iter) { FeatureList featureList = iter->second; int outputNo = iter->first; - for (size_t i = 0; i < featureList.size(); ++i) - { + for (size_t i = 0; i < featureList.size(); ++i) { // make sure the timestamp is set - switch (m_outputs[outputNo].sampleType) - { + 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; } @@ -310,14 +470,17 @@ PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets, RealTime } // step forward - for (size_t i = 0; i < m_channels; ++i) - m_queue[i].erase(m_queue[i].begin(), m_queue[i].begin() + m_stepSize); + + for (size_t i = 0; i < m_channels; ++i) { + m_queue[i]->skip(m_stepSize); + } // fake up the timestamp each time we step forward - //std::cerr << m_timestamp; - long frame = RealTime::realTime2Frame(m_timestamp, int(m_inputSampleRate + 0.5)); - m_timestamp = RealTime::frame2RealTime(frame + m_stepSize, int(m_inputSampleRate + 0.5)); - //std::cerr << "--->" << m_timestamp << std::endl; + + 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/PluginInputDomainAdapter.cpp b/libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.cpp index d664486557..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 + +/** + * 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 +#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; }; @@ -117,7 +146,16 @@ PluginInputDomainAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) : 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 + } }