PluginBufferingAdapter.cpp

Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Vamp
00005 
00006     An API for audio analysis and feature extraction plugins.
00007 
00008     Centre for Digital Music, Queen Mary, University of London.
00009     Copyright 2006-2007 Chris Cannam and QMUL.
00010     This file by Mark Levy and Chris Cannam.
00011   
00012     Permission is hereby granted, free of charge, to any person
00013     obtaining a copy of this software and associated documentation
00014     files (the "Software"), to deal in the Software without
00015     restriction, including without limitation the rights to use, copy,
00016     modify, merge, publish, distribute, sublicense, and/or sell copies
00017     of the Software, and to permit persons to whom the Software is
00018     furnished to do so, subject to the following conditions:
00019 
00020     The above copyright notice and this permission notice shall be
00021     included in all copies or substantial portions of the Software.
00022 
00023     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00024     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00025     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00026     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
00027     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
00028     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00029     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00030 
00031     Except as contained in this notice, the names of the Centre for
00032     Digital Music; Queen Mary, University of London; and Chris Cannam
00033     shall not be used in advertising or otherwise to promote the sale,
00034     use or other dealings in this Software without prior written
00035     authorization.
00036 */
00037 
00038 #include <vector>
00039 #include <map>
00040 
00041 #include "PluginBufferingAdapter.h"
00042 
00043 using std::vector;
00044 using std::map;
00045 
00046 namespace Vamp {
00047         
00048 namespace HostExt {
00049                 
00050 class PluginBufferingAdapter::Impl
00051 {
00052 public:
00053     Impl(Plugin *plugin, float inputSampleRate);
00054     ~Impl();
00055                 
00056     bool initialise(size_t channels, size_t stepSize, size_t blockSize);
00057 
00058     OutputList getOutputDescriptors() const;
00059 
00060     void reset();
00061 
00062     FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
00063                 
00064     FeatureSet getRemainingFeatures();
00065                 
00066 protected:
00067     class RingBuffer
00068     {
00069     public:
00070         RingBuffer(int n) :
00071             m_buffer(new float[n+1]), m_writer(0), m_reader(0), m_size(n+1) { }
00072         virtual ~RingBuffer() { delete[] m_buffer; }
00073 
00074         int getSize() const { return m_size-1; }
00075         void reset() { m_writer = 0; m_reader = 0; }
00076 
00077         int getReadSpace() const {
00078             int writer = m_writer, reader = m_reader, space;
00079             if (writer > reader) space = writer - reader;
00080             else if (writer < reader) space = (writer + m_size) - reader;
00081             else space = 0;
00082             return space;
00083         }
00084 
00085         int getWriteSpace() const {
00086             int writer = m_writer;
00087             int reader = m_reader;
00088             int space = (reader + m_size - writer - 1);
00089             if (space >= m_size) space -= m_size;
00090             return space;
00091         }
00092         
00093         int peek(float *destination, int n) const {
00094 
00095             int available = getReadSpace();
00096 
00097             if (n > available) {
00098                 for (int i = available; i < n; ++i) {
00099                     destination[i] = 0.f;
00100                 }
00101                 n = available;
00102             }
00103             if (n == 0) return n;
00104 
00105             int reader = m_reader;
00106             int here = m_size - reader;
00107             const float *const bufbase = m_buffer + reader;
00108 
00109             if (here >= n) {
00110                 for (int i = 0; i < n; ++i) {
00111                     destination[i] = bufbase[i];
00112                 }
00113             } else {
00114                 for (int i = 0; i < here; ++i) {
00115                     destination[i] = bufbase[i];
00116                 }
00117                 float *const destbase = destination + here;
00118                 const int nh = n - here;
00119                 for (int i = 0; i < nh; ++i) {
00120                     destbase[i] = m_buffer[i];
00121                 }
00122             }
00123 
00124             return n;
00125         }
00126 
00127         int skip(int n) {
00128             
00129             int available = getReadSpace();
00130             if (n > available) {
00131                 n = available;
00132             }
00133             if (n == 0) return n;
00134 
00135             int reader = m_reader;
00136             reader += n;
00137             while (reader >= m_size) reader -= m_size;
00138             m_reader = reader;
00139             return n;
00140         }
00141         
00142         int write(const float *source, int n) {
00143 
00144             int available = getWriteSpace();
00145             if (n > available) {
00146                 n = available;
00147             }
00148             if (n == 0) return n;
00149 
00150             int writer = m_writer;
00151             int here = m_size - writer;
00152             float *const bufbase = m_buffer + writer;
00153             
00154             if (here >= n) {
00155                 for (int i = 0; i < n; ++i) {
00156                     bufbase[i] = source[i];
00157                 }
00158             } else {
00159                 for (int i = 0; i < here; ++i) {
00160                     bufbase[i] = source[i];
00161                 }
00162                 const int nh = n - here;
00163                 const float *const srcbase = source + here;
00164                 float *const buf = m_buffer;
00165                 for (int i = 0; i < nh; ++i) {
00166                     buf[i] = srcbase[i];
00167                 }
00168             }
00169 
00170             writer += n;
00171             while (writer >= m_size) writer -= m_size;
00172             m_writer = writer;
00173 
00174             return n;
00175         }
00176 
00177         int zero(int n) {
00178             
00179             int available = getWriteSpace();
00180             if (n > available) {
00181                 n = available;
00182             }
00183             if (n == 0) return n;
00184 
00185             int writer = m_writer;
00186             int here = m_size - writer;
00187             float *const bufbase = m_buffer + writer;
00188 
00189             if (here >= n) {
00190                 for (int i = 0; i < n; ++i) {
00191                     bufbase[i] = 0.f;
00192                 }
00193             } else {
00194                 for (int i = 0; i < here; ++i) {
00195                     bufbase[i] = 0.f;
00196                 }
00197                 const int nh = n - here;
00198                 for (int i = 0; i < nh; ++i) {
00199                     m_buffer[i] = 0.f;
00200                 }
00201             }
00202             
00203             writer += n;
00204             while (writer >= m_size) writer -= m_size;
00205             m_writer = writer;
00206 
00207             return n;
00208         }
00209 
00210     protected:
00211         float *m_buffer;
00212         int    m_writer;
00213         int    m_reader;
00214         int    m_size;
00215 
00216     private:
00217         RingBuffer(const RingBuffer &); // not provided
00218         RingBuffer &operator=(const RingBuffer &); // not provided
00219     };
00220 
00221     Plugin *m_plugin;
00222     size_t m_inputStepSize;
00223     size_t m_inputBlockSize;
00224     size_t m_stepSize;
00225     size_t m_blockSize;
00226     size_t m_channels;
00227     vector<RingBuffer *> m_queue;
00228     float **m_buffers;
00229     float m_inputSampleRate;
00230     RealTime m_timestamp;
00231     bool m_unrun;
00232     OutputList m_outputs;
00233                 
00234     void processBlock(FeatureSet& allFeatureSets, RealTime timestamp);
00235 };
00236                 
00237 PluginBufferingAdapter::PluginBufferingAdapter(Plugin *plugin) :
00238     PluginWrapper(plugin)
00239 {
00240     m_impl = new Impl(plugin, m_inputSampleRate);
00241 }
00242                 
00243 PluginBufferingAdapter::~PluginBufferingAdapter()
00244 {
00245     delete m_impl;
00246 }
00247                 
00248 bool
00249 PluginBufferingAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
00250 {
00251     return m_impl->initialise(channels, stepSize, blockSize);
00252 }
00253 
00254 PluginBufferingAdapter::OutputList
00255 PluginBufferingAdapter::getOutputDescriptors() const
00256 {
00257     return m_impl->getOutputDescriptors();
00258 }
00259 
00260 void
00261 PluginBufferingAdapter::reset()
00262 {
00263     m_impl->reset();
00264 }
00265                 
00266 PluginBufferingAdapter::FeatureSet
00267 PluginBufferingAdapter::process(const float *const *inputBuffers,
00268                                 RealTime timestamp)
00269 {
00270     return m_impl->process(inputBuffers, timestamp);
00271 }
00272                 
00273 PluginBufferingAdapter::FeatureSet
00274 PluginBufferingAdapter::getRemainingFeatures()
00275 {
00276     return m_impl->getRemainingFeatures();
00277 }
00278                 
00279 PluginBufferingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
00280     m_plugin(plugin),
00281     m_inputStepSize(0),
00282     m_inputBlockSize(0),
00283     m_stepSize(0),
00284     m_blockSize(0),
00285     m_channels(0), 
00286     m_queue(0),
00287     m_buffers(0),
00288     m_inputSampleRate(inputSampleRate),
00289     m_timestamp(RealTime::zeroTime),
00290     m_unrun(true)
00291 {
00292     m_outputs = plugin->getOutputDescriptors();
00293 }
00294                 
00295 PluginBufferingAdapter::Impl::~Impl()
00296 {
00297     // the adapter will delete the plugin
00298 
00299     for (size_t i = 0; i < m_channels; ++i) {
00300         delete m_queue[i];
00301         delete[] m_buffers[i];
00302     }
00303     delete[] m_buffers;
00304 }
00305 
00306 size_t
00307 PluginBufferingAdapter::getPreferredStepSize() const
00308 {
00309     return getPreferredBlockSize();
00310 }
00311                 
00312 bool
00313 PluginBufferingAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
00314 {
00315     if (stepSize != blockSize) {
00316         std::cerr << "PluginBufferingAdapter::initialise: input stepSize must be equal to blockSize for this adapter (stepSize = " << stepSize << ", blockSize = " << blockSize << ")" << std::endl;
00317         return false;
00318     }
00319 
00320     m_channels = channels;      
00321     m_inputStepSize = stepSize;
00322     m_inputBlockSize = blockSize;
00323     
00324     // use the step and block sizes which the plugin prefers
00325     m_stepSize = m_plugin->getPreferredStepSize();
00326     m_blockSize = m_plugin->getPreferredBlockSize();
00327     
00328     // or sensible defaults if it has no preference
00329     if (m_blockSize == 0) {
00330         m_blockSize = 1024;
00331     }
00332     if (m_stepSize == 0) {
00333         if (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
00334             m_stepSize = m_blockSize/2;
00335         } else {
00336             m_stepSize = m_blockSize;
00337         }
00338     } else if (m_stepSize > m_blockSize) {
00339         if (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
00340             m_blockSize = m_stepSize * 2;
00341         } else {
00342             m_blockSize = m_stepSize;
00343         }
00344     }
00345     
00346     std::cerr << "PluginBufferingAdapter::initialise: stepSize " << m_inputStepSize << " -> " << m_stepSize 
00347               << ", blockSize " << m_inputBlockSize << " -> " << m_blockSize << std::endl;                      
00348     
00349     // current implementation breaks if step is greater than block
00350     if (m_stepSize > m_blockSize) {
00351         std::cerr << "PluginBufferingAdapter::initialise: plugin's preferred stepSize greater than blockSize, giving up!" << std::endl;
00352         return false;
00353     }
00354 
00355     m_buffers = new float *[m_channels];
00356 
00357     for (size_t i = 0; i < m_channels; ++i) {
00358         m_queue.push_back(new RingBuffer(m_blockSize + m_inputBlockSize));
00359         m_buffers[i] = new float[m_blockSize];
00360     }
00361     
00362     return m_plugin->initialise(m_channels, m_stepSize, m_blockSize);
00363 }
00364                 
00365 PluginBufferingAdapter::OutputList
00366 PluginBufferingAdapter::Impl::getOutputDescriptors() const
00367 {
00368     OutputList outs = m_plugin->getOutputDescriptors();
00369     for (size_t i = 0; i < outs.size(); ++i) {
00370         if (outs[i].sampleType == OutputDescriptor::OneSamplePerStep) {
00371             outs[i].sampleRate = 1.f / m_stepSize;
00372         }
00373         outs[i].sampleType = OutputDescriptor::VariableSampleRate;
00374     }
00375     return outs;
00376 }
00377 
00378 void
00379 PluginBufferingAdapter::Impl::reset()
00380 {
00381     m_timestamp = RealTime::zeroTime;
00382     m_unrun = true;
00383 
00384     for (size_t i = 0; i < m_queue.size(); ++i) {
00385         m_queue[i]->reset();
00386     }
00387 }
00388 
00389 PluginBufferingAdapter::FeatureSet
00390 PluginBufferingAdapter::Impl::process(const float *const *inputBuffers,
00391                                       RealTime timestamp)
00392 {
00393     FeatureSet allFeatureSets;
00394 
00395     if (m_unrun) {
00396         m_timestamp = timestamp;
00397         m_unrun = false;
00398     }
00399                         
00400     // queue the new input
00401     
00402     for (size_t i = 0; i < m_channels; ++i) {
00403         int written = m_queue[i]->write(inputBuffers[i], m_inputBlockSize);
00404         if (written < int(m_inputBlockSize) && i == 0) {
00405             std::cerr << "WARNING: PluginBufferingAdapter::Impl::process: "
00406                       << "Buffer overflow: wrote " << written 
00407                       << " of " << m_inputBlockSize 
00408                       << " input samples (for plugin step size "
00409                       << m_stepSize << ", block size " << m_blockSize << ")"
00410                       << std::endl;
00411         }
00412     }    
00413     
00414     // process as much as we can
00415 
00416     while (m_queue[0]->getReadSpace() >= int(m_blockSize)) {
00417         processBlock(allFeatureSets, timestamp);
00418     }   
00419     
00420     return allFeatureSets;
00421 }
00422     
00423 PluginBufferingAdapter::FeatureSet
00424 PluginBufferingAdapter::Impl::getRemainingFeatures() 
00425 {
00426     FeatureSet allFeatureSets;
00427     
00428     // process remaining samples in queue
00429     while (m_queue[0]->getReadSpace() >= int(m_blockSize)) {
00430         processBlock(allFeatureSets, m_timestamp);
00431     }
00432     
00433     // pad any last samples remaining and process
00434     if (m_queue[0]->getReadSpace() > 0) {
00435         for (size_t i = 0; i < m_channels; ++i) {
00436             m_queue[i]->zero(m_blockSize - m_queue[i]->getReadSpace());
00437         }
00438         processBlock(allFeatureSets, m_timestamp);
00439     }                   
00440     
00441     // get remaining features                   
00442 
00443     FeatureSet featureSet = m_plugin->getRemainingFeatures();
00444 
00445     for (map<int, FeatureList>::iterator iter = featureSet.begin();
00446          iter != featureSet.end(); ++iter) {
00447         FeatureList featureList = iter->second;
00448         for (size_t i = 0; i < featureList.size(); ++i) {
00449             allFeatureSets[iter->first].push_back(featureList[i]);
00450         }
00451     }
00452     
00453     return allFeatureSets;
00454 }
00455     
00456 void
00457 PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets,
00458                                            RealTime timestamp)
00459 {
00460     for (size_t i = 0; i < m_channels; ++i) {
00461         m_queue[i]->peek(m_buffers[i], m_blockSize);
00462     }
00463 
00464     FeatureSet featureSet = m_plugin->process(m_buffers, m_timestamp);
00465     
00466     for (map<int, FeatureList>::iterator iter = featureSet.begin();
00467          iter != featureSet.end(); ++iter) {
00468         
00469         FeatureList featureList = iter->second;
00470         int outputNo = iter->first;
00471         
00472         for (size_t i = 0; i < featureList.size(); ++i) {
00473             
00474             // make sure the timestamp is set
00475             switch (m_outputs[outputNo].sampleType) {
00476 
00477             case OutputDescriptor::OneSamplePerStep:
00478                 // use our internal timestamp - OK????
00479                 featureList[i].timestamp = m_timestamp;
00480                 break;
00481 
00482             case OutputDescriptor::FixedSampleRate:
00483                 // use our internal timestamp
00484                 featureList[i].timestamp = m_timestamp;
00485                 break;
00486 
00487             case OutputDescriptor::VariableSampleRate:
00488                 break;          // plugin must set timestamp
00489 
00490             default:
00491                 break;
00492             }
00493             
00494             allFeatureSets[outputNo].push_back(featureList[i]);         
00495         }
00496     }
00497     
00498     // step forward
00499 
00500     for (size_t i = 0; i < m_channels; ++i) {
00501         m_queue[i]->skip(m_stepSize);
00502     }
00503     
00504     // fake up the timestamp each time we step forward
00505 
00506     long frame = RealTime::realTime2Frame(m_timestamp,
00507                                           int(m_inputSampleRate + 0.5));
00508     m_timestamp = RealTime::frame2RealTime(frame + m_stepSize,
00509                                            int(m_inputSampleRate + 0.5));
00510 }
00511 
00512 }
00513         
00514 }
00515 
00516 

Generated on Thu Jun 19 13:34:02 2008 for VampPluginSDK by  doxygen 1.5.5