Mercurial > hg > qm-vamp-plugins
comparison plugins/BeatTrack.cpp @ 146:c4837ed2eeb1
Merge mepd_new_params branch
author | Chris Cannam <c.cannam@qmul.ac.uk> |
---|---|
date | Mon, 02 Sep 2013 09:29:34 +0100 |
parents | edad8a88a074 |
children | d169df0c0cbc |
comparison
equal
deleted
inserted
replaced
143:592ac92002a8 | 146:c4837ed2eeb1 |
---|---|
31 | 31 |
32 class BeatTrackerData | 32 class BeatTrackerData |
33 { | 33 { |
34 public: | 34 public: |
35 BeatTrackerData(const DFConfig &config) : dfConfig(config) { | 35 BeatTrackerData(const DFConfig &config) : dfConfig(config) { |
36 df = new DetectionFunction(config); | 36 df = new DetectionFunction(config); |
37 } | 37 } |
38 ~BeatTrackerData() { | 38 ~BeatTrackerData() { |
39 delete df; | 39 delete df; |
40 } | 40 } |
41 void reset() { | 41 void reset() { |
42 delete df; | 42 delete df; |
43 df = new DetectionFunction(dfConfig); | 43 df = new DetectionFunction(dfConfig); |
44 dfOutput.clear(); | 44 dfOutput.clear(); |
45 origin = Vamp::RealTime::zeroTime; | 45 origin = Vamp::RealTime::zeroTime; |
46 } | 46 } |
47 | 47 |
48 DFConfig dfConfig; | 48 DFConfig dfConfig; |
49 DetectionFunction *df; | 49 DetectionFunction *df; |
50 vector<double> dfOutput; | 50 vector<double> dfOutput; |
51 Vamp::RealTime origin; | 51 Vamp::RealTime origin; |
52 }; | 52 }; |
53 | 53 |
54 | 54 |
55 BeatTracker::BeatTracker(float inputSampleRate) : | 55 BeatTracker::BeatTracker(float inputSampleRate) : |
56 Vamp::Plugin(inputSampleRate), | 56 Vamp::Plugin(inputSampleRate), |
57 m_d(0), | 57 m_d(0), |
58 m_method(METHOD_NEW), | 58 m_method(METHOD_NEW), |
59 m_dfType(DF_COMPLEXSD), | 59 m_dfType(DF_COMPLEXSD), |
60 m_whiten(false) | 60 m_whiten(false), |
61 m_alpha(0.9), // MEPD new exposed parameter for beat tracker, default value = 0.9 (as old version) | |
62 m_tightness(4.), // MEPD new exposed parameter for beat tracker, default value = 4. (as old version) | |
63 m_inputtempo(120.), // MEPD new exposed parameter for beat tracker, default value = 120. (as old version) | |
64 m_constraintempo(false) // MEPD new exposed parameter for beat tracker, default value = false (as old version) | |
65 // calling the beat tracker with these default parameters will give the same output as the previous existing version | |
66 | |
61 { | 67 { |
62 } | 68 } |
63 | 69 |
64 BeatTracker::~BeatTracker() | 70 BeatTracker::~BeatTracker() |
65 { | 71 { |
91 } | 97 } |
92 | 98 |
93 int | 99 int |
94 BeatTracker::getPluginVersion() const | 100 BeatTracker::getPluginVersion() const |
95 { | 101 { |
96 return 5; | 102 return 6; |
97 } | 103 } |
98 | 104 |
99 string | 105 string |
100 BeatTracker::getCopyright() const | 106 BeatTracker::getCopyright() const |
101 { | 107 { |
102 return "Plugin by Christian Landone and Matthew Davies. Copyright (c) 2006-2009 QMUL - All Rights Reserved"; | 108 return "Plugin by Christian Landone and Matthew Davies. Copyright (c) 2006-2012 QMUL - All Rights Reserved"; |
103 } | 109 } |
104 | 110 |
105 BeatTracker::ParameterList | 111 BeatTracker::ParameterList |
106 BeatTracker::getParameterDescriptors() const | 112 BeatTracker::getParameterDescriptors() const |
107 { | 113 { |
145 desc.quantizeStep = 1; | 151 desc.quantizeStep = 1; |
146 desc.unit = ""; | 152 desc.unit = ""; |
147 desc.valueNames.clear(); | 153 desc.valueNames.clear(); |
148 list.push_back(desc); | 154 list.push_back(desc); |
149 | 155 |
156 // MEPD new exposed parameter - used in the dynamic programming part of the beat tracker | |
157 //Alpha Parameter of Beat Tracker | |
158 desc.identifier = "alpha"; | |
159 desc.name = "Alpha"; | |
160 desc.description = "Inertia - Flexibility Trade Off"; | |
161 desc.minValue = 0.1; | |
162 desc.maxValue = 0.99; | |
163 desc.defaultValue = 0.90; | |
164 desc.unit = ""; | |
165 desc.isQuantized = false; | |
166 list.push_back(desc); | |
167 | |
168 | |
169 // MEPD new exposed parameter - used in the dynamic programming part of the beat tracker | |
170 //Tightness Parameter of Beat Tracker | |
171 desc.identifier = "tightness"; | |
172 desc.name = "Tightness"; | |
173 desc.description = "Inertia - Flexibility Trade Off 2"; | |
174 desc.minValue = 3; | |
175 desc.maxValue = 7; | |
176 desc.defaultValue = 4; | |
177 desc.unit = ""; | |
178 desc.isQuantized = true; | |
179 list.push_back(desc); | |
180 | |
181 // MEPD new exposed parameter - used in the periodicity estimation | |
182 //User input tempo | |
183 desc.identifier = "inputtempo"; | |
184 desc.name = "InputTempo"; | |
185 desc.description = "User defined Tempo"; | |
186 desc.minValue = 50; | |
187 desc.maxValue = 250; | |
188 desc.defaultValue = 120; | |
189 desc.unit = "BPM"; | |
190 desc.isQuantized = true; | |
191 list.push_back(desc); | |
192 | |
193 // MEPD new exposed parameter - used in periodicity estimation | |
194 desc.identifier = "constraintempo"; | |
195 desc.name = "Constrain Tempo"; | |
196 desc.description = "Constrain tempo to use Gaussian weighting"; | |
197 desc.minValue = 0; | |
198 desc.maxValue = 1; | |
199 desc.defaultValue = 0; | |
200 desc.isQuantized = true; | |
201 desc.quantizeStep = 1; | |
202 desc.unit = ""; | |
203 desc.valueNames.clear(); | |
204 list.push_back(desc); | |
205 | |
206 | |
207 | |
150 return list; | 208 return list; |
151 } | 209 } |
152 | 210 |
153 float | 211 float |
154 BeatTracker::getParameter(std::string name) const | 212 BeatTracker::getParameter(std::string name) const |
162 case DF_BROADBAND: return 4; | 220 case DF_BROADBAND: return 4; |
163 } | 221 } |
164 } else if (name == "method") { | 222 } else if (name == "method") { |
165 return m_method; | 223 return m_method; |
166 } else if (name == "whiten") { | 224 } else if (name == "whiten") { |
167 return m_whiten ? 1.0 : 0.0; | 225 return m_whiten ? 1.0 : 0.0; |
226 } else if (name == "alpha") { | |
227 return m_alpha; | |
228 } else if (name == "tightness") { | |
229 return m_tightness; | |
230 } else if (name == "inputtempo") { | |
231 return m_inputtempo; | |
232 } else if (name == "constraintempo") { | |
233 return m_constraintempo ? 1.0 : 0.0; | |
168 } | 234 } |
169 return 0.0; | 235 return 0.0; |
170 } | 236 } |
171 | 237 |
172 void | 238 void |
182 } | 248 } |
183 } else if (name == "method") { | 249 } else if (name == "method") { |
184 m_method = lrintf(value); | 250 m_method = lrintf(value); |
185 } else if (name == "whiten") { | 251 } else if (name == "whiten") { |
186 m_whiten = (value > 0.5); | 252 m_whiten = (value > 0.5); |
253 } else if (name == "alpha") { | |
254 m_alpha = value; | |
255 } else if (name == "tightness") { | |
256 m_tightness = value; | |
257 } else if (name == "inputtempo") { | |
258 m_inputtempo = value; | |
259 } else if (name == "constraintempo") { | |
260 m_constraintempo = (value > 0.5); | |
187 } | 261 } |
188 } | 262 } |
189 | 263 |
190 bool | 264 bool |
191 BeatTracker::initialise(size_t channels, size_t stepSize, size_t blockSize) | 265 BeatTracker::initialise(size_t channels, size_t stepSize, size_t blockSize) |
192 { | 266 { |
193 if (m_d) { | 267 if (m_d) { |
194 delete m_d; | 268 delete m_d; |
195 m_d = 0; | 269 m_d = 0; |
196 } | 270 } |
197 | 271 |
198 if (channels < getMinChannelCount() || | 272 if (channels < getMinChannelCount() || |
199 channels > getMaxChannelCount()) { | 273 channels > getMaxChannelCount()) { |
200 std::cerr << "BeatTracker::initialise: Unsupported channel count: " | 274 std::cerr << "BeatTracker::initialise: Unsupported channel count: " |
201 << channels << std::endl; | 275 << channels << std::endl; |
202 return false; | 276 return false; |
203 } | 277 } |
204 | 278 |
220 dfConfig.frameLength = blockSize; | 294 dfConfig.frameLength = blockSize; |
221 dfConfig.dbRise = 3; | 295 dfConfig.dbRise = 3; |
222 dfConfig.adaptiveWhitening = m_whiten; | 296 dfConfig.adaptiveWhitening = m_whiten; |
223 dfConfig.whiteningRelaxCoeff = -1; | 297 dfConfig.whiteningRelaxCoeff = -1; |
224 dfConfig.whiteningFloor = -1; | 298 dfConfig.whiteningFloor = -1; |
225 | 299 |
226 m_d = new BeatTrackerData(dfConfig); | 300 m_d = new BeatTrackerData(dfConfig); |
227 return true; | 301 return true; |
228 } | 302 } |
229 | 303 |
230 void | 304 void |
300 BeatTracker::FeatureSet | 374 BeatTracker::FeatureSet |
301 BeatTracker::process(const float *const *inputBuffers, | 375 BeatTracker::process(const float *const *inputBuffers, |
302 Vamp::RealTime timestamp) | 376 Vamp::RealTime timestamp) |
303 { | 377 { |
304 if (!m_d) { | 378 if (!m_d) { |
305 cerr << "ERROR: BeatTracker::process: " | 379 cerr << "ERROR: BeatTracker::process: " |
306 << "BeatTracker has not been initialised" | 380 << "BeatTracker has not been initialised" |
307 << endl; | 381 << endl; |
308 return FeatureSet(); | 382 return FeatureSet(); |
309 } | 383 } |
310 | 384 |
311 size_t len = m_d->dfConfig.frameLength / 2; | 385 size_t len = m_d->dfConfig.frameLength / 2; |
312 | 386 |
313 double *magnitudes = new double[len]; | 387 double *magnitudes = new double[len]; |
318 for (size_t i = 0; i < len; ++i) { | 392 for (size_t i = 0; i < len; ++i) { |
319 | 393 |
320 magnitudes[i] = sqrt(inputBuffers[0][i*2 ] * inputBuffers[0][i*2 ] + | 394 magnitudes[i] = sqrt(inputBuffers[0][i*2 ] * inputBuffers[0][i*2 ] + |
321 inputBuffers[0][i*2+1] * inputBuffers[0][i*2+1]); | 395 inputBuffers[0][i*2+1] * inputBuffers[0][i*2+1]); |
322 | 396 |
323 phases[i] = atan2(-inputBuffers[0][i*2+1], inputBuffers[0][i*2]); | 397 phases[i] = atan2(-inputBuffers[0][i*2+1], inputBuffers[0][i*2]); |
324 } | 398 } |
325 | 399 |
326 double output = m_d->df->process(magnitudes, phases); | 400 double output = m_d->df->process(magnitudes, phases); |
327 | 401 |
328 delete[] magnitudes; | 402 delete[] magnitudes; |
344 | 418 |
345 BeatTracker::FeatureSet | 419 BeatTracker::FeatureSet |
346 BeatTracker::getRemainingFeatures() | 420 BeatTracker::getRemainingFeatures() |
347 { | 421 { |
348 if (!m_d) { | 422 if (!m_d) { |
349 cerr << "ERROR: BeatTracker::getRemainingFeatures: " | 423 cerr << "ERROR: BeatTracker::getRemainingFeatures: " |
350 << "BeatTracker has not been initialised" | 424 << "BeatTracker has not been initialised" |
351 << endl; | 425 << endl; |
352 return FeatureSet(); | 426 return FeatureSet(); |
353 } | 427 } |
354 | 428 |
355 if (m_method == METHOD_OLD) return beatTrackOld(); | 429 if (m_method == METHOD_OLD) return beatTrackOld(); |
356 else return beatTrackNew(); | 430 else return beatTrackNew(); |
357 } | 431 } |
381 | 455 |
382 char label[100]; | 456 char label[100]; |
383 | 457 |
384 for (size_t i = 0; i < beats.size(); ++i) { | 458 for (size_t i = 0; i < beats.size(); ++i) { |
385 | 459 |
386 size_t frame = beats[i] * m_d->dfConfig.stepSize; | 460 size_t frame = beats[i] * m_d->dfConfig.stepSize; |
387 | 461 |
388 Feature feature; | 462 Feature feature; |
389 feature.hasTimestamp = true; | 463 feature.hasTimestamp = true; |
390 feature.timestamp = m_d->origin + Vamp::RealTime::frame2RealTime | 464 feature.timestamp = m_d->origin + Vamp::RealTime::frame2RealTime |
391 (frame, lrintf(m_inputSampleRate)); | 465 (frame, lrintf(m_inputSampleRate)); |
392 | 466 |
393 float bpm = 0.0; | 467 float bpm = 0.0; |
394 int frameIncrement = 0; | 468 int frameIncrement = 0; |
395 | 469 |
396 if (i < beats.size() - 1) { | 470 if (i < beats.size() - 1) { |
397 | 471 |
398 frameIncrement = (beats[i+1] - beats[i]) * m_d->dfConfig.stepSize; | 472 frameIncrement = (beats[i+1] - beats[i]) * m_d->dfConfig.stepSize; |
399 | 473 |
400 // one beat is frameIncrement frames, so there are | 474 // one beat is frameIncrement frames, so there are |
401 // samplerate/frameIncrement bps, so | 475 // samplerate/frameIncrement bps, so |
402 // 60*samplerate/frameIncrement bpm | 476 // 60*samplerate/frameIncrement bpm |
403 | 477 |
404 if (frameIncrement > 0) { | 478 if (frameIncrement > 0) { |
405 bpm = (60.0 * m_inputSampleRate) / frameIncrement; | 479 bpm = (60.0 * m_inputSampleRate) / frameIncrement; |
406 bpm = int(bpm * 100.0 + 0.5) / 100.0; | 480 bpm = int(bpm * 100.0 + 0.5) / 100.0; |
407 sprintf(label, "%.2f bpm", bpm); | 481 sprintf(label, "%.2f bpm", bpm); |
408 feature.label = label; | 482 feature.label = label; |
409 } | 483 } |
410 } | 484 } |
411 | 485 |
412 returnFeatures[0].push_back(feature); // beats are output 0 | 486 returnFeatures[0].push_back(feature); // beats are output 0 |
413 } | 487 } |
414 | 488 |
415 double prevTempo = 0.0; | 489 double prevTempo = 0.0; |
416 | 490 |
417 for (size_t i = 0; i < tempi.size(); ++i) { | 491 for (size_t i = 0; i < tempi.size(); ++i) { |
418 | 492 |
419 size_t frame = i * m_d->dfConfig.stepSize * ttParams.lagLength; | 493 size_t frame = i * m_d->dfConfig.stepSize * ttParams.lagLength; |
420 | 494 |
421 // std::cerr << "unit " << i << ", step size " << m_d->dfConfig.stepSize << ", hop " << ttParams.lagLength << ", frame = " << frame << std::endl; | 495 // std::cerr << "unit " << i << ", step size " << m_d->dfConfig.stepSize << ", hop " << ttParams.lagLength << ", frame = " << frame << std::endl; |
422 | 496 |
423 if (tempi[i] > 1 && int(tempi[i] * 100) != int(prevTempo * 100)) { | 497 if (tempi[i] > 1 && int(tempi[i] * 100) != int(prevTempo * 100)) { |
424 Feature feature; | 498 Feature feature; |
425 feature.hasTimestamp = true; | 499 feature.hasTimestamp = true; |
426 feature.timestamp = m_d->origin + Vamp::RealTime::frame2RealTime | 500 feature.timestamp = m_d->origin + Vamp::RealTime::frame2RealTime |
427 (frame, lrintf(m_inputSampleRate)); | 501 (frame, lrintf(m_inputSampleRate)); |
459 } | 533 } |
460 if (df.empty()) return FeatureSet(); | 534 if (df.empty()) return FeatureSet(); |
461 | 535 |
462 TempoTrackV2 tt(m_inputSampleRate, m_d->dfConfig.stepSize); | 536 TempoTrackV2 tt(m_inputSampleRate, m_d->dfConfig.stepSize); |
463 | 537 |
464 tt.calculateBeatPeriod(df, beatPeriod, tempi); | 538 |
539 // MEPD - note this function is now passed 2 new parameters, m_inputtempo and m_constraintempo | |
540 tt.calculateBeatPeriod(df, beatPeriod, tempi, m_inputtempo, m_constraintempo); | |
465 | 541 |
466 vector<double> beats; | 542 vector<double> beats; |
467 tt.calculateBeats(df, beatPeriod, beats); | 543 |
468 | 544 // MEPD - note this function is now passed 2 new parameters, m_alpha and m_tightness |
545 tt.calculateBeats(df, beatPeriod, beats, m_alpha, m_tightness); | |
546 | |
469 FeatureSet returnFeatures; | 547 FeatureSet returnFeatures; |
470 | 548 |
471 char label[100]; | 549 char label[100]; |
472 | 550 |
473 for (size_t i = 0; i < beats.size(); ++i) { | 551 for (size_t i = 0; i < beats.size(); ++i) { |
474 | 552 |
475 size_t frame = beats[i] * m_d->dfConfig.stepSize; | 553 size_t frame = beats[i] * m_d->dfConfig.stepSize; |
476 | 554 |
477 Feature feature; | 555 Feature feature; |
478 feature.hasTimestamp = true; | 556 feature.hasTimestamp = true; |
479 feature.timestamp = m_d->origin + Vamp::RealTime::frame2RealTime | 557 feature.timestamp = m_d->origin + Vamp::RealTime::frame2RealTime |
480 (frame, lrintf(m_inputSampleRate)); | 558 (frame, lrintf(m_inputSampleRate)); |
481 | 559 |
482 float bpm = 0.0; | 560 float bpm = 0.0; |
483 int frameIncrement = 0; | 561 int frameIncrement = 0; |
484 | 562 |
485 if (i+1 < beats.size()) { | 563 if (i+1 < beats.size()) { |
486 | 564 |
487 frameIncrement = (beats[i+1] - beats[i]) * m_d->dfConfig.stepSize; | 565 frameIncrement = (beats[i+1] - beats[i]) * m_d->dfConfig.stepSize; |
488 | 566 |
489 // one beat is frameIncrement frames, so there are | 567 // one beat is frameIncrement frames, so there are |
490 // samplerate/frameIncrement bps, so | 568 // samplerate/frameIncrement bps, so |
491 // 60*samplerate/frameIncrement bpm | 569 // 60*samplerate/frameIncrement bpm |
492 | 570 |
493 if (frameIncrement > 0) { | 571 if (frameIncrement > 0) { |
494 bpm = (60.0 * m_inputSampleRate) / frameIncrement; | 572 bpm = (60.0 * m_inputSampleRate) / frameIncrement; |
495 bpm = int(bpm * 100.0 + 0.5) / 100.0; | 573 bpm = int(bpm * 100.0 + 0.5) / 100.0; |
496 sprintf(label, "%.2f bpm", bpm); | 574 sprintf(label, "%.2f bpm", bpm); |
497 feature.label = label; | 575 feature.label = label; |
498 } | 576 } |
499 } | 577 } |
500 | 578 |
501 returnFeatures[0].push_back(feature); // beats are output 0 | 579 returnFeatures[0].push_back(feature); // beats are output 0 |
502 } | 580 } |
503 | 581 |
504 double prevTempo = 0.0; | 582 double prevTempo = 0.0; |
505 | 583 |
506 for (size_t i = 0; i < tempi.size(); ++i) { | 584 for (size_t i = 0; i < tempi.size(); ++i) { |
507 | 585 |
508 size_t frame = i * m_d->dfConfig.stepSize; | 586 size_t frame = i * m_d->dfConfig.stepSize; |
509 | 587 |
510 if (tempi[i] > 1 && int(tempi[i] * 100) != int(prevTempo * 100)) { | 588 if (tempi[i] > 1 && int(tempi[i] * 100) != int(prevTempo * 100)) { |
511 Feature feature; | 589 Feature feature; |
512 feature.hasTimestamp = true; | 590 feature.hasTimestamp = true; |
513 feature.timestamp = m_d->origin + Vamp::RealTime::frame2RealTime | 591 feature.timestamp = m_d->origin + Vamp::RealTime::frame2RealTime |
514 (frame, lrintf(m_inputSampleRate)); | 592 (frame, lrintf(m_inputSampleRate)); |
520 } | 598 } |
521 } | 599 } |
522 | 600 |
523 return returnFeatures; | 601 return returnFeatures; |
524 } | 602 } |
525 |