annotate examples/FixedTempoEstimator.cpp @ 209:0f6616ef0e18

* More tweaks (still incomplete)
author cannam
date Thu, 16 Oct 2008 16:26:00 +0000
parents df55003e8968
children 87b131a54b0a
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@198 49 FixedTempoEstimator::FixedTempoEstimator(float inputSampleRate) :
cannam@198 50 Plugin(inputSampleRate),
cannam@198 51 m_stepSize(0),
cannam@198 52 m_blockSize(0),
cannam@198 53 m_priorMagnitudes(0),
cannam@200 54 m_df(0),
cannam@200 55 m_r(0),
cannam@200 56 m_fr(0),
cannam@204 57 m_t(0),
cannam@200 58 m_n(0)
cannam@198 59 {
cannam@198 60 }
cannam@198 61
cannam@198 62 FixedTempoEstimator::~FixedTempoEstimator()
cannam@198 63 {
cannam@198 64 delete[] m_priorMagnitudes;
cannam@198 65 delete[] m_df;
cannam@200 66 delete[] m_r;
cannam@200 67 delete[] m_fr;
cannam@204 68 delete[] m_t;
cannam@198 69 }
cannam@198 70
cannam@198 71 string
cannam@198 72 FixedTempoEstimator::getIdentifier() const
cannam@198 73 {
cannam@198 74 return "fixedtempo";
cannam@198 75 }
cannam@198 76
cannam@198 77 string
cannam@198 78 FixedTempoEstimator::getName() const
cannam@198 79 {
cannam@198 80 return "Simple Fixed Tempo Estimator";
cannam@198 81 }
cannam@198 82
cannam@198 83 string
cannam@198 84 FixedTempoEstimator::getDescription() const
cannam@198 85 {
cannam@198 86 return "Study a short section of audio and estimate its tempo, assuming the tempo is constant";
cannam@198 87 }
cannam@198 88
cannam@198 89 string
cannam@198 90 FixedTempoEstimator::getMaker() const
cannam@198 91 {
cannam@198 92 return "Vamp SDK Example Plugins";
cannam@198 93 }
cannam@198 94
cannam@198 95 int
cannam@198 96 FixedTempoEstimator::getPluginVersion() const
cannam@198 97 {
cannam@198 98 return 1;
cannam@198 99 }
cannam@198 100
cannam@198 101 string
cannam@198 102 FixedTempoEstimator::getCopyright() const
cannam@198 103 {
cannam@198 104 return "Code copyright 2008 Queen Mary, University of London. Freely redistributable (BSD license)";
cannam@198 105 }
cannam@198 106
cannam@198 107 size_t
cannam@198 108 FixedTempoEstimator::getPreferredStepSize() const
cannam@198 109 {
cannam@207 110 return 64;
cannam@198 111 }
cannam@198 112
cannam@198 113 size_t
cannam@198 114 FixedTempoEstimator::getPreferredBlockSize() const
cannam@198 115 {
cannam@207 116 return 256;
cannam@198 117 }
cannam@198 118
cannam@198 119 bool
cannam@198 120 FixedTempoEstimator::initialise(size_t channels, size_t stepSize, size_t blockSize)
cannam@198 121 {
cannam@198 122 if (channels < getMinChannelCount() ||
cannam@198 123 channels > getMaxChannelCount()) return false;
cannam@198 124
cannam@198 125 m_stepSize = stepSize;
cannam@198 126 m_blockSize = blockSize;
cannam@198 127
cannam@209 128 float dfLengthSecs = 10.f;
cannam@198 129 m_dfsize = (dfLengthSecs * m_inputSampleRate) / m_stepSize;
cannam@198 130
cannam@198 131 m_priorMagnitudes = new float[m_blockSize/2];
cannam@198 132 m_df = new float[m_dfsize];
cannam@198 133
cannam@198 134 for (size_t i = 0; i < m_blockSize/2; ++i) {
cannam@198 135 m_priorMagnitudes[i] = 0.f;
cannam@198 136 }
cannam@198 137 for (size_t i = 0; i < m_dfsize; ++i) {
cannam@198 138 m_df[i] = 0.f;
cannam@198 139 }
cannam@198 140
cannam@198 141 m_n = 0;
cannam@198 142
cannam@198 143 return true;
cannam@198 144 }
cannam@198 145
cannam@198 146 void
cannam@198 147 FixedTempoEstimator::reset()
cannam@198 148 {
cannam@207 149 cerr << "FixedTempoEstimator: reset called" << endl;
cannam@198 150
cannam@198 151 if (!m_priorMagnitudes) return;
cannam@198 152
cannam@207 153 cerr << "FixedTempoEstimator: resetting" << endl;
cannam@198 154
cannam@198 155 for (size_t i = 0; i < m_blockSize/2; ++i) {
cannam@198 156 m_priorMagnitudes[i] = 0.f;
cannam@198 157 }
cannam@198 158 for (size_t i = 0; i < m_dfsize; ++i) {
cannam@198 159 m_df[i] = 0.f;
cannam@198 160 }
cannam@198 161
cannam@200 162 delete[] m_r;
cannam@200 163 m_r = 0;
cannam@200 164
cannam@200 165 delete[] m_fr;
cannam@200 166 m_fr = 0;
cannam@200 167
cannam@204 168 delete[] m_t;
cannam@204 169 m_t = 0;
cannam@204 170
cannam@198 171 m_n = 0;
cannam@198 172
cannam@198 173 m_start = RealTime::zeroTime;
cannam@198 174 m_lasttime = RealTime::zeroTime;
cannam@198 175 }
cannam@198 176
cannam@198 177 FixedTempoEstimator::ParameterList
cannam@198 178 FixedTempoEstimator::getParameterDescriptors() const
cannam@198 179 {
cannam@198 180 ParameterList list;
cannam@198 181 return list;
cannam@198 182 }
cannam@198 183
cannam@198 184 float
cannam@198 185 FixedTempoEstimator::getParameter(std::string id) const
cannam@198 186 {
cannam@198 187 return 0.f;
cannam@198 188 }
cannam@198 189
cannam@198 190 void
cannam@198 191 FixedTempoEstimator::setParameter(std::string id, float value)
cannam@198 192 {
cannam@198 193 }
cannam@198 194
cannam@200 195 static int TempoOutput = 0;
cannam@200 196 static int CandidatesOutput = 1;
cannam@200 197 static int DFOutput = 2;
cannam@200 198 static int ACFOutput = 3;
cannam@200 199 static int FilteredACFOutput = 4;
cannam@200 200
cannam@198 201 FixedTempoEstimator::OutputList
cannam@198 202 FixedTempoEstimator::getOutputDescriptors() const
cannam@198 203 {
cannam@198 204 OutputList list;
cannam@198 205
cannam@198 206 OutputDescriptor d;
cannam@198 207 d.identifier = "tempo";
cannam@198 208 d.name = "Tempo";
cannam@198 209 d.description = "Estimated tempo";
cannam@198 210 d.unit = "bpm";
cannam@198 211 d.hasFixedBinCount = true;
cannam@198 212 d.binCount = 1;
cannam@198 213 d.hasKnownExtents = false;
cannam@198 214 d.isQuantized = false;
cannam@198 215 d.sampleType = OutputDescriptor::VariableSampleRate;
cannam@198 216 d.sampleRate = m_inputSampleRate;
cannam@198 217 d.hasDuration = true; // our returned tempo spans a certain range
cannam@198 218 list.push_back(d);
cannam@198 219
cannam@200 220 d.identifier = "candidates";
cannam@200 221 d.name = "Tempo candidates";
cannam@200 222 d.description = "Possible tempo estimates, one per bin with the most likely in the first bin";
cannam@200 223 d.unit = "bpm";
cannam@200 224 d.hasFixedBinCount = false;
cannam@200 225 list.push_back(d);
cannam@200 226
cannam@198 227 d.identifier = "detectionfunction";
cannam@198 228 d.name = "Detection Function";
cannam@198 229 d.description = "Onset detection function";
cannam@198 230 d.unit = "";
cannam@198 231 d.hasFixedBinCount = 1;
cannam@198 232 d.binCount = 1;
cannam@198 233 d.hasKnownExtents = true;
cannam@198 234 d.minValue = 0.0;
cannam@198 235 d.maxValue = 1.0;
cannam@198 236 d.isQuantized = false;
cannam@198 237 d.quantizeStep = 0.0;
cannam@198 238 d.sampleType = OutputDescriptor::FixedSampleRate;
cannam@198 239 if (m_stepSize) {
cannam@198 240 d.sampleRate = m_inputSampleRate / m_stepSize;
cannam@198 241 } else {
cannam@198 242 d.sampleRate = m_inputSampleRate / (getPreferredBlockSize()/2);
cannam@198 243 }
cannam@198 244 d.hasDuration = false;
cannam@198 245 list.push_back(d);
cannam@198 246
cannam@198 247 d.identifier = "acf";
cannam@198 248 d.name = "Autocorrelation Function";
cannam@198 249 d.description = "Autocorrelation of onset detection function";
cannam@198 250 d.hasKnownExtents = false;
cannam@201 251 d.unit = "r";
cannam@198 252 list.push_back(d);
cannam@198 253
cannam@198 254 d.identifier = "filtered_acf";
cannam@198 255 d.name = "Filtered Autocorrelation";
cannam@198 256 d.description = "Filtered autocorrelation of onset detection function";
cannam@201 257 d.unit = "r";
cannam@198 258 list.push_back(d);
cannam@198 259
cannam@198 260 return list;
cannam@198 261 }
cannam@198 262
cannam@198 263 FixedTempoEstimator::FeatureSet
cannam@198 264 FixedTempoEstimator::process(const float *const *inputBuffers, RealTime ts)
cannam@198 265 {
cannam@198 266 FeatureSet fs;
cannam@198 267
cannam@198 268 if (m_stepSize == 0) {
cannam@198 269 cerr << "ERROR: FixedTempoEstimator::process: "
cannam@198 270 << "FixedTempoEstimator has not been initialised"
cannam@198 271 << endl;
cannam@198 272 return fs;
cannam@198 273 }
cannam@198 274
cannam@207 275 // if (m_n < m_dfsize) cerr << "m_n = " << m_n << endl;
cannam@198 276
cannam@198 277 if (m_n == 0) m_start = ts;
cannam@198 278 m_lasttime = ts;
cannam@198 279
cannam@198 280 if (m_n == m_dfsize) {
cannam@200 281 calculate();
cannam@200 282 fs = assembleFeatures();
cannam@198 283 ++m_n;
cannam@198 284 return fs;
cannam@198 285 }
cannam@198 286
cannam@198 287 if (m_n > m_dfsize) return FeatureSet();
cannam@198 288
cannam@207 289 float value = 0.f;
cannam@207 290
cannam@207 291 bool print = (ts == RealTime::zeroTime);
cannam@198 292
cannam@198 293 for (size_t i = 1; i < m_blockSize/2; ++i) {
cannam@198 294
cannam@198 295 float real = inputBuffers[0][i*2];
cannam@198 296 float imag = inputBuffers[0][i*2 + 1];
cannam@198 297
cannam@198 298 float sqrmag = real * real + imag * imag;
cannam@207 299 value += fabsf(sqrmag - m_priorMagnitudes[i]);
cannam@198 300
cannam@209 301 if (i == 1 && ts == RealTime::zeroTime) {
cannam@209 302 cerr << "First sqrmag: " << sqrmag << ", value = " << value << endl;
cannam@209 303 }
cannam@198 304
cannam@198 305 m_priorMagnitudes[i] = sqrmag;
cannam@198 306 }
cannam@198 307
cannam@207 308 m_df[m_n] = value;
cannam@207 309
cannam@198 310 ++m_n;
cannam@198 311 return fs;
cannam@198 312 }
cannam@198 313
cannam@198 314 FixedTempoEstimator::FeatureSet
cannam@198 315 FixedTempoEstimator::getRemainingFeatures()
cannam@198 316 {
cannam@198 317 FeatureSet fs;
cannam@198 318 if (m_n > m_dfsize) return fs;
cannam@200 319 calculate();
cannam@200 320 fs = assembleFeatures();
cannam@198 321 ++m_n;
cannam@198 322 return fs;
cannam@198 323 }
cannam@198 324
cannam@198 325 float
cannam@199 326 FixedTempoEstimator::lag2tempo(int lag)
cannam@199 327 {
cannam@198 328 return 60.f / ((lag * m_stepSize) / m_inputSampleRate);
cannam@198 329 }
cannam@198 330
cannam@207 331 int
cannam@207 332 FixedTempoEstimator::tempo2lag(float tempo)
cannam@207 333 {
cannam@207 334 return ((60.f / tempo) * m_inputSampleRate) / m_stepSize;
cannam@207 335 }
cannam@207 336
cannam@200 337 void
cannam@200 338 FixedTempoEstimator::calculate()
cannam@200 339 {
cannam@207 340 cerr << "FixedTempoEstimator::calculate: m_n = " << m_n << endl;
cannam@200 341
cannam@200 342 if (m_r) {
cannam@207 343 cerr << "FixedTempoEstimator::calculate: calculation already happened?" << endl;
cannam@200 344 return;
cannam@200 345 }
cannam@200 346
cannam@209 347 if (m_n < m_dfsize / 9) {
cannam@207 348 cerr << "FixedTempoEstimator::calculate: Not enough data to go on (have " << m_n << ", want at least " << m_dfsize/4 << ")" << endl;
cannam@200 349 return; // not enough data (perhaps we should return the duration of the input as the "estimated" beat length?)
cannam@200 350 }
cannam@200 351
cannam@200 352 int n = m_n;
cannam@200 353
cannam@200 354 m_r = new float[n/2];
cannam@200 355 m_fr = new float[n/2];
cannam@204 356 m_t = new float[n/2];
cannam@200 357
cannam@200 358 for (int i = 0; i < n/2; ++i) {
cannam@200 359 m_r[i] = 0.f;
cannam@200 360 m_fr[i] = 0.f;
cannam@207 361 m_t[i] = lag2tempo(i);
cannam@200 362 }
cannam@200 363
cannam@200 364 for (int i = 0; i < n/2; ++i) {
cannam@200 365
cannam@200 366 for (int j = i; j < n-1; ++j) {
cannam@200 367 m_r[i] += m_df[j] * m_df[j - i];
cannam@200 368 }
cannam@200 369
cannam@200 370 m_r[i] /= n - i - 1;
cannam@200 371 }
cannam@209 372 /*
cannam@200 373 for (int i = 1; i < n/2; ++i) {
cannam@200 374
cannam@204 375 float weight = 1.f - fabsf(128.f - lag2tempo(i)) * 0.005;
cannam@204 376 if (weight < 0.f) weight = 0.f;
cannam@204 377 weight = weight * weight;
cannam@204 378
cannam@209 379 cerr << "i = " << i << ": tempo = " << lag2tempo(i) << ", weight = " << weight << ", " << m_r[i] << " -> ";
cannam@204 380
cannam@209 381 m_fr[i] = m_r[i] * weight; //(1 + weight / 2.f);
cannam@209 382
cannam@209 383 cerr << m_fr[i] << endl;
cannam@204 384 }
cannam@209 385 */
cannam@209 386 int related[] = { 2, 3, 4 };
cannam@204 387
cannam@209 388 int e = tempo2lag(50.f);
cannam@209 389 // int universalDiv = (n/2 - 1) / e;
cannam@209 390 // cerr << "universalDiv = " << universalDiv << endl;
cannam@208 391
cannam@209 392 for (int i = 1; i < n/2-1; ++i) {
cannam@204 393
cannam@209 394 float weight = 1.f - fabsf(128.f - lag2tempo(i)) * 0.005;
cannam@209 395 if (weight < 0.f) weight = 0.f;
cannam@209 396 weight = weight * weight;
cannam@204 397
cannam@209 398 // cerr << "i = " << i << ": tempo = " << lag2tempo(i) << ", weight = " << weight << ", " << m_r[i] << " -> ";
cannam@209 399
cannam@209 400 m_fr[i] = m_r[i];
cannam@209 401 // m_fr[i] = m_r[i] * weight; //(1 + weight / 2.f);
cannam@209 402
cannam@209 403 // if (i == 0 || i == n/2 - 1
cannam@208 404 /*
cannam@208 405 ||
cannam@207 406 !(m_fr[i] > m_fr[i-1] &&
cannam@208 407 m_fr[i] >= m_fr[i+1])
cannam@208 408 */
cannam@209 409 // ) {
cannam@209 410 // continue;
cannam@209 411 // }
cannam@204 412
cannam@200 413 int div = 1;
cannam@200 414
cannam@204 415 for (int j = 0; j < sizeof(related)/sizeof(related[0]); ++j) {
cannam@204 416
cannam@207 417 int k0 = i * related[j];
cannam@209 418
cannam@209 419 if (k0 >= 0 && k0 < n/2) {
cannam@204 420
cannam@207 421 int kmax = 0, kmin = 0;
cannam@207 422 float kvmax = 0, kvmin = 0;
cannam@209 423 bool have = false;
cannam@204 424
cannam@209 425 for (int k = k0 - 1; k <= k0 + 1; ++k) {
cannam@204 426
cannam@209 427 if (k < 0 || k >= n/2) continue;
cannam@209 428
cannam@209 429 if (!have || (m_r[k] > kvmax)) {
cannam@207 430 kmax = k;
cannam@207 431 kvmax = m_r[k];
cannam@207 432 }
cannam@209 433
cannam@209 434 if (!have || (m_r[k] < kvmin)) {
cannam@207 435 kmin = k;
cannam@207 436 kvmin = m_r[k];
cannam@204 437 }
cannam@209 438
cannam@209 439 have = true;
cannam@204 440 }
cannam@209 441
cannam@207 442
cannam@209 443 m_fr[i] += m_r[kmax] / 4;
cannam@208 444
cannam@209 445 // if (related[j] <= universalDiv) {
cannam@209 446 // m_fr[i] += m_fr[kmax]; //!!!
cannam@209 447 // m_fr[i] += m_r[kmax] / related[j];
cannam@209 448 // }
cannam@209 449
cannam@209 450 if ((kmax == 0 || m_r[kmax] > m_r[kmax-1]) &&
cannam@209 451 (kmax == n/2-1 || m_r[kmax] > m_r[kmax+1]) &&
cannam@207 452 kvmax > kvmin * 1.05) {
cannam@209 453
cannam@209 454 // cerr << "peak at " << i << " (val " << m_r[i] << ", tempo " << lag2tempo(i) << ") has sympathetic peak at " << kmax << " (val " << m_r[kmax] << " for relative tempo " << lag2tempo(kmax) * related[j] << ")" << endl;
cannam@207 455
cannam@207 456 m_t[i] = m_t[i] + lag2tempo(kmax) * related[j];
cannam@207 457 ++div;
cannam@207 458 }
cannam@204 459 }
cannam@204 460 }
cannam@209 461
cannam@204 462 m_t[i] /= div;
cannam@204 463
cannam@209 464 if (div > 1) {
cannam@209 465 cerr << "adjusting tempo from " << lag2tempo(i) << " to "
cannam@209 466 << m_t[i] << " for fr = " << m_fr[i] << " (div = " << div << ")" << endl;
cannam@209 467 }
cannam@209 468
cannam@209 469 m_fr[i] += m_fr[i] * (weight / 5);
cannam@207 470 }
cannam@207 471
cannam@208 472 /*
cannam@207 473 int e = tempo2lag(60.f);
cannam@207 474 int div = (n/2 - 1) / e;
cannam@207 475
cannam@207 476 // cerr << "e = " << e << ", n/2 = " << n/2 << ", div = " << div << endl;
cannam@207 477 if (div > 1) {
cannam@207 478 for (int j = 2; j <= div && j <= 8; j *= 2) {
cannam@207 479 for (int i = 1; i <= e; ++i) {
cannam@207 480 m_fr[i] += m_fr[i * j] * (1.f / j);
cannam@207 481 }
cannam@204 482 }
cannam@204 483 }
cannam@208 484 */
cannam@207 485 // cerr << "i = " << i << ", (n/2 - 1)/i = " << (n/2 - 1)/i << ", sum = " << m_fr[i] << ", div = " << div << ", val = " << m_fr[i] / div << ", t = " << lag2tempo(i) << endl;
cannam@200 486
cannam@200 487
cannam@204 488 // }
cannam@207 489
cannam@207 490 cerr << "FixedTempoEstimator::calculate done" << endl;
cannam@200 491 }
cannam@200 492
cannam@200 493
cannam@198 494 FixedTempoEstimator::FeatureSet
cannam@200 495 FixedTempoEstimator::assembleFeatures()
cannam@198 496 {
cannam@198 497 FeatureSet fs;
cannam@200 498 if (!m_r) return fs; // No results
cannam@200 499
cannam@198 500 Feature feature;
cannam@198 501 feature.hasTimestamp = true;
cannam@198 502 feature.hasDuration = false;
cannam@198 503 feature.label = "";
cannam@198 504 feature.values.clear();
cannam@198 505 feature.values.push_back(0.f);
cannam@198 506
cannam@200 507 char buffer[40];
cannam@198 508
cannam@198 509 int n = m_n;
cannam@198 510
cannam@198 511 for (int i = 0; i < n; ++i) {
cannam@208 512 feature.timestamp = m_start +
cannam@208 513 RealTime::frame2RealTime(i * m_stepSize, m_inputSampleRate);
cannam@200 514 feature.values[0] = m_df[i];
cannam@198 515 feature.label = "";
cannam@200 516 fs[DFOutput].push_back(feature);
cannam@198 517 }
cannam@198 518
cannam@199 519 for (int i = 1; i < n/2; ++i) {
cannam@208 520 feature.timestamp = m_start +
cannam@208 521 RealTime::frame2RealTime(i * m_stepSize, m_inputSampleRate);
cannam@200 522 feature.values[0] = m_r[i];
cannam@199 523 sprintf(buffer, "%.1f bpm", lag2tempo(i));
cannam@200 524 if (i == n/2-1) feature.label = "";
cannam@200 525 else feature.label = buffer;
cannam@200 526 fs[ACFOutput].push_back(feature);
cannam@198 527 }
cannam@198 528
cannam@209 529 float t0 = 50.f;
cannam@209 530 float t1 = 190.f;
cannam@198 531
cannam@207 532 int p0 = tempo2lag(t1);
cannam@207 533 int p1 = tempo2lag(t0);
cannam@198 534
cannam@207 535 cerr << "p0 = " << p0 << ", p1 = " << p1 << endl;
cannam@198 536
cannam@198 537 int pc = p1 - p0 + 1;
cannam@207 538 // cerr << "pc = " << pc << endl;
cannam@198 539
cannam@200 540 // int maxpi = 0;
cannam@200 541 // float maxp = 0.f;
cannam@198 542
cannam@200 543 std::map<float, int> candidates;
cannam@198 544
cannam@200 545 for (int i = p0; i <= p1 && i < n/2-1; ++i) {
cannam@198 546
cannam@209 547 if (m_fr[i] > m_fr[i-1] &&
cannam@209 548 m_fr[i] > m_fr[i+1]) {
cannam@209 549 candidates[m_fr[i]] = i;
cannam@209 550 }
cannam@198 551
cannam@208 552 feature.timestamp = m_start +
cannam@208 553 RealTime::frame2RealTime(i * m_stepSize, m_inputSampleRate);
cannam@200 554 feature.values[0] = m_fr[i];
cannam@199 555 sprintf(buffer, "%.1f bpm", lag2tempo(i));
cannam@200 556 if (i == p1 || i == n/2-2) feature.label = "";
cannam@200 557 else feature.label = buffer;
cannam@200 558 fs[FilteredACFOutput].push_back(feature);
cannam@198 559 }
cannam@198 560
cannam@207 561 // cerr << "maxpi = " << maxpi << " for tempo " << lag2tempo(maxpi) << " (value = " << maxp << ")" << endl;
cannam@198 562
cannam@200 563 if (candidates.empty()) {
cannam@207 564 cerr << "No tempo candidates!" << endl;
cannam@200 565 return fs;
cannam@200 566 }
cannam@198 567
cannam@198 568 feature.hasTimestamp = true;
cannam@198 569 feature.timestamp = m_start;
cannam@198 570
cannam@198 571 feature.hasDuration = true;
cannam@198 572 feature.duration = m_lasttime - m_start;
cannam@198 573
cannam@200 574 std::map<float, int>::const_iterator ci = candidates.end();
cannam@200 575 --ci;
cannam@200 576 int maxpi = ci->second;
cannam@198 577
cannam@204 578 if (m_t[maxpi] > 0) {
cannam@207 579 cerr << "*** Using adjusted tempo " << m_t[maxpi] << " instead of lag tempo " << lag2tempo(maxpi) << endl;
cannam@204 580 feature.values[0] = m_t[maxpi];
cannam@204 581 } else {
cannam@204 582 // shouldn't happen -- it would imply that this high value was not a peak!
cannam@204 583 feature.values[0] = lag2tempo(maxpi);
cannam@207 584 cerr << "WARNING: No stored tempo for index " << maxpi << endl;
cannam@204 585 }
cannam@204 586
cannam@204 587 sprintf(buffer, "%.1f bpm", feature.values[0]);
cannam@199 588 feature.label = buffer;
cannam@199 589
cannam@200 590 fs[TempoOutput].push_back(feature);
cannam@198 591
cannam@200 592 feature.values.clear();
cannam@200 593 feature.label = "";
cannam@200 594
cannam@200 595 while (feature.values.size() < 8) {
cannam@207 596 if (m_t[ci->second] > 0) {
cannam@207 597 feature.values.push_back(m_t[ci->second]);
cannam@207 598 } else {
cannam@207 599 feature.values.push_back(lag2tempo(ci->second));
cannam@207 600 }
cannam@200 601 if (ci == candidates.begin()) break;
cannam@200 602 --ci;
cannam@200 603 }
cannam@200 604
cannam@200 605 fs[CandidatesOutput].push_back(feature);
cannam@200 606
cannam@198 607 return fs;
cannam@198 608 }