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