annotate plugins/BeatTrack.cpp @ 144:67b6ea887d5d

* BeatTracker::BeatTracker has 4 new parameters: m_alpha, m_tightness, m_inputtempo and m_constraintempo * same applies to BarBeatTracker (protected)
author luisf <luis.figueira@eecs.qmul.ac.uk>
date Wed, 12 Dec 2012 15:39:00 +0000
parents dcf5800f0f00
children edad8a88a074
rev   line source
c@27 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
c@27 2
c@27 3 /*
c@27 4 QM Vamp Plugin Set
c@27 5
c@27 6 Centre for Digital Music, Queen Mary, University of London.
c@135 7
c@135 8 This program is free software; you can redistribute it and/or
c@135 9 modify it under the terms of the GNU General Public License as
c@135 10 published by the Free Software Foundation; either version 2 of the
c@135 11 License, or (at your option) any later version. See the file
c@135 12 COPYING included with this distribution for more information.
c@27 13 */
c@27 14
c@27 15 #include "BeatTrack.h"
c@27 16
c@27 17 #include <dsp/onsets/DetectionFunction.h>
c@27 18 #include <dsp/onsets/PeakPicking.h>
c@27 19 #include <dsp/tempotracking/TempoTrack.h>
c@86 20 #include <dsp/tempotracking/TempoTrackV2.h>
c@27 21
c@27 22 using std::string;
c@27 23 using std::vector;
c@27 24 using std::cerr;
c@27 25 using std::endl;
c@27 26
c@86 27 float BeatTracker::m_stepSecs = 0.01161; // 512 samples at 44100
c@86 28
c@86 29 #define METHOD_OLD 0
c@86 30 #define METHOD_NEW 1
c@27 31
c@27 32 class BeatTrackerData
c@27 33 {
c@27 34 public:
c@27 35 BeatTrackerData(const DFConfig &config) : dfConfig(config) {
luis@144 36 df = new DetectionFunction(config);
c@27 37 }
c@27 38 ~BeatTrackerData() {
luis@144 39 delete df;
c@27 40 }
c@27 41 void reset() {
luis@144 42 delete df;
luis@144 43 df = new DetectionFunction(dfConfig);
luis@144 44 dfOutput.clear();
c@85 45 origin = Vamp::RealTime::zeroTime;
c@27 46 }
c@27 47
c@27 48 DFConfig dfConfig;
c@27 49 DetectionFunction *df;
c@27 50 vector<double> dfOutput;
c@85 51 Vamp::RealTime origin;
c@27 52 };
luis@144 53
c@27 54
c@27 55 BeatTracker::BeatTracker(float inputSampleRate) :
c@27 56 Vamp::Plugin(inputSampleRate),
c@27 57 m_d(0),
c@86 58 m_method(METHOD_NEW),
c@30 59 m_dfType(DF_COMPLEXSD),
luis@144 60 m_whiten(false),
luis@144 61 m_alpha(0.9), // MEPD new exposed parameter for beat tracker, default value = 0.9 (as old version)
luis@144 62 m_tightness(4.), // MEPD new exposed parameter for beat tracker, default value = 4. (as old version)
luis@144 63 m_inputtempo(120.), // MEPD new exposed parameter for beat tracker, default value = 120. (as old version)
luis@144 64 m_constraintempo(false) // MEPD new exposed parameter for beat tracker, default value = false (as old version)
luis@144 65 // calling the beat tracker with these default parameters will give the same output as the previous existing version
luis@144 66
c@27 67 {
c@27 68 }
c@27 69
c@27 70 BeatTracker::~BeatTracker()
c@27 71 {
c@27 72 delete m_d;
c@27 73 }
c@27 74
c@27 75 string
c@27 76 BeatTracker::getIdentifier() const
c@27 77 {
c@27 78 return "qm-tempotracker";
c@27 79 }
c@27 80
c@27 81 string
c@27 82 BeatTracker::getName() const
c@27 83 {
c@27 84 return "Tempo and Beat Tracker";
c@27 85 }
c@27 86
c@27 87 string
c@27 88 BeatTracker::getDescription() const
c@27 89 {
c@27 90 return "Estimate beat locations and tempo";
c@27 91 }
c@27 92
c@27 93 string
c@27 94 BeatTracker::getMaker() const
c@27 95 {
c@50 96 return "Queen Mary, University of London";
c@27 97 }
c@27 98
c@27 99 int
c@27 100 BeatTracker::getPluginVersion() const
c@27 101 {
c@131 102 return 5;
c@27 103 }
c@27 104
c@27 105 string
c@27 106 BeatTracker::getCopyright() const
c@27 107 {
luis@144 108 return "Plugin by Christian Landone and Matthew Davies. Copyright (c) 2006-2012 QMUL - All Rights Reserved";
c@27 109 }
c@27 110
c@27 111 BeatTracker::ParameterList
c@27 112 BeatTracker::getParameterDescriptors() const
c@27 113 {
c@27 114 ParameterList list;
c@27 115
c@27 116 ParameterDescriptor desc;
c@86 117
c@86 118 desc.identifier = "method";
c@86 119 desc.name = "Beat Tracking Method";
c@119 120 desc.description = "Basic method to use ";
c@86 121 desc.minValue = 0;
c@86 122 desc.maxValue = 1;
c@86 123 desc.defaultValue = METHOD_NEW;
c@86 124 desc.isQuantized = true;
c@86 125 desc.quantizeStep = 1;
c@86 126 desc.valueNames.push_back("Old");
c@86 127 desc.valueNames.push_back("New");
c@86 128 list.push_back(desc);
c@86 129
c@27 130 desc.identifier = "dftype";
c@27 131 desc.name = "Onset Detection Function Type";
c@27 132 desc.description = "Method used to calculate the onset detection function";
c@27 133 desc.minValue = 0;
c@31 134 desc.maxValue = 4;
c@27 135 desc.defaultValue = 3;
c@86 136 desc.valueNames.clear();
c@27 137 desc.valueNames.push_back("High-Frequency Content");
c@27 138 desc.valueNames.push_back("Spectral Difference");
c@27 139 desc.valueNames.push_back("Phase Deviation");
c@27 140 desc.valueNames.push_back("Complex Domain");
c@27 141 desc.valueNames.push_back("Broadband Energy Rise");
c@27 142 list.push_back(desc);
c@27 143
c@30 144 desc.identifier = "whiten";
c@30 145 desc.name = "Adaptive Whitening";
c@30 146 desc.description = "Normalize frequency bin magnitudes relative to recent peak levels";
c@30 147 desc.minValue = 0;
c@30 148 desc.maxValue = 1;
c@30 149 desc.defaultValue = 0;
c@30 150 desc.isQuantized = true;
c@30 151 desc.quantizeStep = 1;
c@30 152 desc.unit = "";
c@30 153 desc.valueNames.clear();
c@30 154 list.push_back(desc);
c@30 155
luis@144 156 // MEPD new exposed parameter - used in the dynamic programming part of the beat tracker
luis@144 157 //Alpha Parameter of Beat Tracker
luis@144 158 desc.identifier = "alpha";
luis@144 159 desc.name = "Alpha";
luis@144 160 desc.description = "Inertia - Flexibility Trade Off";
luis@144 161 desc.minValue = 0.1;
luis@144 162 desc.maxValue = 0.99;
luis@144 163 desc.defaultValue = 0.90;
luis@144 164 desc.unit = "";
luis@144 165 desc.isQuantized = false;
luis@144 166 list.push_back(desc);
luis@144 167
luis@144 168
luis@144 169 // MEPD new exposed parameter - used in the dynamic programming part of the beat tracker
luis@144 170 //Tightness Parameter of Beat Tracker
luis@144 171 desc.identifier = "tightness";
luis@144 172 desc.name = "Tightness";
luis@144 173 desc.description = "Inertia - Flexibility Trade Off 2";
luis@144 174 desc.minValue = 3;
luis@144 175 desc.maxValue = 7;
luis@144 176 desc.defaultValue = 4;
luis@144 177 desc.unit = "";
luis@144 178 desc.isQuantized = true;
luis@144 179 list.push_back(desc);
luis@144 180
luis@144 181 // MEPD new exposed parameter - used in the periodicity estimation
luis@144 182 //User input tempo
luis@144 183 desc.identifier = "inputtempo";
luis@144 184 desc.name = "InputTempo";
luis@144 185 desc.description = "User defined Tempo";
luis@144 186 desc.minValue = 50;
luis@144 187 desc.maxValue = 250;
luis@144 188 desc.defaultValue = 120;
luis@144 189 desc.unit = "BPM";
luis@144 190 desc.isQuantized = true;
luis@144 191 list.push_back(desc);
luis@144 192
luis@144 193 // MEPD new exposed parameter - used in periodicity estimation
luis@144 194 desc.identifier = "constraintempo";
luis@144 195 desc.name = "Constrain Tempo";
luis@144 196 desc.description = "Constrain tempo to use Gaussian weighting";
luis@144 197 desc.minValue = 0;
luis@144 198 desc.maxValue = 1;
luis@144 199 desc.defaultValue = 0;
luis@144 200 desc.isQuantized = true;
luis@144 201 desc.quantizeStep = 1;
luis@144 202 desc.unit = "";
luis@144 203 desc.valueNames.clear();
luis@144 204 list.push_back(desc);
luis@144 205
luis@144 206
luis@144 207
c@27 208 return list;
c@27 209 }
c@27 210
c@27 211 float
c@27 212 BeatTracker::getParameter(std::string name) const
c@27 213 {
c@27 214 if (name == "dftype") {
c@27 215 switch (m_dfType) {
c@27 216 case DF_HFC: return 0;
c@27 217 case DF_SPECDIFF: return 1;
c@27 218 case DF_PHASEDEV: return 2;
c@27 219 default: case DF_COMPLEXSD: return 3;
c@27 220 case DF_BROADBAND: return 4;
c@27 221 }
c@86 222 } else if (name == "method") {
c@86 223 return m_method;
c@30 224 } else if (name == "whiten") {
luis@144 225 return m_whiten ? 1.0 : 0.0;
luis@144 226 } else if (name == "alpha") {
luis@144 227 return m_alpha;
luis@144 228 } else if (name == "tightness") {
luis@144 229 return m_tightness;
luis@144 230 } else if (name == "inputtempo") {
luis@144 231 return m_inputtempo;
luis@144 232 } else if (name == "constraintempo") {
luis@144 233 return m_constraintempo ? 1.0 : 0.0;
c@27 234 }
c@27 235 return 0.0;
c@27 236 }
c@27 237
c@27 238 void
c@27 239 BeatTracker::setParameter(std::string name, float value)
c@27 240 {
c@27 241 if (name == "dftype") {
c@27 242 switch (lrintf(value)) {
c@27 243 case 0: m_dfType = DF_HFC; break;
c@27 244 case 1: m_dfType = DF_SPECDIFF; break;
c@27 245 case 2: m_dfType = DF_PHASEDEV; break;
c@27 246 default: case 3: m_dfType = DF_COMPLEXSD; break;
c@27 247 case 4: m_dfType = DF_BROADBAND; break;
c@27 248 }
c@86 249 } else if (name == "method") {
c@86 250 m_method = lrintf(value);
c@30 251 } else if (name == "whiten") {
c@30 252 m_whiten = (value > 0.5);
luis@144 253 } else if (name == "alpha") {
luis@144 254 m_alpha = value;
luis@144 255 } else if (name == "tightness") {
luis@144 256 m_tightness = value;
luis@144 257 } else if (name == "inputtempo") {
luis@144 258 m_inputtempo = value;
luis@144 259 } else if (name == "constraintempo") {
luis@144 260 m_constraintempo = (value > 0.5);
c@27 261 }
c@27 262 }
c@27 263
c@27 264 bool
c@27 265 BeatTracker::initialise(size_t channels, size_t stepSize, size_t blockSize)
c@27 266 {
c@27 267 if (m_d) {
luis@144 268 delete m_d;
luis@144 269 m_d = 0;
c@27 270 }
c@27 271
c@27 272 if (channels < getMinChannelCount() ||
luis@144 273 channels > getMaxChannelCount()) {
c@27 274 std::cerr << "BeatTracker::initialise: Unsupported channel count: "
c@27 275 << channels << std::endl;
c@27 276 return false;
c@27 277 }
c@27 278
c@28 279 if (stepSize != getPreferredStepSize()) {
c@28 280 std::cerr << "ERROR: BeatTracker::initialise: Unsupported step size for this sample rate: "
c@28 281 << stepSize << " (wanted " << (getPreferredStepSize()) << ")" << std::endl;
c@27 282 return false;
c@27 283 }
c@27 284
c@28 285 if (blockSize != getPreferredBlockSize()) {
c@29 286 std::cerr << "WARNING: BeatTracker::initialise: Sub-optimal block size for this sample rate: "
c@28 287 << blockSize << " (wanted " << getPreferredBlockSize() << ")" << std::endl;
c@28 288 // return false;
c@27 289 }
c@27 290
c@27 291 DFConfig dfConfig;
c@27 292 dfConfig.DFType = m_dfType;
c@27 293 dfConfig.stepSize = stepSize;
c@27 294 dfConfig.frameLength = blockSize;
c@27 295 dfConfig.dbRise = 3;
c@30 296 dfConfig.adaptiveWhitening = m_whiten;
c@30 297 dfConfig.whiteningRelaxCoeff = -1;
c@30 298 dfConfig.whiteningFloor = -1;
luis@144 299
c@27 300 m_d = new BeatTrackerData(dfConfig);
c@27 301 return true;
c@27 302 }
c@27 303
c@27 304 void
c@27 305 BeatTracker::reset()
c@27 306 {
c@27 307 if (m_d) m_d->reset();
c@27 308 }
c@27 309
c@27 310 size_t
c@27 311 BeatTracker::getPreferredStepSize() const
c@27 312 {
c@27 313 size_t step = size_t(m_inputSampleRate * m_stepSecs + 0.0001);
c@27 314 // std::cerr << "BeatTracker::getPreferredStepSize: input sample rate is " << m_inputSampleRate << ", step size is " << step << std::endl;
c@27 315 return step;
c@27 316 }
c@27 317
c@27 318 size_t
c@27 319 BeatTracker::getPreferredBlockSize() const
c@27 320 {
c@28 321 size_t theoretical = getPreferredStepSize() * 2;
c@28 322
c@52 323 // I think this is not necessarily going to be a power of two, and
c@52 324 // the host might have a problem with that, but I'm not sure we
c@52 325 // can do much about it here
c@28 326 return theoretical;
c@27 327 }
c@27 328
c@27 329 BeatTracker::OutputList
c@27 330 BeatTracker::getOutputDescriptors() const
c@27 331 {
c@27 332 OutputList list;
c@27 333
c@27 334 OutputDescriptor beat;
c@27 335 beat.identifier = "beats";
c@27 336 beat.name = "Beats";
c@27 337 beat.description = "Estimated metrical beat locations";
c@27 338 beat.unit = "";
c@27 339 beat.hasFixedBinCount = true;
c@27 340 beat.binCount = 0;
c@27 341 beat.sampleType = OutputDescriptor::VariableSampleRate;
c@27 342 beat.sampleRate = 1.0 / m_stepSecs;
c@27 343
c@27 344 OutputDescriptor df;
c@27 345 df.identifier = "detection_fn";
c@27 346 df.name = "Onset Detection Function";
c@27 347 df.description = "Probability function of note onset likelihood";
c@27 348 df.unit = "";
c@27 349 df.hasFixedBinCount = true;
c@27 350 df.binCount = 1;
c@27 351 df.hasKnownExtents = false;
c@27 352 df.isQuantized = false;
c@27 353 df.sampleType = OutputDescriptor::OneSamplePerStep;
c@27 354
c@27 355 OutputDescriptor tempo;
c@27 356 tempo.identifier = "tempo";
c@27 357 tempo.name = "Tempo";
c@27 358 tempo.description = "Locked tempo estimates";
c@27 359 tempo.unit = "bpm";
c@27 360 tempo.hasFixedBinCount = true;
c@27 361 tempo.binCount = 1;
c@31 362 tempo.hasKnownExtents = false;
c@31 363 tempo.isQuantized = false;
c@27 364 tempo.sampleType = OutputDescriptor::VariableSampleRate;
c@27 365 tempo.sampleRate = 1.0 / m_stepSecs;
c@27 366
c@27 367 list.push_back(beat);
c@27 368 list.push_back(df);
c@27 369 list.push_back(tempo);
c@27 370
c@27 371 return list;
c@27 372 }
c@27 373
c@27 374 BeatTracker::FeatureSet
c@27 375 BeatTracker::process(const float *const *inputBuffers,
c@85 376 Vamp::RealTime timestamp)
c@27 377 {
c@27 378 if (!m_d) {
luis@144 379 cerr << "ERROR: BeatTracker::process: "
luis@144 380 << "BeatTracker has not been initialised"
luis@144 381 << endl;
luis@144 382 return FeatureSet();
c@27 383 }
c@27 384
c@27 385 size_t len = m_d->dfConfig.frameLength / 2;
c@27 386
c@27 387 double *magnitudes = new double[len];
c@27 388 double *phases = new double[len];
c@27 389
c@27 390 // We only support a single input channel
c@27 391
c@27 392 for (size_t i = 0; i < len; ++i) {
c@27 393
c@27 394 magnitudes[i] = sqrt(inputBuffers[0][i*2 ] * inputBuffers[0][i*2 ] +
c@27 395 inputBuffers[0][i*2+1] * inputBuffers[0][i*2+1]);
c@27 396
luis@144 397 phases[i] = atan2(-inputBuffers[0][i*2+1], inputBuffers[0][i*2]);
c@27 398 }
c@27 399
c@27 400 double output = m_d->df->process(magnitudes, phases);
c@27 401
c@27 402 delete[] magnitudes;
c@27 403 delete[] phases;
c@27 404
c@85 405 if (m_d->dfOutput.empty()) m_d->origin = timestamp;
c@85 406
c@27 407 m_d->dfOutput.push_back(output);
c@27 408
c@27 409 FeatureSet returnFeatures;
c@27 410
c@27 411 Feature feature;
c@27 412 feature.hasTimestamp = false;
c@27 413 feature.values.push_back(output);
c@27 414
c@27 415 returnFeatures[1].push_back(feature); // detection function is output 1
c@27 416 return returnFeatures;
c@27 417 }
c@27 418
c@27 419 BeatTracker::FeatureSet
c@27 420 BeatTracker::getRemainingFeatures()
c@27 421 {
c@27 422 if (!m_d) {
luis@144 423 cerr << "ERROR: BeatTracker::getRemainingFeatures: "
luis@144 424 << "BeatTracker has not been initialised"
luis@144 425 << endl;
luis@144 426 return FeatureSet();
c@27 427 }
c@27 428
c@86 429 if (m_method == METHOD_OLD) return beatTrackOld();
c@86 430 else return beatTrackNew();
c@86 431 }
c@86 432
c@86 433 BeatTracker::FeatureSet
c@86 434 BeatTracker::beatTrackOld()
c@86 435 {
c@27 436 double aCoeffs[] = { 1.0000, -0.5949, 0.2348 };
c@27 437 double bCoeffs[] = { 0.1600, 0.3200, 0.1600 };
c@27 438
c@27 439 TTParams ttParams;
c@27 440 ttParams.winLength = 512;
c@27 441 ttParams.lagLength = 128;
c@27 442 ttParams.LPOrd = 2;
c@27 443 ttParams.LPACoeffs = aCoeffs;
c@27 444 ttParams.LPBCoeffs = bCoeffs;
c@27 445 ttParams.alpha = 9;
c@27 446 ttParams.WinT.post = 8;
c@27 447 ttParams.WinT.pre = 7;
c@27 448
c@27 449 TempoTrack tempoTracker(ttParams);
c@27 450
c@87 451 vector<double> tempi;
c@87 452 vector<int> beats = tempoTracker.process(m_d->dfOutput, &tempi);
c@27 453
c@27 454 FeatureSet returnFeatures;
c@27 455
c@27 456 char label[100];
c@27 457
c@27 458 for (size_t i = 0; i < beats.size(); ++i) {
c@27 459
luis@144 460 size_t frame = beats[i] * m_d->dfConfig.stepSize;
c@27 461
luis@144 462 Feature feature;
luis@144 463 feature.hasTimestamp = true;
luis@144 464 feature.timestamp = m_d->origin + Vamp::RealTime::frame2RealTime
luis@144 465 (frame, lrintf(m_inputSampleRate));
c@27 466
luis@144 467 float bpm = 0.0;
luis@144 468 int frameIncrement = 0;
c@27 469
luis@144 470 if (i < beats.size() - 1) {
c@27 471
luis@144 472 frameIncrement = (beats[i+1] - beats[i]) * m_d->dfConfig.stepSize;
c@27 473
luis@144 474 // one beat is frameIncrement frames, so there are
luis@144 475 // samplerate/frameIncrement bps, so
luis@144 476 // 60*samplerate/frameIncrement bpm
c@27 477
luis@144 478 if (frameIncrement > 0) {
luis@144 479 bpm = (60.0 * m_inputSampleRate) / frameIncrement;
luis@144 480 bpm = int(bpm * 100.0 + 0.5) / 100.0;
c@27 481 sprintf(label, "%.2f bpm", bpm);
c@27 482 feature.label = label;
luis@144 483 }
luis@144 484 }
c@27 485
luis@144 486 returnFeatures[0].push_back(feature); // beats are output 0
c@27 487 }
c@27 488
c@27 489 double prevTempo = 0.0;
c@27 490
c@87 491 for (size_t i = 0; i < tempi.size(); ++i) {
c@27 492
c@27 493 size_t frame = i * m_d->dfConfig.stepSize * ttParams.lagLength;
c@27 494
c@27 495 // std::cerr << "unit " << i << ", step size " << m_d->dfConfig.stepSize << ", hop " << ttParams.lagLength << ", frame = " << frame << std::endl;
luis@144 496
c@87 497 if (tempi[i] > 1 && int(tempi[i] * 100) != int(prevTempo * 100)) {
c@27 498 Feature feature;
c@27 499 feature.hasTimestamp = true;
c@85 500 feature.timestamp = m_d->origin + Vamp::RealTime::frame2RealTime
c@27 501 (frame, lrintf(m_inputSampleRate));
c@87 502 feature.values.push_back(tempi[i]);
c@87 503 sprintf(label, "%.2f bpm", tempi[i]);
c@27 504 feature.label = label;
c@27 505 returnFeatures[2].push_back(feature); // tempo is output 2
c@87 506 prevTempo = tempi[i];
c@27 507 }
c@27 508 }
c@27 509
c@27 510 return returnFeatures;
c@27 511 }
c@27 512
c@86 513 BeatTracker::FeatureSet
c@86 514 BeatTracker::beatTrackNew()
c@86 515 {
c@86 516 vector<double> df;
c@86 517 vector<double> beatPeriod;
c@87 518 vector<double> tempi;
c@86 519
c@120 520 size_t nonZeroCount = m_d->dfOutput.size();
c@120 521 while (nonZeroCount > 0) {
c@120 522 if (m_d->dfOutput[nonZeroCount-1] > 0.0) {
c@120 523 break;
c@120 524 }
c@120 525 --nonZeroCount;
c@120 526 }
c@120 527
c@120 528 std::cerr << "Note: nonZeroCount was " << m_d->dfOutput.size() << ", is now " << nonZeroCount << std::endl;
c@120 529
c@120 530 for (size_t i = 2; i < nonZeroCount; ++i) { // discard first two elts
c@86 531 df.push_back(m_d->dfOutput[i]);
c@86 532 beatPeriod.push_back(0.0);
c@86 533 }
c@86 534 if (df.empty()) return FeatureSet();
c@86 535
c@88 536 TempoTrackV2 tt(m_inputSampleRate, m_d->dfConfig.stepSize);
c@86 537
luis@144 538
luis@144 539 // MEPD - note this function is now passed 2 new parameters, m_inputtempo and m_constraintempo
luis@144 540 tt.calculateBeatPeriod(df, beatPeriod, tempi, m_inputtempo, m_constraintempo);
c@86 541
c@86 542 vector<double> beats;
luis@144 543
luis@144 544 // MEPD - note this function is now passed 2 new parameters, m_alpha and m_tightness
luis@144 545 tt.calculateBeats(df, beatPeriod, beats, m_alpha, m_tightness);
luis@144 546
c@86 547 FeatureSet returnFeatures;
c@86 548
c@86 549 char label[100];
c@86 550
c@86 551 for (size_t i = 0; i < beats.size(); ++i) {
c@86 552
luis@144 553 size_t frame = beats[i] * m_d->dfConfig.stepSize;
c@86 554
luis@144 555 Feature feature;
luis@144 556 feature.hasTimestamp = true;
luis@144 557 feature.timestamp = m_d->origin + Vamp::RealTime::frame2RealTime
luis@144 558 (frame, lrintf(m_inputSampleRate));
c@86 559
luis@144 560 float bpm = 0.0;
luis@144 561 int frameIncrement = 0;
c@86 562
luis@144 563 if (i+1 < beats.size()) {
c@86 564
luis@144 565 frameIncrement = (beats[i+1] - beats[i]) * m_d->dfConfig.stepSize;
c@86 566
luis@144 567 // one beat is frameIncrement frames, so there are
luis@144 568 // samplerate/frameIncrement bps, so
luis@144 569 // 60*samplerate/frameIncrement bpm
luis@144 570
luis@144 571 if (frameIncrement > 0) {
luis@144 572 bpm = (60.0 * m_inputSampleRate) / frameIncrement;
luis@144 573 bpm = int(bpm * 100.0 + 0.5) / 100.0;
c@86 574 sprintf(label, "%.2f bpm", bpm);
c@86 575 feature.label = label;
luis@144 576 }
luis@144 577 }
c@86 578
luis@144 579 returnFeatures[0].push_back(feature); // beats are output 0
c@86 580 }
c@86 581
c@87 582 double prevTempo = 0.0;
c@87 583
c@87 584 for (size_t i = 0; i < tempi.size(); ++i) {
c@87 585
luis@144 586 size_t frame = i * m_d->dfConfig.stepSize;
luis@144 587
c@87 588 if (tempi[i] > 1 && int(tempi[i] * 100) != int(prevTempo * 100)) {
c@87 589 Feature feature;
c@87 590 feature.hasTimestamp = true;
c@87 591 feature.timestamp = m_d->origin + Vamp::RealTime::frame2RealTime
c@87 592 (frame, lrintf(m_inputSampleRate));
c@87 593 feature.values.push_back(tempi[i]);
c@87 594 sprintf(label, "%.2f bpm", tempi[i]);
c@87 595 feature.label = label;
c@87 596 returnFeatures[2].push_back(feature); // tempo is output 2
c@87 597 prevTempo = tempi[i];
c@87 598 }
c@87 599 }
c@87 600
c@86 601 return returnFeatures;
c@86 602 }