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