cannam@21: cannam@21: cannam@21: VampPluginSDK: FixedTempoEstimator.cpp Source File cannam@35: cannam@21: cannam@21: cannam@35: cannam@21: cannam@21:
cannam@21:

FixedTempoEstimator.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: */
cannam@21: 00002 
cannam@21: 00003 /*
cannam@21: 00004     Vamp
cannam@21: 00005 
cannam@21: 00006     An API for audio analysis and feature extraction plugins.
cannam@21: 00007 
cannam@21: 00008     Centre for Digital Music, Queen Mary, University of London.
cannam@35: 00009     Copyright 2006-2009 Chris Cannam and QMUL.
cannam@21: 00010   
cannam@21: 00011     Permission is hereby granted, free of charge, to any person
cannam@21: 00012     obtaining a copy of this software and associated documentation
cannam@21: 00013     files (the "Software"), to deal in the Software without
cannam@21: 00014     restriction, including without limitation the rights to use, copy,
cannam@21: 00015     modify, merge, publish, distribute, sublicense, and/or sell copies
cannam@21: 00016     of the Software, and to permit persons to whom the Software is
cannam@21: 00017     furnished to do so, subject to the following conditions:
cannam@21: 00018 
cannam@21: 00019     The above copyright notice and this permission notice shall be
cannam@21: 00020     included in all copies or substantial portions of the Software.
cannam@21: 00021 
cannam@21: 00022     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
cannam@21: 00023     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
cannam@21: 00024     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
cannam@21: 00025     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
cannam@21: 00026     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
cannam@21: 00027     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
cannam@21: 00028     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
cannam@21: 00029 
cannam@21: 00030     Except as contained in this notice, the names of the Centre for
cannam@21: 00031     Digital Music; Queen Mary, University of London; and Chris Cannam
cannam@21: 00032     shall not be used in advertising or otherwise to promote the sale,
cannam@21: 00033     use or other dealings in this Software without prior written
cannam@21: 00034     authorization.
cannam@21: 00035 */
cannam@21: 00036 
cannam@21: 00037 #include "FixedTempoEstimator.h"
cannam@21: 00038 
cannam@21: 00039 using std::string;
cannam@21: 00040 using std::vector;
cannam@21: 00041 using std::cerr;
cannam@21: 00042 using std::endl;
cannam@21: 00043 
cannam@21: 00044 using Vamp::RealTime;
cannam@21: 00045 
cannam@21: 00046 #include <cmath>
cannam@21: 00047 
cannam@21: 00048 
cannam@21: 00049 class FixedTempoEstimator::D
cannam@21: 00050 // this class just avoids us having to declare any data members in the header
cannam@21: 00051 {
cannam@21: 00052 public:
cannam@35: 00053     D(float inputSampleRate);
cannam@35: 00054     ~D();
cannam@21: 00055 
cannam@35: 00056     size_t getPreferredStepSize() const { return 64; }
cannam@35: 00057     size_t getPreferredBlockSize() const { return 256; }
cannam@21: 00058 
cannam@35: 00059     ParameterList getParameterDescriptors() const;
cannam@35: 00060     float getParameter(string id) const;
cannam@35: 00061     void setParameter(string id, float value);
cannam@21: 00062 
cannam@35: 00063     OutputList getOutputDescriptors() const;
cannam@21: 00064 
cannam@35: 00065     bool initialise(size_t channels, size_t stepSize, size_t blockSize);
cannam@35: 00066     void reset();
cannam@35: 00067     FeatureSet process(const float *const *, RealTime);
cannam@35: 00068     FeatureSet getRemainingFeatures();
cannam@21: 00069 
cannam@21: 00070 private:
cannam@35: 00071     void calculate();
cannam@35: 00072     FeatureSet assembleFeatures();
cannam@21: 00073 
cannam@35: 00074     float lag2tempo(int);
cannam@35: 00075     int tempo2lag(float);
cannam@21: 00076 
cannam@35: 00077     float m_inputSampleRate;
cannam@35: 00078     size_t m_stepSize;
cannam@35: 00079     size_t m_blockSize;
cannam@21: 00080 
cannam@35: 00081     float m_minbpm;
cannam@35: 00082     float m_maxbpm;
cannam@35: 00083     float m_maxdflen;
cannam@21: 00084 
cannam@35: 00085     float *m_priorMagnitudes;
cannam@21: 00086 
cannam@35: 00087     size_t m_dfsize;
cannam@35: 00088     float *m_df;
cannam@35: 00089     float *m_r;
cannam@35: 00090     float *m_fr;
cannam@35: 00091     float *m_t;
cannam@35: 00092     size_t m_n;
cannam@21: 00093 
cannam@35: 00094     Vamp::RealTime m_start;
cannam@35: 00095     Vamp::RealTime m_lasttime;
cannam@21: 00096 };
cannam@21: 00097 
cannam@35: 00098 FixedTempoEstimator::D::D(float inputSampleRate) :
cannam@21: 00099     m_inputSampleRate(inputSampleRate),
cannam@21: 00100     m_stepSize(0),
cannam@21: 00101     m_blockSize(0),
cannam@21: 00102     m_minbpm(50),
cannam@21: 00103     m_maxbpm(190),
cannam@21: 00104     m_maxdflen(10),
cannam@21: 00105     m_priorMagnitudes(0),
cannam@21: 00106     m_df(0),
cannam@21: 00107     m_r(0),
cannam@21: 00108     m_fr(0),
cannam@21: 00109     m_t(0),
cannam@21: 00110     m_n(0)
cannam@21: 00111 {
cannam@21: 00112 }
cannam@21: 00113 
cannam@35: 00114 FixedTempoEstimator::D::~D()
cannam@21: 00115 {
cannam@35: 00116     delete[] m_priorMagnitudes;
cannam@35: 00117     delete[] m_df;
cannam@35: 00118     delete[] m_r;
cannam@35: 00119     delete[] m_fr;
cannam@35: 00120     delete[] m_t;
cannam@21: 00121 }
cannam@21: 00122 
cannam@21: 00123 FixedTempoEstimator::ParameterList
cannam@35: 00124 FixedTempoEstimator::D::getParameterDescriptors() const
cannam@21: 00125 {
cannam@21: 00126     ParameterList list;
cannam@21: 00127 
cannam@21: 00128     ParameterDescriptor d;
cannam@35: 00129     d.identifier = "minbpm";
cannam@35: 00130     d.name = "Minimum estimated tempo";
cannam@35: 00131     d.description = "Minimum beat-per-minute value which the tempo estimator is able to return";
cannam@35: 00132     d.unit = "bpm";
cannam@35: 00133     d.minValue = 10;
cannam@35: 00134     d.maxValue = 360;
cannam@35: 00135     d.defaultValue = 50;
cannam@35: 00136     d.isQuantized = false;
cannam@21: 00137     list.push_back(d);
cannam@21: 00138 
cannam@35: 00139     d.identifier = "maxbpm";
cannam@35: 00140     d.name = "Maximum estimated tempo";
cannam@35: 00141     d.description = "Maximum beat-per-minute value which the tempo estimator is able to return";
cannam@35: 00142     d.defaultValue = 190;
cannam@21: 00143     list.push_back(d);
cannam@21: 00144 
cannam@35: 00145     d.identifier = "maxdflen";
cannam@35: 00146     d.name = "Input duration to study";
cannam@35: 00147     d.description = "Length of audio input, in seconds, which should be taken into account when estimating tempo.  There is no need to supply the plugin with any further input once this time has elapsed since the start of the audio.  The tempo estimator may use only the first part of this, up to eight times the slowest beat duration: increasing this value further than that is unlikely to improve results.";
cannam@35: 00148     d.unit = "s";
cannam@35: 00149     d.minValue = 2;
cannam@35: 00150     d.maxValue = 40;
cannam@35: 00151     d.defaultValue = 10;
cannam@21: 00152     list.push_back(d);
cannam@21: 00153 
cannam@21: 00154     return list;
cannam@21: 00155 }
cannam@21: 00156 
cannam@21: 00157 float
cannam@35: 00158 FixedTempoEstimator::D::getParameter(string id) const
cannam@21: 00159 {
cannam@21: 00160     if (id == "minbpm") {
cannam@35: 00161         return m_minbpm;
cannam@21: 00162     } else if (id == "maxbpm") {
cannam@35: 00163         return m_maxbpm;
cannam@21: 00164     } else if (id == "maxdflen") {
cannam@35: 00165         return m_maxdflen;
cannam@21: 00166     }
cannam@21: 00167     return 0.f;
cannam@21: 00168 }
cannam@21: 00169 
cannam@21: 00170 void
cannam@35: 00171 FixedTempoEstimator::D::setParameter(string id, float value)
cannam@21: 00172 {
cannam@21: 00173     if (id == "minbpm") {
cannam@35: 00174         m_minbpm = value;
cannam@21: 00175     } else if (id == "maxbpm") {
cannam@35: 00176         m_maxbpm = value;
cannam@21: 00177     } else if (id == "maxdflen") {
cannam@35: 00178         m_maxdflen = value;
cannam@21: 00179     }
cannam@21: 00180 }
cannam@21: 00181 
cannam@21: 00182 static int TempoOutput = 0;
cannam@21: 00183 static int CandidatesOutput = 1;
cannam@21: 00184 static int DFOutput = 2;
cannam@21: 00185 static int ACFOutput = 3;
cannam@21: 00186 static int FilteredACFOutput = 4;
cannam@21: 00187 
cannam@21: 00188 FixedTempoEstimator::OutputList
cannam@35: 00189 FixedTempoEstimator::D::getOutputDescriptors() const
cannam@21: 00190 {
cannam@21: 00191     OutputList list;
cannam@21: 00192 
cannam@21: 00193     OutputDescriptor d;
cannam@35: 00194     d.identifier = "tempo";
cannam@35: 00195     d.name = "Tempo";
cannam@35: 00196     d.description = "Estimated tempo";
cannam@35: 00197     d.unit = "bpm";
cannam@35: 00198     d.hasFixedBinCount = true;
cannam@35: 00199     d.binCount = 1;
cannam@35: 00200     d.hasKnownExtents = false;
cannam@35: 00201     d.isQuantized = false;
cannam@35: 00202     d.sampleType = OutputDescriptor::VariableSampleRate;
cannam@35: 00203     d.sampleRate = m_inputSampleRate;
cannam@35: 00204     d.hasDuration = true; // our returned tempo spans a certain range
cannam@21: 00205     list.push_back(d);
cannam@21: 00206 
cannam@35: 00207     d.identifier = "candidates";
cannam@35: 00208     d.name = "Tempo candidates";
cannam@35: 00209     d.description = "Possible tempo estimates, one per bin with the most likely in the first bin";
cannam@35: 00210     d.unit = "bpm";
cannam@35: 00211     d.hasFixedBinCount = false;
cannam@21: 00212     list.push_back(d);
cannam@21: 00213 
cannam@35: 00214     d.identifier = "detectionfunction";
cannam@35: 00215     d.name = "Detection Function";
cannam@35: 00216     d.description = "Onset detection function";
cannam@35: 00217     d.unit = "";
cannam@35: 00218     d.hasFixedBinCount = 1;
cannam@35: 00219     d.binCount = 1;
cannam@35: 00220     d.hasKnownExtents = true;
cannam@35: 00221     d.minValue = 0.0;
cannam@35: 00222     d.maxValue = 1.0;
cannam@35: 00223     d.isQuantized = false;
cannam@35: 00224     d.quantizeStep = 0.0;
cannam@35: 00225     d.sampleType = OutputDescriptor::FixedSampleRate;
cannam@35: 00226     if (m_stepSize) {
cannam@35: 00227         d.sampleRate = m_inputSampleRate / m_stepSize;
cannam@21: 00228     } else {
cannam@35: 00229         d.sampleRate = m_inputSampleRate / (getPreferredBlockSize()/2);
cannam@21: 00230     }
cannam@35: 00231     d.hasDuration = false;
cannam@21: 00232     list.push_back(d);
cannam@21: 00233 
cannam@35: 00234     d.identifier = "acf";
cannam@35: 00235     d.name = "Autocorrelation Function";
cannam@35: 00236     d.description = "Autocorrelation of onset detection function";
cannam@35: 00237     d.hasKnownExtents = false;
cannam@35: 00238     d.unit = "r";
cannam@21: 00239     list.push_back(d);
cannam@21: 00240 
cannam@35: 00241     d.identifier = "filtered_acf";
cannam@35: 00242     d.name = "Filtered Autocorrelation";
cannam@35: 00243     d.description = "Filtered autocorrelation of onset detection function";
cannam@35: 00244     d.unit = "r";
cannam@21: 00245     list.push_back(d);
cannam@21: 00246 
cannam@21: 00247     return list;
cannam@21: 00248 }
cannam@21: 00249 
cannam@21: 00250 bool
cannam@35: 00251 FixedTempoEstimator::D::initialise(size_t, size_t stepSize, size_t blockSize)
cannam@21: 00252 {
cannam@35: 00253     m_stepSize = stepSize;
cannam@35: 00254     m_blockSize = blockSize;
cannam@21: 00255 
cannam@35: 00256     float dfLengthSecs = m_maxdflen;
cannam@35: 00257     m_dfsize = (dfLengthSecs * m_inputSampleRate) / m_stepSize;
cannam@21: 00258 
cannam@35: 00259     m_priorMagnitudes = new float[m_blockSize/2];
cannam@35: 00260     m_df = new float[m_dfsize];
cannam@21: 00261 
cannam@35: 00262     for (size_t i = 0; i < m_blockSize/2; ++i) {
cannam@35: 00263         m_priorMagnitudes[i] = 0.f;
cannam@21: 00264     }
cannam@35: 00265     for (size_t i = 0; i < m_dfsize; ++i) {
cannam@35: 00266         m_df[i] = 0.f;
cannam@21: 00267     }
cannam@21: 00268 
cannam@35: 00269     m_n = 0;
cannam@21: 00270 
cannam@21: 00271     return true;
cannam@21: 00272 }
cannam@21: 00273 
cannam@21: 00274 void
cannam@35: 00275 FixedTempoEstimator::D::reset()
cannam@21: 00276 {
cannam@35: 00277     if (!m_priorMagnitudes) return;
cannam@21: 00278 
cannam@35: 00279     for (size_t i = 0; i < m_blockSize/2; ++i) {
cannam@35: 00280         m_priorMagnitudes[i] = 0.f;
cannam@21: 00281     }
cannam@35: 00282     for (size_t i = 0; i < m_dfsize; ++i) {
cannam@35: 00283         m_df[i] = 0.f;
cannam@21: 00284     }
cannam@21: 00285 
cannam@35: 00286     delete[] m_r;
cannam@35: 00287     m_r = 0;
cannam@21: 00288 
cannam@35: 00289     delete[] m_fr; 
cannam@35: 00290     m_fr = 0;
cannam@21: 00291 
cannam@35: 00292     delete[] m_t; 
cannam@35: 00293     m_t = 0;
cannam@21: 00294 
cannam@35: 00295     m_n = 0;
cannam@21: 00296 
cannam@35: 00297     m_start = RealTime::zeroTime;
cannam@35: 00298     m_lasttime = RealTime::zeroTime;
cannam@21: 00299 }
cannam@21: 00300 
cannam@21: 00301 FixedTempoEstimator::FeatureSet
cannam@35: 00302 FixedTempoEstimator::D::process(const float *const *inputBuffers, RealTime ts)
cannam@21: 00303 {
cannam@21: 00304     FeatureSet fs;
cannam@21: 00305 
cannam@35: 00306     if (m_stepSize == 0) {
cannam@21: 00307         cerr << "ERROR: FixedTempoEstimator::process: "
cannam@21: 00308              << "FixedTempoEstimator has not been initialised"
cannam@21: 00309              << endl;
cannam@21: 00310         return fs;
cannam@21: 00311     }
cannam@21: 00312 
cannam@35: 00313     if (m_n == 0) m_start = ts;
cannam@35: 00314     m_lasttime = ts;
cannam@21: 00315 
cannam@35: 00316     if (m_n == m_dfsize) {
cannam@21: 00317         // If we have seen enough input, do the estimation and return
cannam@35: 00318         calculate();
cannam@35: 00319         fs = assembleFeatures();
cannam@35: 00320         ++m_n;
cannam@21: 00321         return fs;
cannam@21: 00322     }
cannam@21: 00323 
cannam@21: 00324     // If we have seen more than enough, just discard and return!
cannam@35: 00325     if (m_n > m_dfsize) return FeatureSet();
cannam@21: 00326 
cannam@21: 00327     float value = 0.f;
cannam@21: 00328 
cannam@21: 00329     // m_df will contain an onset detection function based on the rise
cannam@21: 00330     // in overall power from one spectral frame to the next --
cannam@21: 00331     // simplistic but reasonably effective for our purposes.
cannam@21: 00332 
cannam@35: 00333     for (size_t i = 1; i < m_blockSize/2; ++i) {
cannam@21: 00334 
cannam@21: 00335         float real = inputBuffers[0][i*2];
cannam@21: 00336         float imag = inputBuffers[0][i*2 + 1];
cannam@21: 00337 
cannam@21: 00338         float sqrmag = real * real + imag * imag;
cannam@35: 00339         value += fabsf(sqrmag - m_priorMagnitudes[i]);
cannam@21: 00340 
cannam@35: 00341         m_priorMagnitudes[i] = sqrmag;
cannam@21: 00342     }
cannam@21: 00343 
cannam@35: 00344     m_df[m_n] = value;
cannam@21: 00345 
cannam@35: 00346     ++m_n;
cannam@21: 00347     return fs;
cannam@21: 00348 }    
cannam@21: 00349 
cannam@21: 00350 FixedTempoEstimator::FeatureSet
cannam@35: 00351 FixedTempoEstimator::D::getRemainingFeatures()
cannam@21: 00352 {
cannam@21: 00353     FeatureSet fs;
cannam@35: 00354     if (m_n > m_dfsize) return fs;
cannam@35: 00355     calculate();
cannam@35: 00356     fs = assembleFeatures();
cannam@35: 00357     ++m_n;
cannam@21: 00358     return fs;
cannam@21: 00359 }
cannam@21: 00360 
cannam@21: 00361 float
cannam@35: 00362 FixedTempoEstimator::D::lag2tempo(int lag)
cannam@21: 00363 {
cannam@35: 00364     return 60.f / ((lag * m_stepSize) / m_inputSampleRate);
cannam@21: 00365 }
cannam@21: 00366 
cannam@21: 00367 int
cannam@35: 00368 FixedTempoEstimator::D::tempo2lag(float tempo)
cannam@21: 00369 {
cannam@35: 00370     return ((60.f / tempo) * m_inputSampleRate) / m_stepSize;
cannam@21: 00371 }
cannam@21: 00372 
cannam@21: 00373 void
cannam@35: 00374 FixedTempoEstimator::D::calculate()
cannam@21: 00375 {    
cannam@35: 00376     if (m_r) {
cannam@21: 00377         cerr << "FixedTempoEstimator::calculate: calculation already happened?" << endl;
cannam@21: 00378         return;
cannam@21: 00379     }
cannam@21: 00380 
cannam@35: 00381     if (m_n < m_dfsize / 9 &&
cannam@35: 00382         m_n < (1.0 * m_inputSampleRate) / m_stepSize) { // 1 second
cannam@21: 00383         cerr << "FixedTempoEstimator::calculate: Input is too short" << endl;
cannam@21: 00384         return;
cannam@21: 00385     }
cannam@21: 00386 
cannam@21: 00387     // This function takes m_df (the detection function array filled
cannam@21: 00388     // out in process()) and calculates m_r (the raw autocorrelation)
cannam@21: 00389     // and m_fr (the filtered autocorrelation from whose peaks tempo
cannam@21: 00390     // estimates will be taken).
cannam@21: 00391 
cannam@35: 00392     int n = m_n; // length of actual df array (m_dfsize is the theoretical max)
cannam@21: 00393 
cannam@35: 00394     m_r  = new float[n/2]; // raw autocorrelation
cannam@35: 00395     m_fr = new float[n/2]; // filtered autocorrelation
cannam@35: 00396     m_t  = new float[n/2]; // averaged tempo estimate for each lag value
cannam@21: 00397 
cannam@21: 00398     for (int i = 0; i < n/2; ++i) {
cannam@35: 00399         m_r[i]  = 0.f;
cannam@35: 00400         m_fr[i] = 0.f;
cannam@35: 00401         m_t[i]  = lag2tempo(i);
cannam@21: 00402     }
cannam@21: 00403 
cannam@21: 00404     // Calculate the raw autocorrelation of the detection function
cannam@21: 00405 
cannam@21: 00406     for (int i = 0; i < n/2; ++i) {
cannam@21: 00407 
cannam@21: 00408         for (int j = i; j < n; ++j) {
cannam@35: 00409             m_r[i] += m_df[j] * m_df[j - i];
cannam@21: 00410         }
cannam@21: 00411 
cannam@35: 00412         m_r[i] /= n - i - 1;
cannam@21: 00413     }
cannam@21: 00414 
cannam@21: 00415     // Filter the autocorrelation and average out the tempo estimates
cannam@21: 00416     
cannam@21: 00417     float related[] = { 0.5, 2, 4, 8 };
cannam@21: 00418 
cannam@21: 00419     for (int i = 1; i < n/2-1; ++i) {
cannam@21: 00420 
cannam@35: 00421         m_fr[i] = m_r[i];
cannam@21: 00422 
cannam@21: 00423         int div = 1;
cannam@21: 00424 
cannam@21: 00425         for (int j = 0; j < int(sizeof(related)/sizeof(related[0])); ++j) {
cannam@21: 00426 
cannam@21: 00427             // Check for an obvious peak at each metrically related lag
cannam@21: 00428 
cannam@21: 00429             int k0 = int(i * related[j] + 0.5);
cannam@21: 00430 
cannam@21: 00431             if (k0 >= 0 && k0 < int(n/2)) {
cannam@21: 00432 
cannam@21: 00433                 int kmax = 0, kmin = 0;
cannam@21: 00434                 float kvmax = 0, kvmin = 0;
cannam@21: 00435                 bool have = false;
cannam@21: 00436 
cannam@21: 00437                 for (int k = k0 - 1; k <= k0 + 1; ++k) {
cannam@21: 00438 
cannam@21: 00439                     if (k < 0 || k >= n/2) continue;
cannam@21: 00440 
cannam@35: 00441                     if (!have || (m_r[k] > kvmax)) { kmax = k; kvmax = m_r[k]; }
cannam@35: 00442                     if (!have || (m_r[k] < kvmin)) { kmin = k; kvmin = m_r[k]; }
cannam@21: 00443                     
cannam@21: 00444                     have = true;
cannam@21: 00445                 }
cannam@21: 00446                 
cannam@21: 00447                 // Boost the original lag according to the strongest
cannam@21: 00448                 // value found close to this related lag
cannam@21: 00449 
cannam@35: 00450                 m_fr[i] += m_r[kmax] / 5;
cannam@21: 00451 
cannam@35: 00452                 if ((kmax == 0 || m_r[kmax] > m_r[kmax-1]) &&
cannam@35: 00453                     (kmax == n/2-1 || m_r[kmax] > m_r[kmax+1]) &&
cannam@21: 00454                     kvmax > kvmin * 1.05) {
cannam@21: 00455 
cannam@21: 00456                     // The strongest value close to the related lag is
cannam@21: 00457                     // also a pretty good looking peak, so use it to
cannam@21: 00458                     // improve our tempo estimate for the original lag
cannam@21: 00459                     
cannam@35: 00460                     m_t[i] = m_t[i] + lag2tempo(kmax) * related[j];
cannam@21: 00461                     ++div;
cannam@21: 00462                 }
cannam@21: 00463             }
cannam@21: 00464         }
cannam@21: 00465         
cannam@35: 00466         m_t[i] /= div;
cannam@21: 00467         
cannam@21: 00468         // Finally apply a primitive perceptual weighting (to prefer
cannam@21: 00469         // tempi of around 120-130)
cannam@21: 00470 
cannam@35: 00471         float weight = 1.f - fabsf(128.f - lag2tempo(i)) * 0.005;
cannam@21: 00472         if (weight < 0.f) weight = 0.f;
cannam@21: 00473         weight = weight * weight * weight;
cannam@21: 00474 
cannam@35: 00475         m_fr[i] += m_fr[i] * (weight / 3);
cannam@21: 00476     }
cannam@21: 00477 }
cannam@21: 00478     
cannam@21: 00479 FixedTempoEstimator::FeatureSet
cannam@35: 00480 FixedTempoEstimator::D::assembleFeatures()
cannam@21: 00481 {
cannam@21: 00482     FeatureSet fs;
cannam@35: 00483     if (!m_r) return fs; // No autocorrelation: no results
cannam@21: 00484 
cannam@21: 00485     Feature feature;
cannam@35: 00486     feature.hasTimestamp = true;
cannam@35: 00487     feature.hasDuration = false;
cannam@35: 00488     feature.label = "";
cannam@35: 00489     feature.values.clear();
cannam@35: 00490     feature.values.push_back(0.f);
cannam@21: 00491 
cannam@21: 00492     char buffer[40];
cannam@21: 00493 
cannam@35: 00494     int n = m_n;
cannam@21: 00495 
cannam@21: 00496     for (int i = 0; i < n; ++i) {
cannam@21: 00497 
cannam@21: 00498         // Return the detection function in the DF output
cannam@21: 00499 
cannam@35: 00500         feature.timestamp = m_start +
cannam@35: 00501             RealTime::frame2RealTime(i * m_stepSize, m_inputSampleRate);
cannam@35: 00502         feature.values[0] = m_df[i];
cannam@35: 00503         feature.label = "";
cannam@21: 00504         fs[DFOutput].push_back(feature);
cannam@21: 00505     }
cannam@21: 00506 
cannam@21: 00507     for (int i = 1; i < n/2; ++i) {
cannam@21: 00508 
cannam@21: 00509         // Return the raw autocorrelation in the ACF output, each
cannam@21: 00510         // value labelled according to its corresponding tempo
cannam@21: 00511 
cannam@35: 00512         feature.timestamp = m_start +
cannam@35: 00513             RealTime::frame2RealTime(i * m_stepSize, m_inputSampleRate);
cannam@35: 00514         feature.values[0] = m_r[i];
cannam@35: 00515         sprintf(buffer, "%.1f bpm", lag2tempo(i));
cannam@35: 00516         if (i == n/2-1) feature.label = "";
cannam@35: 00517         else feature.label = buffer;
cannam@21: 00518         fs[ACFOutput].push_back(feature);
cannam@21: 00519     }
cannam@21: 00520 
cannam@35: 00521     float t0 = m_minbpm; // our minimum detected tempo
cannam@35: 00522     float t1 = m_maxbpm; // our maximum detected tempo
cannam@21: 00523 
cannam@35: 00524     int p0 = tempo2lag(t1);
cannam@35: 00525     int p1 = tempo2lag(t0);
cannam@21: 00526 
cannam@21: 00527     std::map<float, int> candidates;
cannam@21: 00528 
cannam@21: 00529     for (int i = p0; i <= p1 && i+1 < n/2; ++i) {
cannam@21: 00530 
cannam@35: 00531         if (m_fr[i] > m_fr[i-1] &&
cannam@35: 00532             m_fr[i] > m_fr[i+1]) {
cannam@21: 00533 
cannam@21: 00534             // This is a peak in the filtered autocorrelation: stick
cannam@21: 00535             // it into the map from filtered autocorrelation to lag
cannam@21: 00536             // index -- this sorts our peaks by filtered acf value
cannam@21: 00537 
cannam@35: 00538             candidates[m_fr[i]] = i;
cannam@21: 00539         }
cannam@21: 00540 
cannam@21: 00541         // Also return the filtered autocorrelation in its own output
cannam@21: 00542 
cannam@35: 00543         feature.timestamp = m_start +
cannam@35: 00544             RealTime::frame2RealTime(i * m_stepSize, m_inputSampleRate);
cannam@35: 00545         feature.values[0] = m_fr[i];
cannam@35: 00546         sprintf(buffer, "%.1f bpm", lag2tempo(i));
cannam@35: 00547         if (i == p1 || i == n/2-2) feature.label = "";
cannam@35: 00548         else feature.label = buffer;
cannam@21: 00549         fs[FilteredACFOutput].push_back(feature);
cannam@21: 00550     }
cannam@21: 00551 
cannam@21: 00552     if (candidates.empty()) {
cannam@21: 00553         cerr << "No tempo candidates!" << endl;
cannam@21: 00554         return fs;
cannam@21: 00555     }
cannam@21: 00556 
cannam@35: 00557     feature.hasTimestamp = true;
cannam@35: 00558     feature.timestamp = m_start;
cannam@21: 00559     
cannam@35: 00560     feature.hasDuration = true;
cannam@35: 00561     feature.duration = m_lasttime - m_start;
cannam@21: 00562 
cannam@21: 00563     // The map contains only peaks and is sorted by filtered acf
cannam@21: 00564     // value, so the final element in it is our "best" tempo guess
cannam@21: 00565 
cannam@21: 00566     std::map<float, int>::const_iterator ci = candidates.end();
cannam@21: 00567     --ci;
cannam@21: 00568     int maxpi = ci->second;
cannam@21: 00569 
cannam@35: 00570     if (m_t[maxpi] > 0) {
cannam@21: 00571 
cannam@21: 00572         // This lag has an adjusted tempo from the averaging process:
cannam@21: 00573         // use it
cannam@21: 00574 
cannam@35: 00575         feature.values[0] = m_t[maxpi];
cannam@21: 00576 
cannam@21: 00577     } else {
cannam@21: 00578 
cannam@21: 00579         // shouldn't happen -- it would imply that this high value was
cannam@21: 00580         // not a peak!
cannam@21: 00581 
cannam@35: 00582         feature.values[0] = lag2tempo(maxpi);
cannam@21: 00583         cerr << "WARNING: No stored tempo for index " << maxpi << endl;
cannam@21: 00584     }
cannam@21: 00585 
cannam@35: 00586     sprintf(buffer, "%.1f bpm", feature.values[0]);
cannam@35: 00587     feature.label = buffer;
cannam@21: 00588 
cannam@21: 00589     // Return the best tempo in the main output
cannam@21: 00590 
cannam@21: 00591     fs[TempoOutput].push_back(feature);
cannam@21: 00592 
cannam@21: 00593     // And return the other estimates (up to the arbitrarily chosen
cannam@21: 00594     // number of 10 of them) in the candidates output
cannam@21: 00595 
cannam@35: 00596     feature.values.clear();
cannam@35: 00597     feature.label = "";
cannam@21: 00598 
cannam@35: 00599     while (feature.values.size() < 10) {
cannam@35: 00600         if (m_t[ci->second] > 0) {
cannam@35: 00601             feature.values.push_back(m_t[ci->second]);
cannam@21: 00602         } else {
cannam@35: 00603             feature.values.push_back(lag2tempo(ci->second));
cannam@21: 00604         }
cannam@21: 00605         if (ci == candidates.begin()) break;
cannam@21: 00606         --ci;
cannam@21: 00607     }
cannam@21: 00608 
cannam@21: 00609     fs[CandidatesOutput].push_back(feature);
cannam@21: 00610     
cannam@21: 00611     return fs;
cannam@21: 00612 }
cannam@21: 00613 
cannam@21: 00614     
cannam@21: 00615 
cannam@21: 00616 FixedTempoEstimator::FixedTempoEstimator(float inputSampleRate) :
cannam@21: 00617     Plugin(inputSampleRate),
cannam@21: 00618     m_d(new D(inputSampleRate))
cannam@21: 00619 {
cannam@21: 00620 }
cannam@21: 00621 
cannam@21: 00622 FixedTempoEstimator::~FixedTempoEstimator()
cannam@21: 00623 {
cannam@21: 00624     delete m_d;
cannam@21: 00625 }
cannam@21: 00626 
cannam@21: 00627 string
cannam@21: 00628 FixedTempoEstimator::getIdentifier() const
cannam@21: 00629 {
cannam@21: 00630     return "fixedtempo";
cannam@21: 00631 }
cannam@21: 00632 
cannam@21: 00633 string
cannam@21: 00634 FixedTempoEstimator::getName() const
cannam@21: 00635 {
cannam@21: 00636     return "Simple Fixed Tempo Estimator";
cannam@21: 00637 }
cannam@21: 00638 
cannam@21: 00639 string
cannam@21: 00640 FixedTempoEstimator::getDescription() const
cannam@21: 00641 {
cannam@21: 00642     return "Study a short section of audio and estimate its tempo, assuming the tempo is constant";
cannam@21: 00643 }
cannam@21: 00644 
cannam@21: 00645 string
cannam@21: 00646 FixedTempoEstimator::getMaker() const
cannam@21: 00647 {
cannam@21: 00648     return "Vamp SDK Example Plugins";
cannam@21: 00649 }
cannam@21: 00650 
cannam@21: 00651 int
cannam@21: 00652 FixedTempoEstimator::getPluginVersion() const
cannam@21: 00653 {
cannam@21: 00654     return 1;
cannam@21: 00655 }
cannam@21: 00656 
cannam@21: 00657 string
cannam@21: 00658 FixedTempoEstimator::getCopyright() const
cannam@21: 00659 {
cannam@21: 00660     return "Code copyright 2008 Queen Mary, University of London.  Freely redistributable (BSD license)";
cannam@21: 00661 }
cannam@21: 00662 
cannam@21: 00663 size_t
cannam@21: 00664 FixedTempoEstimator::getPreferredStepSize() const
cannam@21: 00665 {
cannam@35: 00666     return m_d->getPreferredStepSize();
cannam@21: 00667 }
cannam@21: 00668 
cannam@21: 00669 size_t
cannam@21: 00670 FixedTempoEstimator::getPreferredBlockSize() const
cannam@21: 00671 {
cannam@35: 00672     return m_d->getPreferredBlockSize();
cannam@21: 00673 }
cannam@21: 00674 
cannam@21: 00675 bool
cannam@21: 00676 FixedTempoEstimator::initialise(size_t channels, size_t stepSize, size_t blockSize)
cannam@21: 00677 {
cannam@21: 00678     if (channels < getMinChannelCount() ||
cannam@21: 00679         channels > getMaxChannelCount()) return false;
cannam@21: 00680 
cannam@35: 00681     return m_d->initialise(channels, stepSize, blockSize);
cannam@21: 00682 }
cannam@21: 00683 
cannam@21: 00684 void
cannam@21: 00685 FixedTempoEstimator::reset()
cannam@21: 00686 {
cannam@35: 00687     return m_d->reset();
cannam@21: 00688 }
cannam@21: 00689 
cannam@21: 00690 FixedTempoEstimator::ParameterList
cannam@21: 00691 FixedTempoEstimator::getParameterDescriptors() const
cannam@21: 00692 {
cannam@35: 00693     return m_d->getParameterDescriptors();
cannam@21: 00694 }
cannam@21: 00695 
cannam@21: 00696 float
cannam@21: 00697 FixedTempoEstimator::getParameter(std::string id) const
cannam@21: 00698 {
cannam@35: 00699     return m_d->getParameter(id);
cannam@21: 00700 }
cannam@21: 00701 
cannam@21: 00702 void
cannam@21: 00703 FixedTempoEstimator::setParameter(std::string id, float value)
cannam@21: 00704 {
cannam@35: 00705     m_d->setParameter(id, value);
cannam@21: 00706 }
cannam@21: 00707 
cannam@21: 00708 FixedTempoEstimator::OutputList
cannam@21: 00709 FixedTempoEstimator::getOutputDescriptors() const
cannam@21: 00710 {
cannam@35: 00711     return m_d->getOutputDescriptors();
cannam@21: 00712 }
cannam@21: 00713 
cannam@21: 00714 FixedTempoEstimator::FeatureSet
cannam@21: 00715 FixedTempoEstimator::process(const float *const *inputBuffers, RealTime ts)
cannam@21: 00716 {
cannam@35: 00717     return m_d->process(inputBuffers, ts);
cannam@21: 00718 }
cannam@21: 00719 
cannam@21: 00720 FixedTempoEstimator::FeatureSet
cannam@21: 00721 FixedTempoEstimator::getRemainingFeatures()
cannam@21: 00722 {
cannam@35: 00723     return m_d->getRemainingFeatures();
cannam@21: 00724 }
cannam@21: 
cannam@35:
Generated on Thu Sep 24 13:40:13 2009 for VampPluginSDK by  cannam@21: cannam@35: doxygen 1.5.8
cannam@21: cannam@21: