annotate examples/FixedTempoEstimator.cpp @ 288:283e15f6e548

* The beginnings of making the shift-timestamp or shift-data behaviour of PluginInputDomainAdapter into an option
author cannam
date Tue, 15 Sep 2009 16:24:53 +0000
parents 23352e424631
children c97e70ed5abc
rev   line source
cannam@198 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
cannam@198 2
cannam@198 3 /*
cannam@198 4 Vamp
cannam@198 5
cannam@198 6 An API for audio analysis and feature extraction plugins.
cannam@198 7
cannam@198 8 Centre for Digital Music, Queen Mary, University of London.
cannam@198 9 Copyright 2006-2008 Chris Cannam and QMUL.
cannam@198 10
cannam@198 11 Permission is hereby granted, free of charge, to any person
cannam@198 12 obtaining a copy of this software and associated documentation
cannam@198 13 files (the "Software"), to deal in the Software without
cannam@198 14 restriction, including without limitation the rights to use, copy,
cannam@198 15 modify, merge, publish, distribute, sublicense, and/or sell copies
cannam@198 16 of the Software, and to permit persons to whom the Software is
cannam@198 17 furnished to do so, subject to the following conditions:
cannam@198 18
cannam@198 19 The above copyright notice and this permission notice shall be
cannam@198 20 included in all copies or substantial portions of the Software.
cannam@198 21
cannam@198 22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
cannam@198 23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
cannam@198 24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
cannam@198 25 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
cannam@198 26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
cannam@198 27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
cannam@198 28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
cannam@198 29
cannam@198 30 Except as contained in this notice, the names of the Centre for
cannam@198 31 Digital Music; Queen Mary, University of London; and Chris Cannam
cannam@198 32 shall not be used in advertising or otherwise to promote the sale,
cannam@198 33 use or other dealings in this Software without prior written
cannam@198 34 authorization.
cannam@198 35 */
cannam@198 36
cannam@198 37 #include "FixedTempoEstimator.h"
cannam@198 38
cannam@198 39 using std::string;
cannam@198 40 using std::vector;
cannam@198 41 using std::cerr;
cannam@198 42 using std::endl;
cannam@198 43
cannam@198 44 using Vamp::RealTime;
cannam@198 45
cannam@198 46 #include <cmath>
cannam@198 47
cannam@198 48
cannam@243 49 class FixedTempoEstimator::D
cannam@255 50 // this class just avoids us having to declare any data members in the header
cannam@243 51 {
cannam@243 52 public:
cannam@243 53 D(float inputSampleRate);
cannam@243 54 ~D();
cannam@243 55
cannam@243 56 size_t getPreferredStepSize() const { return 64; }
cannam@243 57 size_t getPreferredBlockSize() const { return 256; }
cannam@243 58
cannam@243 59 ParameterList getParameterDescriptors() const;
cannam@243 60 float getParameter(string id) const;
cannam@243 61 void setParameter(string id, float value);
cannam@243 62
cannam@243 63 OutputList getOutputDescriptors() const;
cannam@243 64
cannam@243 65 bool initialise(size_t channels, size_t stepSize, size_t blockSize);
cannam@243 66 void reset();
cannam@243 67 FeatureSet process(const float *const *, RealTime);
cannam@243 68 FeatureSet getRemainingFeatures();
cannam@243 69
cannam@243 70 private:
cannam@243 71 void calculate();
cannam@243 72 FeatureSet assembleFeatures();
cannam@243 73
cannam@243 74 float lag2tempo(int);
cannam@243 75 int tempo2lag(float);
cannam@243 76
cannam@243 77 float m_inputSampleRate;
cannam@243 78 size_t m_stepSize;
cannam@243 79 size_t m_blockSize;
cannam@243 80
cannam@243 81 float m_minbpm;
cannam@243 82 float m_maxbpm;
cannam@243 83 float m_maxdflen;
cannam@243 84
cannam@243 85 float *m_priorMagnitudes;
cannam@243 86
cannam@243 87 size_t m_dfsize;
cannam@243 88 float *m_df;
cannam@243 89 float *m_r;
cannam@243 90 float *m_fr;
cannam@243 91 float *m_t;
cannam@243 92 size_t m_n;
cannam@243 93
cannam@243 94 Vamp::RealTime m_start;
cannam@243 95 Vamp::RealTime m_lasttime;
cannam@243 96 };
cannam@243 97
cannam@243 98 FixedTempoEstimator::D::D(float inputSampleRate) :
cannam@243 99 m_inputSampleRate(inputSampleRate),
cannam@198 100 m_stepSize(0),
cannam@198 101 m_blockSize(0),
cannam@243 102 m_minbpm(50),
cannam@243 103 m_maxbpm(190),
cannam@243 104 m_maxdflen(10),
cannam@198 105 m_priorMagnitudes(0),
cannam@200 106 m_df(0),
cannam@200 107 m_r(0),
cannam@200 108 m_fr(0),
cannam@204 109 m_t(0),
cannam@200 110 m_n(0)
cannam@198 111 {
cannam@198 112 }
cannam@198 113
cannam@243 114 FixedTempoEstimator::D::~D()
cannam@198 115 {
cannam@198 116 delete[] m_priorMagnitudes;
cannam@198 117 delete[] m_df;
cannam@200 118 delete[] m_r;
cannam@200 119 delete[] m_fr;
cannam@204 120 delete[] m_t;
cannam@198 121 }
cannam@198 122
cannam@198 123 FixedTempoEstimator::ParameterList
cannam@243 124 FixedTempoEstimator::D::getParameterDescriptors() const
cannam@198 125 {
cannam@198 126 ParameterList list;
cannam@243 127
cannam@243 128 ParameterDescriptor d;
cannam@243 129 d.identifier = "minbpm";
cannam@243 130 d.name = "Minimum estimated tempo";
cannam@243 131 d.description = "Minimum beat-per-minute value which the tempo estimator is able to return";
cannam@243 132 d.unit = "bpm";
cannam@243 133 d.minValue = 10;
cannam@243 134 d.maxValue = 360;
cannam@243 135 d.defaultValue = 50;
cannam@243 136 d.isQuantized = false;
cannam@243 137 list.push_back(d);
cannam@243 138
cannam@243 139 d.identifier = "maxbpm";
cannam@243 140 d.name = "Maximum estimated tempo";
cannam@243 141 d.description = "Maximum beat-per-minute value which the tempo estimator is able to return";
cannam@243 142 d.defaultValue = 190;
cannam@243 143 list.push_back(d);
cannam@243 144
cannam@243 145 d.identifier = "maxdflen";
cannam@243 146 d.name = "Input duration to study";
cannam@243 147 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@243 148 d.unit = "s";
cannam@243 149 d.minValue = 2;
cannam@243 150 d.maxValue = 40;
cannam@243 151 d.defaultValue = 10;
cannam@243 152 list.push_back(d);
cannam@243 153
cannam@198 154 return list;
cannam@198 155 }
cannam@198 156
cannam@198 157 float
cannam@243 158 FixedTempoEstimator::D::getParameter(string id) const
cannam@198 159 {
cannam@243 160 if (id == "minbpm") {
cannam@243 161 return m_minbpm;
cannam@243 162 } else if (id == "maxbpm") {
cannam@243 163 return m_maxbpm;
cannam@243 164 } else if (id == "maxdflen") {
cannam@243 165 return m_maxdflen;
cannam@243 166 }
cannam@198 167 return 0.f;
cannam@198 168 }
cannam@198 169
cannam@198 170 void
cannam@243 171 FixedTempoEstimator::D::setParameter(string id, float value)
cannam@198 172 {
cannam@243 173 if (id == "minbpm") {
cannam@243 174 m_minbpm = value;
cannam@243 175 } else if (id == "maxbpm") {
cannam@243 176 m_maxbpm = value;
cannam@243 177 } else if (id == "maxdflen") {
cannam@243 178 m_maxdflen = value;
cannam@243 179 }
cannam@198 180 }
cannam@198 181
cannam@200 182 static int TempoOutput = 0;
cannam@200 183 static int CandidatesOutput = 1;
cannam@200 184 static int DFOutput = 2;
cannam@200 185 static int ACFOutput = 3;
cannam@200 186 static int FilteredACFOutput = 4;
cannam@200 187
cannam@198 188 FixedTempoEstimator::OutputList
cannam@243 189 FixedTempoEstimator::D::getOutputDescriptors() const
cannam@198 190 {
cannam@198 191 OutputList list;
cannam@198 192
cannam@198 193 OutputDescriptor d;
cannam@198 194 d.identifier = "tempo";
cannam@198 195 d.name = "Tempo";
cannam@198 196 d.description = "Estimated tempo";
cannam@198 197 d.unit = "bpm";
cannam@198 198 d.hasFixedBinCount = true;
cannam@198 199 d.binCount = 1;
cannam@198 200 d.hasKnownExtents = false;
cannam@198 201 d.isQuantized = false;
cannam@198 202 d.sampleType = OutputDescriptor::VariableSampleRate;
cannam@198 203 d.sampleRate = m_inputSampleRate;
cannam@198 204 d.hasDuration = true; // our returned tempo spans a certain range
cannam@198 205 list.push_back(d);
cannam@198 206
cannam@200 207 d.identifier = "candidates";
cannam@200 208 d.name = "Tempo candidates";
cannam@200 209 d.description = "Possible tempo estimates, one per bin with the most likely in the first bin";
cannam@200 210 d.unit = "bpm";
cannam@200 211 d.hasFixedBinCount = false;
cannam@200 212 list.push_back(d);
cannam@200 213
cannam@198 214 d.identifier = "detectionfunction";
cannam@198 215 d.name = "Detection Function";
cannam@198 216 d.description = "Onset detection function";
cannam@198 217 d.unit = "";
cannam@198 218 d.hasFixedBinCount = 1;
cannam@198 219 d.binCount = 1;
cannam@198 220 d.hasKnownExtents = true;
cannam@198 221 d.minValue = 0.0;
cannam@198 222 d.maxValue = 1.0;
cannam@198 223 d.isQuantized = false;
cannam@198 224 d.quantizeStep = 0.0;
cannam@198 225 d.sampleType = OutputDescriptor::FixedSampleRate;
cannam@198 226 if (m_stepSize) {
cannam@198 227 d.sampleRate = m_inputSampleRate / m_stepSize;
cannam@198 228 } else {
cannam@198 229 d.sampleRate = m_inputSampleRate / (getPreferredBlockSize()/2);
cannam@198 230 }
cannam@198 231 d.hasDuration = false;
cannam@198 232 list.push_back(d);
cannam@198 233
cannam@198 234 d.identifier = "acf";
cannam@198 235 d.name = "Autocorrelation Function";
cannam@198 236 d.description = "Autocorrelation of onset detection function";
cannam@198 237 d.hasKnownExtents = false;
cannam@201 238 d.unit = "r";
cannam@198 239 list.push_back(d);
cannam@198 240
cannam@198 241 d.identifier = "filtered_acf";
cannam@198 242 d.name = "Filtered Autocorrelation";
cannam@198 243 d.description = "Filtered autocorrelation of onset detection function";
cannam@201 244 d.unit = "r";
cannam@198 245 list.push_back(d);
cannam@198 246
cannam@198 247 return list;
cannam@198 248 }
cannam@198 249
cannam@243 250 bool
cannam@271 251 FixedTempoEstimator::D::initialise(size_t, size_t stepSize, size_t blockSize)
cannam@243 252 {
cannam@243 253 m_stepSize = stepSize;
cannam@243 254 m_blockSize = blockSize;
cannam@243 255
cannam@243 256 float dfLengthSecs = m_maxdflen;
cannam@243 257 m_dfsize = (dfLengthSecs * m_inputSampleRate) / m_stepSize;
cannam@243 258
cannam@243 259 m_priorMagnitudes = new float[m_blockSize/2];
cannam@243 260 m_df = new float[m_dfsize];
cannam@243 261
cannam@243 262 for (size_t i = 0; i < m_blockSize/2; ++i) {
cannam@243 263 m_priorMagnitudes[i] = 0.f;
cannam@243 264 }
cannam@243 265 for (size_t i = 0; i < m_dfsize; ++i) {
cannam@243 266 m_df[i] = 0.f;
cannam@243 267 }
cannam@243 268
cannam@243 269 m_n = 0;
cannam@243 270
cannam@243 271 return true;
cannam@243 272 }
cannam@243 273
cannam@243 274 void
cannam@243 275 FixedTempoEstimator::D::reset()
cannam@243 276 {
cannam@243 277 if (!m_priorMagnitudes) return;
cannam@243 278
cannam@243 279 for (size_t i = 0; i < m_blockSize/2; ++i) {
cannam@243 280 m_priorMagnitudes[i] = 0.f;
cannam@243 281 }
cannam@243 282 for (size_t i = 0; i < m_dfsize; ++i) {
cannam@243 283 m_df[i] = 0.f;
cannam@243 284 }
cannam@243 285
cannam@243 286 delete[] m_r;
cannam@243 287 m_r = 0;
cannam@243 288
cannam@243 289 delete[] m_fr;
cannam@243 290 m_fr = 0;
cannam@243 291
cannam@243 292 delete[] m_t;
cannam@243 293 m_t = 0;
cannam@243 294
cannam@243 295 m_n = 0;
cannam@243 296
cannam@243 297 m_start = RealTime::zeroTime;
cannam@243 298 m_lasttime = RealTime::zeroTime;
cannam@243 299 }
cannam@243 300
cannam@198 301 FixedTempoEstimator::FeatureSet
cannam@243 302 FixedTempoEstimator::D::process(const float *const *inputBuffers, RealTime ts)
cannam@198 303 {
cannam@198 304 FeatureSet fs;
cannam@198 305
cannam@198 306 if (m_stepSize == 0) {
cannam@198 307 cerr << "ERROR: FixedTempoEstimator::process: "
cannam@198 308 << "FixedTempoEstimator has not been initialised"
cannam@198 309 << endl;
cannam@198 310 return fs;
cannam@198 311 }
cannam@198 312
cannam@198 313 if (m_n == 0) m_start = ts;
cannam@198 314 m_lasttime = ts;
cannam@198 315
cannam@198 316 if (m_n == m_dfsize) {
cannam@255 317 // If we have seen enough input, do the estimation and return
cannam@200 318 calculate();
cannam@200 319 fs = assembleFeatures();
cannam@198 320 ++m_n;
cannam@198 321 return fs;
cannam@198 322 }
cannam@198 323
cannam@255 324 // If we have seen more than enough, just discard and return!
cannam@198 325 if (m_n > m_dfsize) return FeatureSet();
cannam@198 326
cannam@207 327 float value = 0.f;
cannam@207 328
cannam@255 329 // m_df will contain an onset detection function based on the rise
cannam@255 330 // in overall power from one spectral frame to the next --
cannam@255 331 // simplistic but reasonably effective for our purposes.
cannam@255 332
cannam@198 333 for (size_t i = 1; i < m_blockSize/2; ++i) {
cannam@198 334
cannam@198 335 float real = inputBuffers[0][i*2];
cannam@198 336 float imag = inputBuffers[0][i*2 + 1];
cannam@198 337
cannam@198 338 float sqrmag = real * real + imag * imag;
cannam@207 339 value += fabsf(sqrmag - m_priorMagnitudes[i]);
cannam@198 340
cannam@198 341 m_priorMagnitudes[i] = sqrmag;
cannam@198 342 }
cannam@198 343
cannam@207 344 m_df[m_n] = value;
cannam@207 345
cannam@198 346 ++m_n;
cannam@198 347 return fs;
cannam@243 348 }
cannam@198 349
cannam@198 350 FixedTempoEstimator::FeatureSet
cannam@243 351 FixedTempoEstimator::D::getRemainingFeatures()
cannam@198 352 {
cannam@198 353 FeatureSet fs;
cannam@198 354 if (m_n > m_dfsize) return fs;
cannam@200 355 calculate();
cannam@200 356 fs = assembleFeatures();
cannam@198 357 ++m_n;
cannam@198 358 return fs;
cannam@198 359 }
cannam@198 360
cannam@198 361 float
cannam@243 362 FixedTempoEstimator::D::lag2tempo(int lag)
cannam@199 363 {
cannam@198 364 return 60.f / ((lag * m_stepSize) / m_inputSampleRate);
cannam@198 365 }
cannam@198 366
cannam@207 367 int
cannam@243 368 FixedTempoEstimator::D::tempo2lag(float tempo)
cannam@207 369 {
cannam@207 370 return ((60.f / tempo) * m_inputSampleRate) / m_stepSize;
cannam@207 371 }
cannam@207 372
cannam@200 373 void
cannam@243 374 FixedTempoEstimator::D::calculate()
cannam@200 375 {
cannam@200 376 if (m_r) {
cannam@207 377 cerr << "FixedTempoEstimator::calculate: calculation already happened?" << endl;
cannam@200 378 return;
cannam@200 379 }
cannam@200 380
cannam@243 381 if (m_n < m_dfsize / 9 &&
cannam@243 382 m_n < (1.0 * m_inputSampleRate) / m_stepSize) { // 1 second
cannam@243 383 cerr << "FixedTempoEstimator::calculate: Input is too short" << endl;
cannam@243 384 return;
cannam@200 385 }
cannam@200 386
cannam@255 387 // This function takes m_df (the detection function array filled
cannam@255 388 // out in process()) and calculates m_r (the raw autocorrelation)
cannam@255 389 // and m_fr (the filtered autocorrelation from whose peaks tempo
cannam@255 390 // estimates will be taken).
cannam@200 391
cannam@255 392 int n = m_n; // length of actual df array (m_dfsize is the theoretical max)
cannam@255 393
cannam@255 394 m_r = new float[n/2]; // raw autocorrelation
cannam@255 395 m_fr = new float[n/2]; // filtered autocorrelation
cannam@255 396 m_t = new float[n/2]; // averaged tempo estimate for each lag value
cannam@200 397
cannam@200 398 for (int i = 0; i < n/2; ++i) {
cannam@255 399 m_r[i] = 0.f;
cannam@200 400 m_fr[i] = 0.f;
cannam@255 401 m_t[i] = lag2tempo(i);
cannam@200 402 }
cannam@200 403
cannam@255 404 // Calculate the raw autocorrelation of the detection function
cannam@255 405
cannam@200 406 for (int i = 0; i < n/2; ++i) {
cannam@200 407
cannam@271 408 for (int j = i; j < n; ++j) {
cannam@200 409 m_r[i] += m_df[j] * m_df[j - i];
cannam@200 410 }
cannam@200 411
cannam@200 412 m_r[i] /= n - i - 1;
cannam@200 413 }
cannam@200 414
cannam@255 415 // Filter the autocorrelation and average out the tempo estimates
cannam@255 416
cannam@246 417 float related[] = { 0.5, 2, 4, 8 };
cannam@208 418
cannam@209 419 for (int i = 1; i < n/2-1; ++i) {
cannam@204 420
cannam@209 421 m_fr[i] = m_r[i];
cannam@204 422
cannam@200 423 int div = 1;
cannam@200 424
cannam@215 425 for (int j = 0; j < int(sizeof(related)/sizeof(related[0])); ++j) {
cannam@204 426
cannam@255 427 // Check for an obvious peak at each metrically related lag
cannam@255 428
cannam@215 429 int k0 = int(i * related[j] + 0.5);
cannam@209 430
cannam@215 431 if (k0 >= 0 && k0 < int(n/2)) {
cannam@204 432
cannam@207 433 int kmax = 0, kmin = 0;
cannam@207 434 float kvmax = 0, kvmin = 0;
cannam@209 435 bool have = false;
cannam@204 436
cannam@209 437 for (int k = k0 - 1; k <= k0 + 1; ++k) {
cannam@204 438
cannam@209 439 if (k < 0 || k >= n/2) continue;
cannam@209 440
cannam@215 441 if (!have || (m_r[k] > kvmax)) { kmax = k; kvmax = m_r[k]; }
cannam@215 442 if (!have || (m_r[k] < kvmin)) { kmin = k; kvmin = m_r[k]; }
cannam@209 443
cannam@209 444 have = true;
cannam@204 445 }
cannam@209 446
cannam@255 447 // Boost the original lag according to the strongest
cannam@255 448 // value found close to this related lag
cannam@255 449
cannam@215 450 m_fr[i] += m_r[kmax] / 5;
cannam@209 451
cannam@209 452 if ((kmax == 0 || m_r[kmax] > m_r[kmax-1]) &&
cannam@209 453 (kmax == n/2-1 || m_r[kmax] > m_r[kmax+1]) &&
cannam@207 454 kvmax > kvmin * 1.05) {
cannam@255 455
cannam@255 456 // The strongest value close to the related lag is
cannam@255 457 // also a pretty good looking peak, so use it to
cannam@255 458 // improve our tempo estimate for the original lag
cannam@209 459
cannam@207 460 m_t[i] = m_t[i] + lag2tempo(kmax) * related[j];
cannam@207 461 ++div;
cannam@207 462 }
cannam@204 463 }
cannam@204 464 }
cannam@209 465
cannam@204 466 m_t[i] /= div;
cannam@204 467
cannam@255 468 // Finally apply a primitive perceptual weighting (to prefer
cannam@255 469 // tempi of around 120-130)
cannam@255 470
cannam@255 471 float weight = 1.f - fabsf(128.f - lag2tempo(i)) * 0.005;
cannam@255 472 if (weight < 0.f) weight = 0.f;
cannam@255 473 weight = weight * weight * weight;
cannam@255 474
cannam@215 475 m_fr[i] += m_fr[i] * (weight / 3);
cannam@207 476 }
cannam@200 477 }
cannam@200 478
cannam@198 479 FixedTempoEstimator::FeatureSet
cannam@243 480 FixedTempoEstimator::D::assembleFeatures()
cannam@198 481 {
cannam@198 482 FeatureSet fs;
cannam@255 483 if (!m_r) return fs; // No autocorrelation: no results
cannam@200 484
cannam@198 485 Feature feature;
cannam@198 486 feature.hasTimestamp = true;
cannam@198 487 feature.hasDuration = false;
cannam@198 488 feature.label = "";
cannam@198 489 feature.values.clear();
cannam@198 490 feature.values.push_back(0.f);
cannam@198 491
cannam@200 492 char buffer[40];
cannam@198 493
cannam@198 494 int n = m_n;
cannam@198 495
cannam@198 496 for (int i = 0; i < n; ++i) {
cannam@255 497
cannam@255 498 // Return the detection function in the DF output
cannam@255 499
cannam@208 500 feature.timestamp = m_start +
cannam@208 501 RealTime::frame2RealTime(i * m_stepSize, m_inputSampleRate);
cannam@200 502 feature.values[0] = m_df[i];
cannam@198 503 feature.label = "";
cannam@200 504 fs[DFOutput].push_back(feature);
cannam@198 505 }
cannam@198 506
cannam@199 507 for (int i = 1; i < n/2; ++i) {
cannam@255 508
cannam@255 509 // Return the raw autocorrelation in the ACF output, each
cannam@255 510 // value labelled according to its corresponding tempo
cannam@255 511
cannam@208 512 feature.timestamp = m_start +
cannam@208 513 RealTime::frame2RealTime(i * m_stepSize, m_inputSampleRate);
cannam@200 514 feature.values[0] = m_r[i];
cannam@199 515 sprintf(buffer, "%.1f bpm", lag2tempo(i));
cannam@200 516 if (i == n/2-1) feature.label = "";
cannam@200 517 else feature.label = buffer;
cannam@200 518 fs[ACFOutput].push_back(feature);
cannam@198 519 }
cannam@198 520
cannam@243 521 float t0 = m_minbpm; // our minimum detected tempo
cannam@243 522 float t1 = m_maxbpm; // our maximum detected tempo
cannam@216 523
cannam@207 524 int p0 = tempo2lag(t1);
cannam@207 525 int p1 = tempo2lag(t0);
cannam@198 526
cannam@200 527 std::map<float, int> candidates;
cannam@198 528
cannam@271 529 for (int i = p0; i <= p1 && i+1 < n/2; ++i) {
cannam@198 530
cannam@209 531 if (m_fr[i] > m_fr[i-1] &&
cannam@209 532 m_fr[i] > m_fr[i+1]) {
cannam@255 533
cannam@255 534 // This is a peak in the filtered autocorrelation: stick
cannam@255 535 // it into the map from filtered autocorrelation to lag
cannam@255 536 // index -- this sorts our peaks by filtered acf value
cannam@255 537
cannam@209 538 candidates[m_fr[i]] = i;
cannam@209 539 }
cannam@198 540
cannam@255 541 // Also return the filtered autocorrelation in its own output
cannam@255 542
cannam@208 543 feature.timestamp = m_start +
cannam@208 544 RealTime::frame2RealTime(i * m_stepSize, m_inputSampleRate);
cannam@200 545 feature.values[0] = m_fr[i];
cannam@199 546 sprintf(buffer, "%.1f bpm", lag2tempo(i));
cannam@200 547 if (i == p1 || i == n/2-2) feature.label = "";
cannam@200 548 else feature.label = buffer;
cannam@200 549 fs[FilteredACFOutput].push_back(feature);
cannam@198 550 }
cannam@198 551
cannam@200 552 if (candidates.empty()) {
cannam@207 553 cerr << "No tempo candidates!" << endl;
cannam@200 554 return fs;
cannam@200 555 }
cannam@198 556
cannam@198 557 feature.hasTimestamp = true;
cannam@198 558 feature.timestamp = m_start;
cannam@198 559
cannam@198 560 feature.hasDuration = true;
cannam@198 561 feature.duration = m_lasttime - m_start;
cannam@198 562
cannam@255 563 // The map contains only peaks and is sorted by filtered acf
cannam@255 564 // value, so the final element in it is our "best" tempo guess
cannam@255 565
cannam@200 566 std::map<float, int>::const_iterator ci = candidates.end();
cannam@200 567 --ci;
cannam@200 568 int maxpi = ci->second;
cannam@198 569
cannam@204 570 if (m_t[maxpi] > 0) {
cannam@255 571
cannam@255 572 // This lag has an adjusted tempo from the averaging process:
cannam@255 573 // use it
cannam@255 574
cannam@204 575 feature.values[0] = m_t[maxpi];
cannam@255 576
cannam@204 577 } else {
cannam@255 578
cannam@255 579 // shouldn't happen -- it would imply that this high value was
cannam@255 580 // not a peak!
cannam@255 581
cannam@204 582 feature.values[0] = lag2tempo(maxpi);
cannam@207 583 cerr << "WARNING: No stored tempo for index " << maxpi << endl;
cannam@204 584 }
cannam@204 585
cannam@204 586 sprintf(buffer, "%.1f bpm", feature.values[0]);
cannam@199 587 feature.label = buffer;
cannam@199 588
cannam@255 589 // Return the best tempo in the main output
cannam@255 590
cannam@200 591 fs[TempoOutput].push_back(feature);
cannam@198 592
cannam@255 593 // And return the other estimates (up to the arbitrarily chosen
cannam@255 594 // number of 10 of them) in the candidates output
cannam@255 595
cannam@200 596 feature.values.clear();
cannam@200 597 feature.label = "";
cannam@200 598
cannam@255 599 while (feature.values.size() < 10) {
cannam@207 600 if (m_t[ci->second] > 0) {
cannam@207 601 feature.values.push_back(m_t[ci->second]);
cannam@207 602 } else {
cannam@207 603 feature.values.push_back(lag2tempo(ci->second));
cannam@207 604 }
cannam@200 605 if (ci == candidates.begin()) break;
cannam@200 606 --ci;
cannam@200 607 }
cannam@200 608
cannam@200 609 fs[CandidatesOutput].push_back(feature);
cannam@200 610
cannam@198 611 return fs;
cannam@198 612 }
cannam@243 613
cannam@243 614
cannam@243 615
cannam@243 616 FixedTempoEstimator::FixedTempoEstimator(float inputSampleRate) :
cannam@243 617 Plugin(inputSampleRate),
cannam@243 618 m_d(new D(inputSampleRate))
cannam@243 619 {
cannam@243 620 }
cannam@243 621
cannam@243 622 FixedTempoEstimator::~FixedTempoEstimator()
cannam@243 623 {
cannam@271 624 delete m_d;
cannam@243 625 }
cannam@243 626
cannam@243 627 string
cannam@243 628 FixedTempoEstimator::getIdentifier() const
cannam@243 629 {
cannam@243 630 return "fixedtempo";
cannam@243 631 }
cannam@243 632
cannam@243 633 string
cannam@243 634 FixedTempoEstimator::getName() const
cannam@243 635 {
cannam@243 636 return "Simple Fixed Tempo Estimator";
cannam@243 637 }
cannam@243 638
cannam@243 639 string
cannam@243 640 FixedTempoEstimator::getDescription() const
cannam@243 641 {
cannam@243 642 return "Study a short section of audio and estimate its tempo, assuming the tempo is constant";
cannam@243 643 }
cannam@243 644
cannam@243 645 string
cannam@243 646 FixedTempoEstimator::getMaker() const
cannam@243 647 {
cannam@243 648 return "Vamp SDK Example Plugins";
cannam@243 649 }
cannam@243 650
cannam@243 651 int
cannam@243 652 FixedTempoEstimator::getPluginVersion() const
cannam@243 653 {
cannam@243 654 return 1;
cannam@243 655 }
cannam@243 656
cannam@243 657 string
cannam@243 658 FixedTempoEstimator::getCopyright() const
cannam@243 659 {
cannam@243 660 return "Code copyright 2008 Queen Mary, University of London. Freely redistributable (BSD license)";
cannam@243 661 }
cannam@243 662
cannam@243 663 size_t
cannam@243 664 FixedTempoEstimator::getPreferredStepSize() const
cannam@243 665 {
cannam@243 666 return m_d->getPreferredStepSize();
cannam@243 667 }
cannam@243 668
cannam@243 669 size_t
cannam@243 670 FixedTempoEstimator::getPreferredBlockSize() const
cannam@243 671 {
cannam@243 672 return m_d->getPreferredBlockSize();
cannam@243 673 }
cannam@243 674
cannam@243 675 bool
cannam@243 676 FixedTempoEstimator::initialise(size_t channels, size_t stepSize, size_t blockSize)
cannam@243 677 {
cannam@243 678 if (channels < getMinChannelCount() ||
cannam@243 679 channels > getMaxChannelCount()) return false;
cannam@243 680
cannam@243 681 return m_d->initialise(channels, stepSize, blockSize);
cannam@243 682 }
cannam@243 683
cannam@243 684 void
cannam@243 685 FixedTempoEstimator::reset()
cannam@243 686 {
cannam@243 687 return m_d->reset();
cannam@243 688 }
cannam@243 689
cannam@243 690 FixedTempoEstimator::ParameterList
cannam@243 691 FixedTempoEstimator::getParameterDescriptors() const
cannam@243 692 {
cannam@243 693 return m_d->getParameterDescriptors();
cannam@243 694 }
cannam@243 695
cannam@243 696 float
cannam@243 697 FixedTempoEstimator::getParameter(std::string id) const
cannam@243 698 {
cannam@243 699 return m_d->getParameter(id);
cannam@243 700 }
cannam@243 701
cannam@243 702 void
cannam@243 703 FixedTempoEstimator::setParameter(std::string id, float value)
cannam@243 704 {
cannam@243 705 m_d->setParameter(id, value);
cannam@243 706 }
cannam@243 707
cannam@243 708 FixedTempoEstimator::OutputList
cannam@243 709 FixedTempoEstimator::getOutputDescriptors() const
cannam@243 710 {
cannam@243 711 return m_d->getOutputDescriptors();
cannam@243 712 }
cannam@243 713
cannam@243 714 FixedTempoEstimator::FeatureSet
cannam@243 715 FixedTempoEstimator::process(const float *const *inputBuffers, RealTime ts)
cannam@243 716 {
cannam@243 717 return m_d->process(inputBuffers, ts);
cannam@243 718 }
cannam@243 719
cannam@243 720 FixedTempoEstimator::FeatureSet
cannam@243 721 FixedTempoEstimator::getRemainingFeatures()
cannam@243 722 {
cannam@243 723 return m_d->getRemainingFeatures();
cannam@243 724 }