annotate TempogramPlugin.cpp @ 21:12b952286959

* Debugging 1Hz freeze using vamp-plugin-tester
author Carl Bussey <c.bussey@se10.qmul.ac.uk>
date Mon, 18 Aug 2014 15:22:44 +0100
parents de7213b35755
children 99380ba63be6
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@9 8 #include <sstream>
c@9 9 #include <stdexcept>
c@4 10
c@0 11 using Vamp::FFT;
c@7 12 using Vamp::RealTime;
c@0 13 using namespace std;
c@0 14
c@14 15 TempogramPlugin::TempogramPlugin(float inputSampleRate) :
c@0 16 Plugin(inputSampleRate),
c@18 17 m_inputBlockSize(0), //host parameter
c@18 18 m_inputStepSize(0), //host parameter
c@19 19 m_noveltyCurveMinDB(pow(10,(float)-74/20)), //set in initialise()
c@18 20 m_noveltyCurveCompressionConstant(1000), //parameter
c@18 21 m_tempogramLog2WindowLength(10), //parameter
c@18 22 m_tempogramWindowLength(pow((float)2,m_tempogramLog2WindowLength)),
c@18 23 m_tempogramLog2FftLength(m_tempogramLog2WindowLength), //parameter
c@18 24 m_tempogramFftLength(m_tempogramWindowLength),
c@18 25 m_tempogramLog2HopSize(6), //parameter
c@18 26 m_tempogramHopSize(pow((float)2,m_tempogramLog2HopSize)),
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@18 31 m_cyclicTempogramMinBPM(30), //reset in initialise()
c@18 32 m_cyclicTempogramNumberOfOctaves(0), //set in initialise()
c@18 33 m_cyclicTempogramOctaveDivider(30) //parameter
c@0 34
c@0 35 // Also be sure to set your plugin parameters (presumably stored
c@0 36 // in member variables) to their default values here -- the host
c@0 37 // will not do that for you
c@0 38 {
c@0 39 }
c@0 40
c@14 41 TempogramPlugin::~TempogramPlugin()
c@0 42 {
c@0 43 //delete stuff
c@19 44
c@0 45 }
c@0 46
c@0 47 string
c@14 48 TempogramPlugin::getIdentifier() const
c@0 49 {
c@0 50 return "tempogram";
c@0 51 }
c@0 52
c@0 53 string
c@14 54 TempogramPlugin::getName() const
c@0 55 {
c@0 56 return "Tempogram";
c@0 57 }
c@0 58
c@0 59 string
c@14 60 TempogramPlugin::getDescription() const
c@0 61 {
c@0 62 // Return something helpful here!
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 //Your name here
c@0 70 return "Carl Bussey";
c@0 71 }
c@0 72
c@0 73 int
c@14 74 TempogramPlugin::getPluginVersion() const
c@0 75 {
c@0 76 // Increment this each time you release a version that behaves
c@0 77 // differently from the previous one
c@0 78 return 1;
c@0 79 }
c@0 80
c@0 81 string
c@14 82 TempogramPlugin::getCopyright() const
c@0 83 {
c@0 84 // This function is not ideally named. It does not necessarily
c@0 85 // need to say who made the plugin -- getMaker does that -- but it
c@0 86 // should indicate the terms under which it is distributed. For
c@0 87 // example, "Copyright (year). All Rights Reserved", or "GPL"
c@0 88 return "";
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@9 149
c@14 150 ParameterDescriptor d2;
c@14 151 d2.identifier = "log2TN";
c@14 152 d2.name = "Tempogram Window Length";
c@14 153 d2.description = "FFT window length when analysing the novelty curve and extracting the tempogram time-frequency function.";
c@14 154 d2.unit = "";
c@14 155 d2.minValue = 7;
c@14 156 d2.maxValue = 12;
c@14 157 d2.defaultValue = 10;
c@14 158 d2.isQuantized = true;
c@14 159 d2.quantizeStep = 1;
c@14 160 for (int i = d2.minValue; i <= d2.maxValue; i++){
c@14 161 d2.valueNames.push_back(floatToString(pow((float)2,(float)i)));
c@13 162 }
c@14 163 list.push_back(d2);
c@0 164
c@14 165 ParameterDescriptor d3;
c@14 166 d3.identifier = "log2HopSize";
c@14 167 d3.name = "Tempogram Hopsize";
c@14 168 d3.description = "FFT hopsize when analysing the novelty curve and extracting the tempogram time-frequency function.";
c@14 169 d3.unit = "";
c@14 170 d3.minValue = 6;
c@14 171 d3.maxValue = 12;
c@14 172 d3.defaultValue = 6;
c@14 173 d3.isQuantized = true;
c@14 174 d3.quantizeStep = 1;
c@14 175 for (int i = d3.minValue; i <= d3.maxValue; i++){
c@14 176 d3.valueNames.push_back(floatToString(pow((float)2,(float)i)));
c@14 177 }
c@14 178 list.push_back(d3);
c@9 179
c@14 180 ParameterDescriptor d4;
c@14 181 d4.identifier = "log2FftLength";
c@14 182 d4.name = "Tempogram FFT Length";
c@14 183 d4.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 184 d4.unit = "";
c@14 185 d4.minValue = 6;
c@14 186 d4.maxValue = 12;
c@14 187 d4.defaultValue = d2.defaultValue;
c@14 188 d4.isQuantized = true;
c@14 189 d4.quantizeStep = 1;
c@14 190 for (int i = d4.minValue; i <= d4.maxValue; i++){
c@14 191 d4.valueNames.push_back(floatToString(pow((float)2,(float)i)));
c@14 192 }
c@14 193 list.push_back(d4);
c@14 194
c@14 195 ParameterDescriptor d5;
c@14 196 d5.identifier = "minBPM";
c@18 197 d5.name = "(Cyclic) Tempogram Minimum BPM";
c@14 198 d5.description = "The minimum BPM of the tempogram output bins.";
c@14 199 d5.unit = "";
c@14 200 d5.minValue = 0;
c@14 201 d5.maxValue = 2000;
c@14 202 d5.defaultValue = 30;
c@14 203 d5.isQuantized = true;
c@14 204 d5.quantizeStep = 5;
c@14 205 list.push_back(d5);
c@14 206
c@14 207 ParameterDescriptor d6;
c@14 208 d6.identifier = "maxBPM";
c@18 209 d6.name = "(Cyclic) Tempogram Maximum BPM";
c@18 210 d6.description = "The maximum BPM of the tempogram output bins.";
c@14 211 d6.unit = "";
c@14 212 d6.minValue = 30;
c@14 213 d6.maxValue = 2000;
c@14 214 d6.defaultValue = 480;
c@14 215 d6.isQuantized = true;
c@14 216 d6.quantizeStep = 5;
c@14 217 list.push_back(d6);
c@18 218
c@18 219 ParameterDescriptor d7;
c@18 220 d7.identifier = "octDiv";
c@18 221 d7.name = "Cyclic Tempogram Octave Divider";
c@18 222 d7.description = "The number bins within each octave.";
c@18 223 d7.unit = "";
c@18 224 d7.minValue = 5;
c@18 225 d7.maxValue = 60;
c@18 226 d7.defaultValue = 30;
c@18 227 d7.isQuantized = true;
c@18 228 d7.quantizeStep = 1;
c@18 229 list.push_back(d7);
c@0 230
c@0 231 return list;
c@0 232 }
c@0 233
c@0 234 float
c@14 235 TempogramPlugin::getParameter(string identifier) const
c@0 236 {
c@0 237 if (identifier == "C") {
c@18 238 return m_noveltyCurveCompressionConstant; // return the ACTUAL current value of your parameter here!
c@0 239 }
c@14 240 else if (identifier == "log2TN"){
c@18 241 return m_tempogramLog2WindowLength;
c@9 242 }
c@14 243 else if (identifier == "log2HopSize"){
c@18 244 return m_tempogramLog2HopSize;
c@14 245 }
c@14 246 else if (identifier == "log2FftLength"){
c@18 247 return m_tempogramLog2FftLength;
c@14 248 }
c@14 249 else if (identifier == "minBPM") {
c@18 250 return m_tempogramMinBPM;
c@9 251 }
c@14 252 else if (identifier == "maxBPM"){
c@18 253 return m_tempogramMaxBPM;
c@18 254 }
c@18 255 else if (identifier == "octDiv"){
c@18 256 return m_cyclicTempogramOctaveDivider;
c@0 257 }
c@0 258
c@0 259 return 0;
c@0 260 }
c@0 261
c@0 262 void
c@14 263 TempogramPlugin::setParameter(string identifier, float value)
c@0 264 {
c@9 265
c@0 266 if (identifier == "C") {
c@18 267 m_noveltyCurveCompressionConstant = value; // set the actual value of your parameter
c@0 268 }
c@14 269 else if (identifier == "log2TN") {
c@18 270 m_tempogramWindowLength = pow(2,value);
c@18 271 m_tempogramLog2WindowLength = value;
c@0 272 }
c@14 273 else if (identifier == "log2HopSize"){
c@18 274 m_tempogramHopSize = pow(2,value);
c@18 275 m_tempogramLog2HopSize = value;
c@14 276 }
c@18 277 else if (identifier == "log2FftLength"){
c@18 278 m_tempogramFftLength = pow(2,value);
c@18 279 m_tempogramLog2FftLength = value;
c@14 280 }
c@14 281 else if (identifier == "minBPM") {
c@18 282 m_tempogramMinBPM = value;
c@9 283 }
c@14 284 else if (identifier == "maxBPM"){
c@18 285 m_tempogramMaxBPM = value;
c@18 286 }
c@18 287 else if (identifier == "octDiv"){
c@18 288 m_cyclicTempogramOctaveDivider = value;
c@9 289 }
c@9 290
c@9 291 }
c@9 292
c@14 293 TempogramPlugin::ProgramList
c@14 294 TempogramPlugin::getPrograms() const
c@0 295 {
c@0 296 ProgramList list;
c@0 297
c@0 298 // If you have no programs, return an empty list (or simply don't
c@0 299 // implement this function or getCurrentProgram/selectProgram)
c@0 300
c@0 301 return list;
c@0 302 }
c@0 303
c@0 304 string
c@14 305 TempogramPlugin::getCurrentProgram() const
c@0 306 {
c@0 307 return ""; // no programs
c@0 308 }
c@0 309
c@0 310 void
c@14 311 TempogramPlugin::selectProgram(string name)
c@0 312 {
c@0 313 }
c@0 314
c@14 315 string TempogramPlugin::floatToString(float value) const
c@9 316 {
c@9 317 ostringstream ss;
c@9 318
c@14 319 if(!(ss << value)) throw runtime_error("TempogramPlugin::floatToString(): invalid conversion from float to string");
c@9 320 return ss.str();
c@9 321 }
c@9 322
c@14 323 TempogramPlugin::OutputList
c@14 324 TempogramPlugin::getOutputDescriptors() const
c@0 325 {
c@0 326 OutputList list;
c@0 327
c@0 328 // See OutputDescriptor documentation for the possibilities here.
c@0 329 // Every plugin must have at least one output.
c@1 330
c@7 331 float d_sampleRate;
c@18 332 float tempogramInputSampleRate = (float)m_inputSampleRate/m_inputStepSize;
c@7 333
c@21 334 OutputDescriptor d3;
c@21 335 d3.identifier = "cyclicTempogram";
c@21 336 d3.name = "Cyclic Tempogram";
c@21 337 d3.description = "Cyclic Tempogram";
c@21 338 d3.unit = "";
c@21 339 d3.hasFixedBinCount = true;
c@21 340 d3.binCount = m_cyclicTempogramOctaveDivider > 0 && !isnan(m_cyclicTempogramOctaveDivider) ? m_cyclicTempogramOctaveDivider : 0;
c@21 341 d3.hasKnownExtents = false;
c@21 342 d3.isQuantized = false;
c@21 343 d3.sampleType = OutputDescriptor::FixedSampleRate;
c@21 344 d_sampleRate = tempogramInputSampleRate/m_tempogramHopSize;
c@21 345 d3.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0;
c@21 346 d3.hasDuration = false;
c@21 347 list.push_back(d3);
c@21 348
c@21 349 OutputDescriptor d1;
c@18 350 d1.identifier = "tempogram";
c@18 351 d1.name = "Tempogram";
c@18 352 d1.description = "Tempogram";
c@18 353 d1.unit = "BPM";
c@18 354 d1.hasFixedBinCount = true;
c@18 355 d1.binCount = m_tempogramMaxBin - m_tempogramMinBin + 1;
c@18 356 d1.hasKnownExtents = false;
c@18 357 d1.isQuantized = false;
c@18 358 d1.sampleType = OutputDescriptor::FixedSampleRate;
c@18 359 d_sampleRate = tempogramInputSampleRate/m_tempogramHopSize;
c@18 360 d1.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
c@18 361 for(int i = m_tempogramMinBin; i <= (int)m_tempogramMaxBin; i++){
c@18 362 float w = ((float)i/m_tempogramFftLength)*(tempogramInputSampleRate);
c@18 363 d1.binNames.push_back(floatToString(w*60));
c@9 364 }
c@18 365 d1.hasDuration = false;
c@18 366 list.push_back(d1);
c@7 367
c@18 368 OutputDescriptor d2;
c@18 369 d2.identifier = "nc";
c@18 370 d2.name = "Novelty Curve";
c@18 371 d2.description = "Novelty Curve";
c@18 372 d2.unit = "";
c@18 373 d2.hasFixedBinCount = true;
c@18 374 d2.binCount = 1;
c@18 375 d2.hasKnownExtents = false;
c@18 376 d2.isQuantized = false;
c@18 377 d2.sampleType = OutputDescriptor::FixedSampleRate;
c@9 378 d_sampleRate = tempogramInputSampleRate;
c@18 379 d2.sampleRate = d_sampleRate > 0 && !isnan(d_sampleRate) ? d_sampleRate : 0;
c@18 380 d2.hasDuration = false;
c@18 381 list.push_back(d2);
c@18 382
c@0 383 return list;
c@0 384 }
c@0 385
c@21 386 bool TempogramPlugin::handleParameterValues(){
c@21 387
c@21 388 if (m_tempogramHopSize <= 0) return false;
c@21 389 if (m_tempogramLog2FftLength <= 0) return false;
c@0 390
c@18 391 if (m_tempogramFftLength < m_tempogramWindowLength){
c@18 392 m_tempogramFftLength = m_tempogramWindowLength;
c@14 393 }
c@18 394 if (m_tempogramMinBPM > m_tempogramMaxBPM){
c@18 395 m_tempogramMinBPM = 30;
c@18 396 m_tempogramMaxBPM = 480;
c@9 397 }
c@9 398
c@18 399 float tempogramInputSampleRate = (float)m_inputSampleRate/m_inputStepSize;
c@21 400 m_tempogramMinBin = (max(floor(((m_tempogramMinBPM/60)/tempogramInputSampleRate)*m_tempogramFftLength), (float)0.0));
c@21 401 m_tempogramMaxBin = (min(ceil(((m_tempogramMaxBPM/60)/tempogramInputSampleRate)*m_tempogramFftLength), (float)m_tempogramFftLength/2));
c@18 402
c@18 403 if (m_tempogramMinBPM > m_cyclicTempogramMinBPM) m_cyclicTempogramMinBPM = m_tempogramMinBPM;
c@18 404 float cyclicTempogramMaxBPM = 480;
c@18 405 if (m_tempogramMaxBPM < cyclicTempogramMaxBPM) cyclicTempogramMaxBPM = m_tempogramMaxBPM;
c@18 406
c@18 407 m_cyclicTempogramNumberOfOctaves = floor(log2(cyclicTempogramMaxBPM/m_cyclicTempogramMinBPM));
c@18 408 int numberOfBinsInFirstOctave = bpmToBin(m_cyclicTempogramMinBPM);
c@19 409 if (m_cyclicTempogramOctaveDivider > numberOfBinsInFirstOctave) m_cyclicTempogramOctaveDivider = numberOfBinsInFirstOctave;
c@19 410
c@21 411 return true;
c@20 412 }
c@20 413
c@20 414 bool
c@20 415 TempogramPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
c@20 416 {
c@20 417 if (channels < getMinChannelCount() ||
c@20 418 channels > getMaxChannelCount()) return false;
c@20 419
c@20 420 // Real initialisation work goes here!
c@20 421 m_inputBlockSize = blockSize;
c@20 422 m_inputStepSize = stepSize;
c@20 423
c@20 424 m_spectrogram = SpectrogramTransposed(m_inputBlockSize/2.0f + 1);
c@21 425 if (!handleParameterValues()) return false;
c@19 426 //cout << m_cyclicTempogramOctaveDivider << endl;
c@4 427
c@0 428 return true;
c@0 429 }
c@0 430
c@0 431 void
c@14 432 TempogramPlugin::reset()
c@0 433 {
c@0 434 // Clear buffers, reset stored values, etc
c@19 435 m_spectrogram.clear();
c@20 436 m_spectrogram = SpectrogramTransposed(m_inputBlockSize/2.0f + 1);
c@21 437 handleParameterValues();
c@0 438 }
c@0 439
c@14 440 TempogramPlugin::FeatureSet
c@14 441 TempogramPlugin::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
c@0 442 {
c@21 443 //cerr << "Here" << endl;
c@21 444
c@18 445 size_t n = m_inputBlockSize/2 + 1;
c@0 446
c@0 447 FeatureSet featureSet;
c@0 448 Feature feature;
c@0 449
c@0 450 const float *in = inputBuffers[0];
c@3 451
c@9 452 //calculate magnitude of FrequencyDomain input
c@20 453 for (int i = 0; i < (int)n; i++){
c@0 454 float magnitude = sqrt(in[2*i] * in[2*i] + in[2*i + 1] * in[2*i + 1]);
c@18 455 magnitude = magnitude > m_noveltyCurveMinDB ? magnitude : m_noveltyCurveMinDB;
c@13 456 m_spectrogram[i].push_back(magnitude);
c@0 457 }
c@21 458
c@2 459 return featureSet;
c@0 460 }
c@0 461
c@18 462 vector<unsigned int> TempogramPlugin::calculateTempogramNearestNeighbourLogBins() const
c@18 463 {
c@18 464 vector<unsigned int> logBins;
c@18 465
c@18 466 for (int i = 0; i < (int)ceil(m_cyclicTempogramNumberOfOctaves*m_cyclicTempogramOctaveDivider); i++){
c@18 467 float bpm = m_cyclicTempogramMinBPM*pow(2.0f, (float)i/m_cyclicTempogramOctaveDivider);
c@18 468 int bin = bpmToBin(bpm);
c@18 469
c@18 470 logBins.push_back(bin);
c@20 471 //cerr << bin << endl;
c@18 472 }
c@18 473
c@20 474 //cerr << logBins.size() << endl;
c@19 475
c@18 476 return logBins;
c@18 477 }
c@18 478
c@18 479 int TempogramPlugin::bpmToBin(const float &bpm) const
c@18 480 {
c@18 481 float w = (float)bpm/60;
c@18 482 float sampleRate = m_inputSampleRate/m_inputStepSize;
c@18 483 int bin = floor((float)m_tempogramFftLength*w/sampleRate + 0.5);
c@18 484
c@18 485 if(bin < 0) bin = 0;
c@20 486 else if(bin > m_tempogramFftLength/2.0f) bin = m_tempogramFftLength;
c@18 487
c@18 488 return bin;
c@18 489 }
c@18 490
c@21 491 float TempogramPlugin::binToBPM(const int &bin) const
c@21 492 {
c@21 493 float sampleRate = m_inputSampleRate/m_inputStepSize;
c@21 494
c@21 495 return (bin*sampleRate/m_tempogramFftLength)*60;
c@21 496 }
c@21 497
c@14 498 TempogramPlugin::FeatureSet
c@14 499 TempogramPlugin::getRemainingFeatures()
c@11 500 {
c@0 501
c@18 502 float * hannWindow = new float[m_tempogramWindowLength];
c@20 503 for (int i = 0; i < (int)m_tempogramWindowLength; i++){
c@14 504 hannWindow[i] = 0.0;
c@4 505 }
c@11 506
c@1 507 FeatureSet featureSet;
c@0 508
c@19 509 //initialise novelty curve processor
c@14 510 size_t numberOfBlocks = m_spectrogram[0].size();
c@20 511 //cerr << numberOfBlocks << endl;
c@18 512 NoveltyCurveProcessor nc(m_inputSampleRate, m_inputBlockSize, numberOfBlocks, m_noveltyCurveCompressionConstant);
c@21 513 vector<float> noveltyCurve = nc.spectrogramToNoveltyCurve(m_spectrogram); //calculate novelty curvefrom magnitude data
c@20 514 //if(noveltyCurve.size() > 50) for (int i = 0; i < 50; i++) cerr << noveltyCurve[i] << endl;
c@4 515
c@9 516 //push novelty curve data to featureset 1 and set timestamps
c@20 517 for (int i = 0; i < (int)numberOfBlocks; i++){
c@19 518 Feature noveltyCurveFeature;
c@19 519 noveltyCurveFeature.values.push_back(noveltyCurve[i]);
c@19 520 noveltyCurveFeature.hasTimestamp = false;
c@21 521 featureSet[2].push_back(noveltyCurveFeature);
c@21 522 assert(!isnan(noveltyCurveFeature.values.back()));
c@4 523 }
c@4 524
c@9 525 //window function for spectrogram
c@18 526 WindowFunction::hanning(hannWindow, m_tempogramWindowLength);
c@9 527
c@9 528 //initialise spectrogram processor
c@18 529 SpectrogramProcessor spectrogramProcessor(m_tempogramWindowLength, m_tempogramFftLength, m_tempogramHopSize);
c@9 530 //compute spectrogram from novelty curve data (i.e., tempogram)
c@19 531 Tempogram tempogram = spectrogramProcessor.process(&noveltyCurve[0], numberOfBlocks, hannWindow);
c@18 532 delete []hannWindow;
c@18 533 hannWindow = 0;
c@0 534
c@14 535 int tempogramLength = tempogram.size();
c@7 536
c@9 537 //push tempogram data to featureset 0 and set timestamps.
c@7 538 for (int block = 0; block < tempogramLength; block++){
c@19 539 Feature tempogramFeature;
c@0 540
c@18 541 assert(tempogram[block].size() == (m_tempogramFftLength/2 + 1));
c@18 542 for(int k = m_tempogramMinBin; k < (int)m_tempogramMaxBin; k++){
c@19 543 tempogramFeature.values.push_back(tempogram[block][k]);
c@21 544 assert(!isnan(tempogramFeature.values.back()));
c@0 545 }
c@19 546 tempogramFeature.hasTimestamp = false;
c@21 547 featureSet[1].push_back(tempogramFeature);
c@0 548 }
c@0 549
c@18 550 //Calculate cyclic tempogram
c@18 551 vector<unsigned int> logBins = calculateTempogramNearestNeighbourLogBins();
c@18 552
c@20 553 assert(logBins.back() <= m_tempogramFftLength/2.0f);
c@20 554 assert((int)logBins.size() == m_cyclicTempogramOctaveDivider*m_cyclicTempogramNumberOfOctaves);
c@18 555 for (int block = 0; block < tempogramLength; block++){
c@19 556 Feature cyclicTempogramFeature;
c@18 557
c@20 558 for (int i = 0; i < (int)m_cyclicTempogramOctaveDivider; i++){
c@18 559 float sum = 0;
c@21 560
c@21 561 //mcerr << floor(binToBPM(logBins[i]) + 0.5) << " " << floor(binToBPM(logBins[i + m_cyclicTempogramOctaveDivider]) + 0.5) << endl;
c@21 562
c@20 563 for (int j = 0; j < (int)m_cyclicTempogramNumberOfOctaves; j++){
c@18 564 sum += tempogram[block][logBins[i+j*m_cyclicTempogramOctaveDivider]];
c@18 565 }
c@19 566 cyclicTempogramFeature.values.push_back(sum/m_cyclicTempogramNumberOfOctaves);
c@21 567 assert(!isnan(cyclicTempogramFeature.values.back()));
c@18 568 }
c@18 569
c@19 570 cyclicTempogramFeature.hasTimestamp = false;
c@21 571 featureSet[0].push_back(cyclicTempogramFeature);
c@18 572 }
c@0 573
c@0 574 return featureSet;
c@0 575 }