annotate TempogramPlugin.cpp @ 42:d4b74059a005

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