merge to 2967 in vamp vendor branch
authorPaul Davis <paul@linuxaudiosystems.com>
Tue, 29 Jan 2008 14:53:08 +0000 (14:53 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Tue, 29 Jan 2008 14:53:08 +0000 (14:53 +0000)
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2975 d708f5d6-7413-0410-9779-e7cbd77b26cf

libs/vamp-sdk/SConscript
libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp
libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.cpp

index f6a27288f9653d62b5b844297ce0df9a2be48af9..a6130d3faec988f9e50a26ef3a3c7a0175faade1 100644 (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)
index 7ef8540921dfcf233cf8dbd3c32e79bcbcbc363e..406d4978c468894407fa2b393ae721b4e14b19c3 100644 (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));
 }
 
 }
index d6644865570b6eb4b2925d61087a35b062256ec5..e706414ae2416ab6a3ca644815040601c66a0d28 100644 (file)
 
 #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
+
 }
         
 }