merge to 2967 in vamp vendor branch

git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2975 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2008-01-29 14:53:08 +00:00
parent 301009848c
commit 3b0e89d43c
3 changed files with 338 additions and 82 deletions

View file

@ -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)

View file

@ -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<vector<float> > 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<RingBuffer *> 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<int, FeatureList>::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<int, FeatureList>::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));
}
}

View file

@ -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;
};
@ -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
}
}