annotate TempogramPlugin.cpp @ 44:a908a5a56267

Some unsigned -> int (while bug hunting)
author Chris Cannam
date Thu, 25 Sep 2014 15:42:15 +0100
parents 4cf2d163127b
children e6a43500629b
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
c@22 559 for (int octave = 0; octave < (int)m_cyclicTempogramNumberOfOctaves; octave++){
c@22 560 vector<unsigned int> octaveBins;
c@22 561
c@22 562 for (int bin = 0; bin < (int)m_cyclicTempogramOctaveDivider; bin++){
c@22 563 float bpm = m_cyclicTempogramMinBPM*pow(2.0f, octave+(float)bin/m_cyclicTempogramOctaveDivider);
c@22 564 octaveBins.push_back(bpmToBin(bpm));
c@23 565 //cout << octaveBins.back() << endl;
c@22 566 }
c@22 567 logBins.push_back(octaveBins);
c@22 568 }
c@22 569
c@22 570 return logBins;
c@22 571 }
c@22 572
c@22 573 unsigned int TempogramPlugin::bpmToBin(const float &bpm) const
c@22 574 {
c@22 575 float w = (float)bpm/60;
c@22 576 float sampleRate = m_inputSampleRate/m_inputStepSize;
c@22 577 int bin = floor((float)m_tempogramFftLength*w/sampleRate + 0.5);
c@22 578
c@22 579 if(bin < 0) bin = 0;
c@22 580 else if(bin > m_tempogramFftLength/2.0f) bin = m_tempogramFftLength;
c@22 581
c@22 582 return bin;
c@22 583 }
c@22 584
c@22 585 float TempogramPlugin::binToBPM(const int &bin) const
c@22 586 {
c@22 587 float sampleRate = m_inputSampleRate/m_inputStepSize;
c@22 588
c@22 589 return (bin*sampleRate/m_tempogramFftLength)*60;
c@22 590 }
c@22 591
c@22 592 bool TempogramPlugin::handleParameterValues(){
c@22 593
Chris@42 594 if (m_tempogramLog2HopSize <= 0) {
Chris@42 595 cerr << "Tempogram log2 hop size " << m_tempogramLog2HopSize
Chris@42 596 << " <= 0, failing initialise" << endl;
Chris@42 597 return false;
Chris@42 598 }
Chris@42 599 if (m_tempogramLog2FftLength <= 0) {
Chris@42 600 cerr << "Tempogram log2 fft length " << m_tempogramLog2FftLength
Chris@42 601 << " <= 0, failing initialise" << endl;
Chris@42 602 return false;
Chris@42 603 }
c@22 604
Chris@42 605 if (m_tempogramMinBPM < 1) {
Chris@42 606 m_tempogramMinBPM = 1;
Chris@42 607 }
c@22 608 if (m_tempogramMinBPM >= m_tempogramMaxBPM){
c@22 609 m_tempogramMinBPM = 30;
c@22 610 m_tempogramMaxBPM = 480;
c@22 611 }
c@22 612
c@29 613 m_noveltyCurveMinV = pow(10,(float)m_noveltyCurveMinDB/20);
c@29 614
c@29 615 m_tempogramWindowLength = pow(2,m_tempogramLog2WindowLength);
c@29 616 m_tempogramHopSize = pow(2,m_tempogramLog2HopSize);
c@29 617 m_tempogramFftLength = pow(2,m_tempogramLog2FftLength);
c@29 618
c@30 619 if (m_tempogramFftLength < m_tempogramWindowLength){
c@30 620 m_tempogramFftLength = m_tempogramWindowLength;
c@30 621 }
c@30 622
c@22 623 float tempogramInputSampleRate = (float)m_inputSampleRate/m_inputStepSize;
c@28 624 m_tempogramMinBin = (max((int)floor(((m_tempogramMinBPM/60)/tempogramInputSampleRate)*m_tempogramFftLength), 0));
c@28 625 m_tempogramMaxBin = (min((int)ceil(((m_tempogramMaxBPM/60)/tempogramInputSampleRate)*m_tempogramFftLength), (int)(m_tempogramFftLength/2)));
Chris@32 626
Chris@32 627 if (m_tempogramMaxBin < m_tempogramMinBin) {
Chris@32 628 cerr << "At audio sample rate " << m_inputSampleRate
Chris@32 629 << ", tempogram sample rate " << tempogramInputSampleRate
Chris@32 630 << " with bpm range " << m_tempogramMinBPM << " -> "
Chris@32 631 << m_tempogramMaxBPM << ", min bin = " << m_tempogramMinBin
Chris@32 632 << " > max bin " << m_tempogramMaxBin
Chris@32 633 << ": can't proceed, failing initialise" << endl;
Chris@32 634 return false;
Chris@32 635 }
c@28 636
c@28 637 m_tempogramMinLag = max((int)ceil((60/(m_inputStepSize * m_tempogramMaxBPM))*m_inputSampleRate), 0);
c@28 638 m_tempogramMaxLag = min((int)floor((60/(m_inputStepSize * m_tempogramMinBPM))*m_inputSampleRate), (int)m_tempogramWindowLength);
Chris@32 639
Chris@32 640 if (m_tempogramMaxLag < m_tempogramMinLag) {
Chris@32 641 cerr << "At audio sample rate " << m_inputSampleRate
Chris@32 642 << ", tempogram sample rate " << tempogramInputSampleRate
Chris@42 643 << ", window length " << m_tempogramWindowLength
Chris@32 644 << " with bpm range " << m_tempogramMinBPM << " -> "
Chris@42 645 << m_tempogramMaxBPM << ", min lag = " << m_tempogramMinLag
Chris@42 646 << " > max lag " << m_tempogramMaxLag
Chris@32 647 << ": can't proceed, failing initialise" << endl;
Chris@32 648 return false;
Chris@32 649 }
c@22 650
Chris@42 651 m_cyclicTempogramMinBPM = m_tempogramMinBPM;
Chris@42 652 float cyclicTempogramMaxBPM = m_tempogramMaxBPM;
Chris@42 653
c@22 654 m_cyclicTempogramNumberOfOctaves = floor(log2(cyclicTempogramMaxBPM/m_cyclicTempogramMinBPM));
Chris@42 655
Chris@42 656 if (m_cyclicTempogramNumberOfOctaves < 1) {
Chris@42 657 cerr << "At audio sample rate " << m_inputSampleRate
Chris@42 658 << ", tempogram sample rate " << tempogramInputSampleRate
Chris@42 659 << " with bpm range " << m_tempogramMinBPM << " -> "
Chris@42 660 << m_tempogramMaxBPM << ", cyclic tempogram min bpm = "
Chris@42 661 << m_cyclicTempogramMinBPM << " and max bpm = "
Chris@42 662 << cyclicTempogramMaxBPM << " giving number of octaves = "
Chris@42 663 << m_cyclicTempogramNumberOfOctaves
Chris@42 664 << ": can't proceed, failing initialise" << endl;
Chris@42 665 return false;
Chris@42 666 }
c@22 667
c@22 668 return true;
c@22 669 }
c@22 670
c@22 671 string TempogramPlugin::floatToString(float value) const
c@22 672 {
c@22 673 ostringstream ss;
c@22 674
c@22 675 if(!(ss << value)) throw runtime_error("TempogramPlugin::floatToString(): invalid conversion from float to string");
c@22 676 return ss.str();
c@22 677 }