annotate TempogramPlugin.cpp @ 47:cb79f27f0748

Clamp cyclic tempogram min and max BPM to the BPM equivalents of the actual underlying min and max tempogram bins, rather than the "input" min and max BPM (fixed root cause of #1054)
author Chris Cannam
date Mon, 29 Sep 2014 16:20:16 +0100
parents eafb3a7f80fb
children 8c151a9ca202
rev   line source
Chris@43 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
c@0 2
Chris@43 3 /*
Chris@43 4 Vamp Tempogram Plugin
Chris@43 5 Carl Bussey, Centre for Digital Music, Queen Mary University of London
Chris@43 6 Copyright 2014 Queen Mary University of London.
Chris@43 7
Chris@43 8 This program is free software; you can redistribute it and/or
Chris@43 9 modify it under the terms of the GNU General Public License as
Chris@43 10 published by the Free Software Foundation; either version 2 of the
Chris@43 11 License, or (at your option) any later version. See the file
Chris@43 12 COPYING included with this distribution for more information.
Chris@43 13 */
c@0 14
c@14 15 #include "TempogramPlugin.h"
c@25 16
c@0 17 using Vamp::FFT;
c@7 18 using Vamp::RealTime;
c@0 19 using namespace std;
c@0 20
c@14 21 TempogramPlugin::TempogramPlugin(float inputSampleRate) :
c@0 22 Plugin(inputSampleRate),
c@18 23 m_inputBlockSize(0), //host parameter
c@18 24 m_inputStepSize(0), //host parameter
c@29 25 m_noveltyCurveMinDB(-74), //parameter
c@29 26 m_noveltyCurveMinV(0), //set in initialise()
c@18 27 m_noveltyCurveCompressionConstant(1000), //parameter
c@18 28 m_tempogramLog2WindowLength(10), //parameter
c@29 29 m_tempogramWindowLength(0), //set in initialise()
c@18 30 m_tempogramLog2FftLength(m_tempogramLog2WindowLength), //parameter
c@29 31 m_tempogramFftLength(0), //set in initialise()
c@18 32 m_tempogramLog2HopSize(6), //parameter
c@29 33 m_tempogramHopSize(0), //set in initialise()
c@18 34 m_tempogramMinBPM(30), //parameter
c@18 35 m_tempogramMaxBPM(480), //parameter
c@18 36 m_tempogramMinBin(0), //set in initialise()
c@18 37 m_tempogramMaxBin(0), //set in initialise()
c@29 38 m_tempogramMinLag(0), //set in initialise()
c@29 39 m_tempogramMaxLag(0), //set in initialise()
c@18 40 m_cyclicTempogramMinBPM(30), //reset in initialise()
c@18 41 m_cyclicTempogramNumberOfOctaves(0), //set in initialise()
c@18 42 m_cyclicTempogramOctaveDivider(30) //parameter
c@0 43
c@0 44 // Also be sure to set your plugin parameters (presumably stored
c@0 45 // in member variables) to their default values here -- the host
c@0 46 // will not do that for you
c@0 47 {
c@0 48 }
c@0 49
c@14 50 TempogramPlugin::~TempogramPlugin()
c@0 51 {
c@0 52 //delete stuff
c@0 53 }
c@0 54
c@0 55 string
c@14 56 TempogramPlugin::getIdentifier() const
c@0 57 {
c@0 58 return "tempogram";
c@0 59 }
c@0 60
c@0 61 string
c@14 62 TempogramPlugin::getName() const
c@0 63 {
c@0 64 return "Tempogram";
c@0 65 }
c@0 66
c@0 67 string
c@14 68 TempogramPlugin::getDescription() const
c@0 69 {
c@0 70 return "Cyclic Tempogram as described by Peter Grosche and Meinard Muller";
c@0 71 }
c@0 72
c@0 73 string
c@14 74 TempogramPlugin::getMaker() const
c@0 75 {
c@0 76 return "Carl Bussey";
c@0 77 }
c@0 78
c@0 79 int
c@14 80 TempogramPlugin::getPluginVersion() const
c@0 81 {
c@0 82 return 1;
c@0 83 }
c@0 84
c@0 85 string
c@14 86 TempogramPlugin::getCopyright() const
c@0 87 {
Chris@40 88 return "Copyright 2014 Queen Mary University of London. GPL licence.";
c@0 89 }
c@0 90
c@14 91 TempogramPlugin::InputDomain
c@14 92 TempogramPlugin::getInputDomain() const
c@0 93 {
c@0 94 return FrequencyDomain;
c@0 95 }
c@0 96
c@0 97 size_t
c@14 98 TempogramPlugin::getPreferredBlockSize() const
c@0 99 {
c@9 100 return 2048; // 0 means "I can handle any block size"
c@0 101 }
c@0 102
c@0 103 size_t
c@14 104 TempogramPlugin::getPreferredStepSize() const
c@0 105 {
c@9 106 return 1024; // 0 means "anything sensible"; in practice this
c@0 107 // means the same as the block size for TimeDomain
c@0 108 // plugins, or half of it for FrequencyDomain plugins
c@0 109 }
c@0 110
c@0 111 size_t
c@14 112 TempogramPlugin::getMinChannelCount() const
c@0 113 {
c@0 114 return 1;
c@0 115 }
c@0 116
c@0 117 size_t
c@14 118 TempogramPlugin::getMaxChannelCount() const
c@0 119 {
c@0 120 return 1;
c@0 121 }
c@0 122
c@14 123 TempogramPlugin::ParameterList
c@14 124 TempogramPlugin::getParameterDescriptors() const
c@0 125 {
c@0 126 ParameterList list;
c@0 127
c@0 128 // If the plugin has no adjustable parameters, return an empty
c@0 129 // list here (and there's no need to provide implementations of
c@0 130 // getParameter and setParameter in that case either).
c@0 131
c@0 132 // Note that it is your responsibility to make sure the parameters
c@0 133 // start off having their default values (e.g. in the constructor
c@0 134 // above). The host needs to know the default value so it can do
c@0 135 // things like provide a "reset to default" function, but it will
c@0 136 // not explicitly set your parameters to their defaults for you if
c@0 137 // they have not changed in the mean time.
c@0 138
c@14 139 ParameterDescriptor d1;
c@14 140 d1.identifier = "C";
c@15 141 d1.name = "Novelty Curve Spectrogram Compression Constant";
c@14 142 d1.description = "Spectrogram compression constant, C, used when retrieving the novelty curve from the audio.";
c@14 143 d1.unit = "";
c@14 144 d1.minValue = 2;
c@14 145 d1.maxValue = 10000;
c@14 146 d1.defaultValue = 1000;
c@14 147 d1.isQuantized = false;
c@14 148 list.push_back(d1);
c@29 149
c@29 150 ParameterDescriptor d2;
c@29 151 d2.identifier = "minDB";
c@29 152 d2.name = "Novelty Curve Minimum DB";
c@29 153 d2.description = "Spectrogram minimum DB used when removing unwanted peaks in the Spectrogram when retrieving the novelty curve from the audio.";
c@29 154 d2.unit = "";
c@29 155 d2.minValue = -100;
c@29 156 d2.maxValue = -50;
c@29 157 d2.defaultValue = -74;
c@29 158 d2.isQuantized = false;
c@29 159 list.push_back(d2);
c@9 160
c@14 161 ParameterDescriptor d3;
c@29 162 d3.identifier = "log2TN";
c@29 163 d3.name = "Tempogram Window Length";
c@29 164 d3.description = "FFT window length when analysing the novelty curve and extracting the tempogram time-frequency function.";
c@14 165 d3.unit = "";
c@29 166 d3.minValue = 7;
c@14 167 d3.maxValue = 12;
c@29 168 d3.defaultValue = 10;
c@14 169 d3.isQuantized = true;
c@14 170 d3.quantizeStep = 1;
c@14 171 for (int i = d3.minValue; i <= d3.maxValue; i++){
c@14 172 d3.valueNames.push_back(floatToString(pow((float)2,(float)i)));
c@14 173 }
c@14 174 list.push_back(d3);
c@9 175
c@14 176 ParameterDescriptor d4;
c@29 177 d4.identifier = "log2HopSize";
c@29 178 d4.name = "Tempogram Hopsize";
c@29 179 d4.description = "FFT hopsize when analysing the novelty curve and extracting the tempogram time-frequency function.";
c@14 180 d4.unit = "";
c@14 181 d4.minValue = 6;
c@14 182 d4.maxValue = 12;
c@29 183 d4.defaultValue = 6;
c@14 184 d4.isQuantized = true;
c@14 185 d4.quantizeStep = 1;
c@14 186 for (int i = d4.minValue; i <= d4.maxValue; i++){
c@14 187 d4.valueNames.push_back(floatToString(pow((float)2,(float)i)));
c@14 188 }
c@14 189 list.push_back(d4);
c@14 190
c@14 191 ParameterDescriptor d5;
c@29 192 d5.identifier = "log2FftLength";
c@29 193 d5.name = "Tempogram FFT Length";
c@29 194 d5.description = "FFT length when analysing the novelty curve and extracting the tempogram time-frequency function. This parameter determines the amount of zero padding.";
c@14 195 d5.unit = "";
c@29 196 d5.minValue = 6;
c@29 197 d5.maxValue = 12;
Chris@42 198 d5.defaultValue = 10;
c@14 199 d5.isQuantized = true;
c@29 200 d5.quantizeStep = 1;
c@29 201 for (int i = d5.minValue; i <= d5.maxValue; i++){
Chris@41 202 d5.valueNames.push_back(floatToString(pow((float)2,(float)i)));
c@29 203 }
c@14 204 list.push_back(d5);
c@14 205
c@14 206 ParameterDescriptor d6;
c@29 207 d6.identifier = "minBPM";
c@29 208 d6.name = "(Cyclic) Tempogram Minimum BPM";
c@29 209 d6.description = "The minimum BPM of the tempogram output bins.";
c@14 210 d6.unit = "";
c@29 211 d6.minValue = 0;
c@14 212 d6.maxValue = 2000;
c@29 213 d6.defaultValue = 30;
c@14 214 d6.isQuantized = true;
c@14 215 d6.quantizeStep = 5;
c@14 216 list.push_back(d6);
c@18 217
c@18 218 ParameterDescriptor d7;
c@29 219 d7.identifier = "maxBPM";
c@29 220 d7.name = "(Cyclic) Tempogram Maximum BPM";
c@29 221 d7.description = "The maximum BPM of the tempogram output bins.";
c@18 222 d7.unit = "";
c@29 223 d7.minValue = 30;
c@29 224 d7.maxValue = 2000;
c@29 225 d7.defaultValue = 480;
c@18 226 d7.isQuantized = true;
c@29 227 d7.quantizeStep = 5;
c@18 228 list.push_back(d7);
c@29 229
c@29 230 ParameterDescriptor d8;
c@29 231 d8.identifier = "octDiv";
c@29 232 d8.name = "Cyclic Tempogram Octave Divider";
c@29 233 d8.description = "The number bins within each octave.";
c@29 234 d8.unit = "";
c@29 235 d8.minValue = 5;
c@29 236 d8.maxValue = 60;
c@29 237 d8.defaultValue = 30;
c@29 238 d8.isQuantized = true;
c@29 239 d8.quantizeStep = 1;
c@29 240 list.push_back(d8);
c@0 241
c@0 242 return list;
c@0 243 }
c@0 244
c@0 245 float
c@14 246 TempogramPlugin::getParameter(string identifier) const
c@0 247 {
c@0 248 if (identifier == "C") {
c@18 249 return m_noveltyCurveCompressionConstant; // return the ACTUAL current value of your parameter here!
c@0 250 }
c@29 251 else if (identifier == "minDB"){
c@29 252 return m_noveltyCurveMinDB;
c@29 253 }
c@14 254 else if (identifier == "log2TN"){
c@18 255 return m_tempogramLog2WindowLength;
c@9 256 }
c@14 257 else if (identifier == "log2HopSize"){
c@18 258 return m_tempogramLog2HopSize;
c@14 259 }
c@14 260 else if (identifier == "log2FftLength"){
c@18 261 return m_tempogramLog2FftLength;
c@14 262 }
c@14 263 else if (identifier == "minBPM") {
c@18 264 return m_tempogramMinBPM;
c@9 265 }
c@14 266 else if (identifier == "maxBPM"){
c@18 267 return m_tempogramMaxBPM;
c@18 268 }
c@18 269 else if (identifier == "octDiv"){
c@18 270 return m_cyclicTempogramOctaveDivider;
c@0 271 }
c@0 272
c@0 273 return 0;
c@0 274 }
c@0 275
c@0 276 void
c@14 277 TempogramPlugin::setParameter(string identifier, float value)
c@0 278 {
c@9 279
c@0 280 if (identifier == "C") {
c@18 281 m_noveltyCurveCompressionConstant = value; // set the actual value of your parameter
c@0 282 }
c@29 283 else if (identifier == "minDB"){
c@29 284 m_noveltyCurveMinDB = value;
c@29 285 }
c@14 286 else if (identifier == "log2TN") {
c@18 287 m_tempogramLog2WindowLength = value;
c@0 288 }
c@14 289 else if (identifier == "log2HopSize"){
c@30 290 m_tempogramLog2HopSize = value;
c@14 291 }
c@18 292 else if (identifier == "log2FftLength"){
c@30 293 m_tempogramLog2FftLength = value;
c@14 294 }
c@14 295 else if (identifier == "minBPM") {
c@18 296 m_tempogramMinBPM = value;
c@9 297 }
c@14 298 else if (identifier == "maxBPM"){
c@18 299 m_tempogramMaxBPM = value;
c@18 300 }
c@18 301 else if (identifier == "octDiv"){
c@18 302 m_cyclicTempogramOctaveDivider = value;
c@9 303 }
c@9 304
c@9 305 }
c@9 306
c@14 307 TempogramPlugin::ProgramList
c@14 308 TempogramPlugin::getPrograms() const
c@0 309 {
c@0 310 ProgramList list;
c@0 311
c@0 312 // If you have no programs, return an empty list (or simply don't
c@0 313 // implement this function or getCurrentProgram/selectProgram)
c@0 314
c@0 315 return list;
c@0 316 }
c@0 317
c@0 318 string
c@14 319 TempogramPlugin::getCurrentProgram() const
c@0 320 {
c@0 321 return ""; // no programs
c@0 322 }
c@0 323
c@0 324 void
c@14 325 TempogramPlugin::selectProgram(string name)
c@0 326 {
c@0 327 }
c@0 328
c@14 329 TempogramPlugin::OutputList
c@14 330 TempogramPlugin::getOutputDescriptors() const
c@0 331 {
c@0 332 OutputList list;
c@0 333
c@0 334 // See OutputDescriptor documentation for the possibilities here.
c@0 335 // Every plugin must have at least one output.
c@1 336
c@7 337 float d_sampleRate;
c@18 338 float tempogramInputSampleRate = (float)m_inputSampleRate/m_inputStepSize;
c@25 339 OutputDescriptor d1;
c@25 340 d1.identifier = "cyclicTempogram";
c@25 341 d1.name = "Cyclic Tempogram";
Chris@43 342 d1.description = "Cyclic tempogram calculated by \"octave folding\" the DFT tempogram";
c@25 343 d1.unit = "";
c@25 344 d1.hasFixedBinCount = true;
c@25 345 d1.binCount = m_cyclicTempogramOctaveDivider > 0 && !isnan(m_cyclicTempogramOctaveDivider) ? m_cyclicTempogramOctaveDivider : 0;
c@25 346 d1.hasKnownExtents = false;
c@25 347 d1.isQuantized = false;
c@25 348 d1.sampleType = OutputDescriptor::FixedSampleRate;
c@25 349 d_sampleRate = tempogramInputSampleRate/m_tempogramHopSize;
c@25 350 d1.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0;
c@25 351 d1.hasDuration = false;
c@25 352 list.push_back(d1);
c@25 353
c@25 354 OutputDescriptor d2;
c@25 355 d2.identifier = "tempogramDFT";
c@25 356 d2.name = "Tempogram via DFT";
Chris@43 357 d2.description = "Tempogram calculated using Discrete Fourier Transform method";
Chris@43 358 d2.unit = ""; // unit of bin contents, not of "bin label", so not bpm
c@25 359 d2.hasFixedBinCount = true;
c@25 360 d2.binCount = m_tempogramMaxBin - m_tempogramMinBin + 1;
c@25 361 d2.hasKnownExtents = false;
c@25 362 d2.isQuantized = false;
c@25 363 d2.sampleType = OutputDescriptor::FixedSampleRate;
c@25 364 d_sampleRate = tempogramInputSampleRate/m_tempogramHopSize;
c@25 365 d2.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
c@25 366 for(int i = m_tempogramMinBin; i <= (int)m_tempogramMaxBin; i++){
c@25 367 float w = ((float)i/m_tempogramFftLength)*(tempogramInputSampleRate);
c@25 368 d2.binNames.push_back(floatToString(w*60));
c@25 369 }
c@25 370 d2.hasDuration = false;
c@25 371 list.push_back(d2);
c@25 372
c@21 373 OutputDescriptor d3;
c@25 374 d3.identifier = "tempogramACT";
c@25 375 d3.name = "Tempogram via ACT";
Chris@43 376 d3.description = "Tempogram calculated using autocorrelation method";
Chris@43 377 d3.unit = ""; // unit of bin contents, not of "bin label", so not bpm
c@21 378 d3.hasFixedBinCount = true;
c@28 379 d3.binCount = m_tempogramMaxLag - m_tempogramMinLag + 1;
c@21 380 d3.hasKnownExtents = false;
c@21 381 d3.isQuantized = false;
c@21 382 d3.sampleType = OutputDescriptor::FixedSampleRate;
c@21 383 d_sampleRate = tempogramInputSampleRate/m_tempogramHopSize;
c@25 384 d3.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
c@28 385 for(int lag = m_tempogramMaxLag; lag >= (int)m_tempogramMinLag; lag--){
c@28 386 d3.binNames.push_back(floatToString(60/(m_inputStepSize*(lag/m_inputSampleRate))));
c@25 387 }
c@21 388 d3.hasDuration = false;
c@21 389 list.push_back(d3);
c@21 390
c@25 391 OutputDescriptor d4;
c@25 392 d4.identifier = "nc";
c@25 393 d4.name = "Novelty Curve";
Chris@43 394 d4.description = "Novelty curve underlying the tempogram calculations";
c@25 395 d4.unit = "";
c@25 396 d4.hasFixedBinCount = true;
c@25 397 d4.binCount = 1;
c@25 398 d4.hasKnownExtents = false;
c@25 399 d4.isQuantized = false;
c@25 400 d4.sampleType = OutputDescriptor::FixedSampleRate;
c@9 401 d_sampleRate = tempogramInputSampleRate;
c@25 402 d4.sampleRate = d_sampleRate > 0 && !isnan(d_sampleRate) ? d_sampleRate : 0;
c@25 403 d4.hasDuration = false;
c@25 404 list.push_back(d4);
c@18 405
c@0 406 return list;
c@0 407 }
c@0 408
c@20 409 bool
c@20 410 TempogramPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
c@20 411 {
c@20 412 if (channels < getMinChannelCount() ||
c@20 413 channels > getMaxChannelCount()) return false;
c@20 414
c@20 415 // Real initialisation work goes here!
c@20 416 m_inputBlockSize = blockSize;
c@20 417 m_inputStepSize = stepSize;
c@20 418
c@24 419 //m_spectrogram = Spectrogram(m_inputBlockSize/2 + 1);
c@21 420 if (!handleParameterValues()) return false;
c@19 421 //cout << m_cyclicTempogramOctaveDivider << endl;
c@4 422
c@0 423 return true;
c@0 424 }
c@0 425
c@0 426 void
c@14 427 TempogramPlugin::reset()
c@0 428 {
c@0 429 // Clear buffers, reset stored values, etc
c@19 430 m_spectrogram.clear();
c@21 431 handleParameterValues();
c@0 432 }
c@0 433
c@14 434 TempogramPlugin::FeatureSet
c@14 435 TempogramPlugin::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
c@0 436 {
c@23 437 int n = m_inputBlockSize/2 + 1;
c@0 438 const float *in = inputBuffers[0];
c@3 439
c@9 440 //calculate magnitude of FrequencyDomain input
c@22 441 vector<float> fftCoefficients;
c@23 442 for (int i = 0; i < n; i++){
c@0 443 float magnitude = sqrt(in[2*i] * in[2*i] + in[2*i + 1] * in[2*i + 1]);
c@29 444 magnitude = magnitude > m_noveltyCurveMinV ? magnitude : m_noveltyCurveMinV;
c@22 445 fftCoefficients.push_back(magnitude);
c@0 446 }
c@22 447 m_spectrogram.push_back(fftCoefficients);
c@24 448 //m_spectrogram.push_back(fftCoefficients);
c@21 449
c@23 450 return FeatureSet();
c@0 451 }
c@0 452
c@14 453 TempogramPlugin::FeatureSet
c@14 454 TempogramPlugin::getRemainingFeatures()
c@11 455 {
c@0 456
c@18 457 float * hannWindow = new float[m_tempogramWindowLength];
c@20 458 for (int i = 0; i < (int)m_tempogramWindowLength; i++){
c@14 459 hannWindow[i] = 0.0;
c@4 460 }
c@11 461
c@1 462 FeatureSet featureSet;
c@0 463
c@19 464 //initialise novelty curve processor
c@23 465 int numberOfBlocks = m_spectrogram.size();
c@20 466 //cerr << numberOfBlocks << endl;
c@22 467 NoveltyCurveProcessor nc(m_inputSampleRate, m_inputBlockSize, m_noveltyCurveCompressionConstant);
c@21 468 vector<float> noveltyCurve = nc.spectrogramToNoveltyCurve(m_spectrogram); //calculate novelty curvefrom magnitude data
c@4 469
c@9 470 //push novelty curve data to featureset 1 and set timestamps
c@23 471 for (int i = 0; i < numberOfBlocks; i++){
c@19 472 Feature noveltyCurveFeature;
c@19 473 noveltyCurveFeature.values.push_back(noveltyCurve[i]);
c@19 474 noveltyCurveFeature.hasTimestamp = false;
c@25 475 featureSet[3].push_back(noveltyCurveFeature);
c@21 476 assert(!isnan(noveltyCurveFeature.values.back()));
c@4 477 }
c@4 478
c@9 479 //window function for spectrogram
c@18 480 WindowFunction::hanning(hannWindow, m_tempogramWindowLength);
c@9 481
c@9 482 //initialise spectrogram processor
c@18 483 SpectrogramProcessor spectrogramProcessor(m_tempogramWindowLength, m_tempogramFftLength, m_tempogramHopSize);
c@9 484 //compute spectrogram from novelty curve data (i.e., tempogram)
c@25 485 Tempogram tempogramDFT = spectrogramProcessor.process(&noveltyCurve[0], numberOfBlocks, hannWindow);
c@18 486 delete []hannWindow;
c@18 487 hannWindow = 0;
c@0 488
c@25 489 int tempogramLength = tempogramDFT.size();
c@7 490
c@9 491 //push tempogram data to featureset 0 and set timestamps.
c@7 492 for (int block = 0; block < tempogramLength; block++){
c@25 493 Feature tempogramDFTFeature;
c@28 494
c@28 495 assert(tempogramDFT[block].size() == (m_tempogramFftLength/2 + 1));
c@28 496 for(int k = m_tempogramMinBin; k <= (int)m_tempogramMaxBin; k++){
c@28 497 tempogramDFTFeature.values.push_back(tempogramDFT[block][k]);
c@28 498 }
c@28 499 tempogramDFTFeature.hasTimestamp = false;
c@28 500 featureSet[1].push_back(tempogramDFTFeature);
c@28 501 }
c@28 502
c@28 503 AutocorrelationProcessor autocorrelationProcessor(m_tempogramWindowLength, m_tempogramHopSize);
c@28 504 Tempogram tempogramACT = autocorrelationProcessor.process(&noveltyCurve[0], numberOfBlocks);
c@28 505
c@28 506 for (int block = 0; block < tempogramLength; block++){
c@25 507 Feature tempogramACTFeature;
Chris@44 508
Chris@44 509 // cerr << "block = " << block << ", window length = " << m_tempogramWindowLength << ", max lag = " << m_tempogramMaxLag << ", min lag = " << m_tempogramMinLag << endl;
c@0 510
c@28 511 for(int k = m_tempogramMaxLag; k >= (int)m_tempogramMinLag; k--){
Chris@44 512 // cerr << "(" << block << "," << k << ") ";
c@25 513 tempogramACTFeature.values.push_back(tempogramACT[block][k]);
c@0 514 }
Chris@44 515 // cerr << endl;
c@25 516 tempogramACTFeature.hasTimestamp = false;
c@25 517 featureSet[2].push_back(tempogramACTFeature);
c@0 518 }
c@0 519
c@18 520 //Calculate cyclic tempogram
c@22 521 vector< vector<unsigned int> > logBins = calculateTempogramNearestNeighbourLogBins();
c@18 522
c@22 523 //assert((int)logBins.size() == m_cyclicTempogramOctaveDivider*m_cyclicTempogramNumberOfOctaves);
c@18 524 for (int block = 0; block < tempogramLength; block++){
c@19 525 Feature cyclicTempogramFeature;
c@18 526
c@23 527 for (int i = 0; i < m_cyclicTempogramOctaveDivider; i++){
c@18 528 float sum = 0;
c@21 529
c@23 530 for (int j = 0; j < m_cyclicTempogramNumberOfOctaves; j++){
Chris@34 531
Chris@34 532 if (block >= tempogramDFT.size()) {
Chris@34 533 cerr << "ERROR: at block = " << block << ", i = " << i << ", j = " << j << ": block " << block << " >= tempogramDFT.size() " << tempogramDFT.size() << endl;
Chris@34 534 } else if (j > logBins.size()) {
Chris@34 535 cerr << "ERROR: at block = " << block << ", i = " << i << ", j = " << j << ": j " << j << " >= logBins.size() " << logBins.size() << endl;
Chris@34 536 } else if (i > logBins[j].size()) {
Chris@34 537 cerr << "ERROR: at block = " << block << ", i = " << i << ", j = " << j << ": i " << i << " >= logBins[j].size() " << logBins[j].size() << endl;
Chris@34 538 } else if (logBins[j][i] >= tempogramDFT[block].size()) {
Chris@34 539 cerr << "ERROR: at block = " << block << ", i = " << i << ", j = " << j << ": logBins[j][i] " << logBins[j][i] << " >= tempogramDFT[block].size() " << tempogramDFT[block].size() << endl;
Chris@34 540 } else {
Chris@34 541 sum += tempogramDFT[block][logBins[j][i]];
Chris@34 542 }
c@18 543 }
c@19 544 cyclicTempogramFeature.values.push_back(sum/m_cyclicTempogramNumberOfOctaves);
c@21 545 assert(!isnan(cyclicTempogramFeature.values.back()));
c@18 546 }
c@18 547
c@19 548 cyclicTempogramFeature.hasTimestamp = false;
c@21 549 featureSet[0].push_back(cyclicTempogramFeature);
c@18 550 }
c@0 551
c@0 552 return featureSet;
c@0 553 }
c@22 554
c@22 555 vector< vector<unsigned int> > TempogramPlugin::calculateTempogramNearestNeighbourLogBins() const
c@22 556 {
c@22 557 vector< vector<unsigned int> > logBins;
c@22 558
Chris@47 559 cerr << "calculateTempogramNearestNeighbourLogBins: octaves = "
Chris@47 560 << m_cyclicTempogramNumberOfOctaves << endl;
Chris@47 561
c@22 562 for (int octave = 0; octave < (int)m_cyclicTempogramNumberOfOctaves; octave++){
c@22 563 vector<unsigned int> octaveBins;
Chris@47 564
Chris@47 565 cerr << "octave " << octave << ":" << endl;
Chris@47 566
c@22 567 for (int bin = 0; bin < (int)m_cyclicTempogramOctaveDivider; bin++){
c@22 568 float bpm = m_cyclicTempogramMinBPM*pow(2.0f, octave+(float)bin/m_cyclicTempogramOctaveDivider);
c@22 569 octaveBins.push_back(bpmToBin(bpm));
Chris@47 570 cerr << bpmToBin(bpm) << " ";
c@23 571 //cout << octaveBins.back() << endl;
c@22 572 }
Chris@47 573 cerr << endl;
c@22 574 logBins.push_back(octaveBins);
Chris@47 575
c@22 576 }
c@22 577
c@22 578 return logBins;
c@22 579 }
c@22 580
c@22 581 unsigned int TempogramPlugin::bpmToBin(const float &bpm) const
c@22 582 {
c@22 583 float w = (float)bpm/60;
c@22 584 float sampleRate = m_inputSampleRate/m_inputStepSize;
c@22 585 int bin = floor((float)m_tempogramFftLength*w/sampleRate + 0.5);
c@22 586
c@22 587 if(bin < 0) bin = 0;
Chris@46 588 else if(bin > m_tempogramFftLength/2.0f) bin = m_tempogramFftLength/2.0f;
c@22 589
c@22 590 return bin;
c@22 591 }
c@22 592
c@22 593 float TempogramPlugin::binToBPM(const int &bin) const
c@22 594 {
c@22 595 float sampleRate = m_inputSampleRate/m_inputStepSize;
c@22 596
c@22 597 return (bin*sampleRate/m_tempogramFftLength)*60;
c@22 598 }
c@22 599
c@22 600 bool TempogramPlugin::handleParameterValues(){
c@22 601
Chris@42 602 if (m_tempogramLog2HopSize <= 0) {
Chris@42 603 cerr << "Tempogram log2 hop size " << m_tempogramLog2HopSize
Chris@42 604 << " <= 0, failing initialise" << endl;
Chris@42 605 return false;
Chris@42 606 }
Chris@42 607 if (m_tempogramLog2FftLength <= 0) {
Chris@42 608 cerr << "Tempogram log2 fft length " << m_tempogramLog2FftLength
Chris@42 609 << " <= 0, failing initialise" << endl;
Chris@42 610 return false;
Chris@42 611 }
c@22 612
Chris@42 613 if (m_tempogramMinBPM < 1) {
Chris@42 614 m_tempogramMinBPM = 1;
Chris@42 615 }
c@22 616 if (m_tempogramMinBPM >= m_tempogramMaxBPM){
c@22 617 m_tempogramMinBPM = 30;
c@22 618 m_tempogramMaxBPM = 480;
c@22 619 }
c@22 620
c@29 621 m_noveltyCurveMinV = pow(10,(float)m_noveltyCurveMinDB/20);
c@29 622
c@29 623 m_tempogramWindowLength = pow(2,m_tempogramLog2WindowLength);
c@29 624 m_tempogramHopSize = pow(2,m_tempogramLog2HopSize);
c@29 625 m_tempogramFftLength = pow(2,m_tempogramLog2FftLength);
c@29 626
c@30 627 if (m_tempogramFftLength < m_tempogramWindowLength){
c@30 628 m_tempogramFftLength = m_tempogramWindowLength;
c@30 629 }
c@30 630
c@22 631 float tempogramInputSampleRate = (float)m_inputSampleRate/m_inputStepSize;
c@28 632 m_tempogramMinBin = (max((int)floor(((m_tempogramMinBPM/60)/tempogramInputSampleRate)*m_tempogramFftLength), 0));
c@28 633 m_tempogramMaxBin = (min((int)ceil(((m_tempogramMaxBPM/60)/tempogramInputSampleRate)*m_tempogramFftLength), (int)(m_tempogramFftLength/2)));
Chris@32 634
Chris@32 635 if (m_tempogramMaxBin < m_tempogramMinBin) {
Chris@32 636 cerr << "At audio sample rate " << m_inputSampleRate
Chris@32 637 << ", tempogram sample rate " << tempogramInputSampleRate
Chris@32 638 << " with bpm range " << m_tempogramMinBPM << " -> "
Chris@32 639 << m_tempogramMaxBPM << ", min bin = " << m_tempogramMinBin
Chris@32 640 << " > max bin " << m_tempogramMaxBin
Chris@32 641 << ": can't proceed, failing initialise" << endl;
Chris@32 642 return false;
Chris@32 643 }
c@28 644
c@28 645 m_tempogramMinLag = max((int)ceil((60/(m_inputStepSize * m_tempogramMaxBPM))*m_inputSampleRate), 0);
Chris@45 646 m_tempogramMaxLag = min((int)floor((60/(m_inputStepSize * m_tempogramMinBPM))*m_inputSampleRate), (int)m_tempogramWindowLength-1);
Chris@32 647
Chris@32 648 if (m_tempogramMaxLag < m_tempogramMinLag) {
Chris@32 649 cerr << "At audio sample rate " << m_inputSampleRate
Chris@32 650 << ", tempogram sample rate " << tempogramInputSampleRate
Chris@42 651 << ", window length " << m_tempogramWindowLength
Chris@32 652 << " with bpm range " << m_tempogramMinBPM << " -> "
Chris@42 653 << m_tempogramMaxBPM << ", min lag = " << m_tempogramMinLag
Chris@42 654 << " > max lag " << m_tempogramMaxLag
Chris@32 655 << ": can't proceed, failing initialise" << endl;
Chris@32 656 return false;
Chris@32 657 }
c@22 658
Chris@47 659 m_cyclicTempogramMinBPM = max(binToBPM(m_tempogramMinBin), m_tempogramMinBPM);
Chris@47 660 float cyclicTempogramMaxBPM = min(binToBPM(m_tempogramMaxBin), m_tempogramMaxBPM);
Chris@47 661
Chris@47 662 cerr << "tempogram min bpm = " << m_tempogramMinBPM << ", cyclic min = "
Chris@47 663 << m_cyclicTempogramMinBPM << endl;
Chris@47 664 cerr << "tempogram max bpm = " << m_tempogramMaxBPM << ", cyclic max = "
Chris@47 665 << cyclicTempogramMaxBPM << endl;
Chris@42 666
c@22 667 m_cyclicTempogramNumberOfOctaves = floor(log2(cyclicTempogramMaxBPM/m_cyclicTempogramMinBPM));
Chris@42 668
Chris@42 669 if (m_cyclicTempogramNumberOfOctaves < 1) {
Chris@42 670 cerr << "At audio sample rate " << m_inputSampleRate
Chris@42 671 << ", tempogram sample rate " << tempogramInputSampleRate
Chris@42 672 << " with bpm range " << m_tempogramMinBPM << " -> "
Chris@42 673 << m_tempogramMaxBPM << ", cyclic tempogram min bpm = "
Chris@42 674 << m_cyclicTempogramMinBPM << " and max bpm = "
Chris@42 675 << cyclicTempogramMaxBPM << " giving number of octaves = "
Chris@42 676 << m_cyclicTempogramNumberOfOctaves
Chris@42 677 << ": can't proceed, failing initialise" << endl;
Chris@42 678 return false;
Chris@42 679 }
c@22 680
c@22 681 return true;
c@22 682 }
c@22 683
c@22 684 string TempogramPlugin::floatToString(float value) const
c@22 685 {
c@22 686 ostringstream ss;
c@22 687
c@22 688 if(!(ss << value)) throw runtime_error("TempogramPlugin::floatToString(): invalid conversion from float to string");
c@22 689 return ss.str();
c@22 690 }