annotate src/vamp-plugin-sdk-2.4/examples/FixedTempoEstimator.cpp @ 169:223a55898ab9 tip default

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