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