annotate TempogramPlugin.cpp @ 60:ac1a75151fc9 tip

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