annotate TempogramPlugin.cpp @ 14:c11367df624d

* Renamed NoveltyCurve.* and Spectrogram.* to $(Name)Processor.* * Aligned novelty curve with audio - when performing FIRFilter::process(params), take inputLength after group delay. * Removed trail of Spectrogram. * General tidying!
author Carl Bussey <c.bussey@se10.qmul.ac.uk>
date Thu, 14 Aug 2014 10:31:49 +0100
parents Tempogram.cpp@7680cc4c0073
children 203551cbad47
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@0 17 m_blockSize(0),
c@1 18 m_stepSize(0),
c@13 19 m_compressionConstant(1000), //parameter
c@13 20 m_minDB(0),
c@14 21 m_log2WindowLength(10), //parameter
c@14 22 m_windowLength(pow((float)2,m_log2WindowLength)),
c@14 23 m_log2FftLength(m_log2WindowLength),
c@14 24 m_fftLength(m_windowLength),
c@14 25 m_log2HopSize(6), //parameter
c@14 26 m_hopSize(pow((float)2,m_log2HopSize)),
c@13 27 m_minBPM(30),
c@13 28 m_maxBPM(480),
c@13 29 m_minBin(0), //set in initialise()
c@14 30 m_maxBin(0) //set in initialise()
c@0 31
c@0 32 // Also be sure to set your plugin parameters (presumably stored
c@0 33 // in member variables) to their default values here -- the host
c@0 34 // will not do that for you
c@0 35 {
c@0 36 }
c@0 37
c@14 38 TempogramPlugin::~TempogramPlugin()
c@0 39 {
c@0 40 //delete stuff
c@7 41 cleanup();
c@0 42 }
c@0 43
c@0 44 string
c@14 45 TempogramPlugin::getIdentifier() const
c@0 46 {
c@0 47 return "tempogram";
c@0 48 }
c@0 49
c@0 50 string
c@14 51 TempogramPlugin::getName() const
c@0 52 {
c@0 53 return "Tempogram";
c@0 54 }
c@0 55
c@0 56 string
c@14 57 TempogramPlugin::getDescription() const
c@0 58 {
c@0 59 // Return something helpful here!
c@0 60 return "Cyclic Tempogram as described by Peter Grosche and Meinard Muller";
c@0 61 }
c@0 62
c@0 63 string
c@14 64 TempogramPlugin::getMaker() const
c@0 65 {
c@0 66 //Your name here
c@0 67 return "Carl Bussey";
c@0 68 }
c@0 69
c@0 70 int
c@14 71 TempogramPlugin::getPluginVersion() const
c@0 72 {
c@0 73 // Increment this each time you release a version that behaves
c@0 74 // differently from the previous one
c@0 75 return 1;
c@0 76 }
c@0 77
c@0 78 string
c@14 79 TempogramPlugin::getCopyright() const
c@0 80 {
c@0 81 // This function is not ideally named. It does not necessarily
c@0 82 // need to say who made the plugin -- getMaker does that -- but it
c@0 83 // should indicate the terms under which it is distributed. For
c@0 84 // example, "Copyright (year). All Rights Reserved", or "GPL"
c@0 85 return "";
c@0 86 }
c@0 87
c@14 88 TempogramPlugin::InputDomain
c@14 89 TempogramPlugin::getInputDomain() const
c@0 90 {
c@0 91 return FrequencyDomain;
c@0 92 }
c@0 93
c@0 94 size_t
c@14 95 TempogramPlugin::getPreferredBlockSize() const
c@0 96 {
c@9 97 return 2048; // 0 means "I can handle any block size"
c@0 98 }
c@0 99
c@0 100 size_t
c@14 101 TempogramPlugin::getPreferredStepSize() const
c@0 102 {
c@9 103 return 1024; // 0 means "anything sensible"; in practice this
c@0 104 // means the same as the block size for TimeDomain
c@0 105 // plugins, or half of it for FrequencyDomain plugins
c@0 106 }
c@0 107
c@0 108 size_t
c@14 109 TempogramPlugin::getMinChannelCount() const
c@0 110 {
c@0 111 return 1;
c@0 112 }
c@0 113
c@0 114 size_t
c@14 115 TempogramPlugin::getMaxChannelCount() const
c@0 116 {
c@0 117 return 1;
c@0 118 }
c@0 119
c@14 120 TempogramPlugin::ParameterList
c@14 121 TempogramPlugin::getParameterDescriptors() const
c@0 122 {
c@0 123 ParameterList list;
c@0 124
c@0 125 // If the plugin has no adjustable parameters, return an empty
c@0 126 // list here (and there's no need to provide implementations of
c@0 127 // getParameter and setParameter in that case either).
c@0 128
c@0 129 // Note that it is your responsibility to make sure the parameters
c@0 130 // start off having their default values (e.g. in the constructor
c@0 131 // above). The host needs to know the default value so it can do
c@0 132 // things like provide a "reset to default" function, but it will
c@0 133 // not explicitly set your parameters to their defaults for you if
c@0 134 // they have not changed in the mean time.
c@0 135
c@14 136 ParameterDescriptor d1;
c@14 137 d1.identifier = "C";
c@14 138 d1.name = "C";
c@14 139 d1.description = "Spectrogram compression constant, C, used when retrieving the novelty curve from the audio.";
c@14 140 d1.unit = "";
c@14 141 d1.minValue = 2;
c@14 142 d1.maxValue = 10000;
c@14 143 d1.defaultValue = 1000;
c@14 144 d1.isQuantized = false;
c@14 145 list.push_back(d1);
c@9 146
c@14 147 ParameterDescriptor d2;
c@14 148 d2.identifier = "log2TN";
c@14 149 d2.name = "Tempogram Window Length";
c@14 150 d2.description = "FFT window length when analysing the novelty curve and extracting the tempogram time-frequency function.";
c@14 151 d2.unit = "";
c@14 152 d2.minValue = 7;
c@14 153 d2.maxValue = 12;
c@14 154 d2.defaultValue = 10;
c@14 155 d2.isQuantized = true;
c@14 156 d2.quantizeStep = 1;
c@14 157 for (int i = d2.minValue; i <= d2.maxValue; i++){
c@14 158 d2.valueNames.push_back(floatToString(pow((float)2,(float)i)));
c@13 159 }
c@14 160 list.push_back(d2);
c@0 161
c@14 162 ParameterDescriptor d3;
c@14 163 d3.identifier = "log2HopSize";
c@14 164 d3.name = "Tempogram Hopsize";
c@14 165 d3.description = "FFT hopsize when analysing the novelty curve and extracting the tempogram time-frequency function.";
c@14 166 d3.unit = "";
c@14 167 d3.minValue = 6;
c@14 168 d3.maxValue = 12;
c@14 169 d3.defaultValue = 6;
c@14 170 d3.isQuantized = true;
c@14 171 d3.quantizeStep = 1;
c@14 172 for (int i = d3.minValue; i <= d3.maxValue; i++){
c@14 173 d3.valueNames.push_back(floatToString(pow((float)2,(float)i)));
c@14 174 }
c@14 175 list.push_back(d3);
c@9 176
c@14 177 ParameterDescriptor d4;
c@14 178 d4.identifier = "log2FftLength";
c@14 179 d4.name = "Tempogram FFT Length";
c@14 180 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 181 d4.unit = "";
c@14 182 d4.minValue = 6;
c@14 183 d4.maxValue = 12;
c@14 184 d4.defaultValue = d2.defaultValue;
c@14 185 d4.isQuantized = true;
c@14 186 d4.quantizeStep = 1;
c@14 187 for (int i = d4.minValue; i <= d4.maxValue; i++){
c@14 188 d4.valueNames.push_back(floatToString(pow((float)2,(float)i)));
c@14 189 }
c@14 190 list.push_back(d4);
c@14 191
c@14 192 ParameterDescriptor d5;
c@14 193 d5.identifier = "minBPM";
c@14 194 d5.name = "Minimum BPM";
c@14 195 d5.description = "The minimum BPM of the tempogram output bins.";
c@14 196 d5.unit = "";
c@14 197 d5.minValue = 0;
c@14 198 d5.maxValue = 2000;
c@14 199 d5.defaultValue = 30;
c@14 200 d5.isQuantized = true;
c@14 201 d5.quantizeStep = 5;
c@14 202 list.push_back(d5);
c@14 203
c@14 204 ParameterDescriptor d6;
c@14 205 d6.identifier = "maxBPM";
c@14 206 d6.name = "Maximum BPM";
c@14 207 d6.description = "The minimum BPM of the tempogram output bins.";
c@14 208 d6.unit = "";
c@14 209 d6.minValue = 30;
c@14 210 d6.maxValue = 2000;
c@14 211 d6.defaultValue = 480;
c@14 212 d6.isQuantized = true;
c@14 213 d6.quantizeStep = 5;
c@14 214 list.push_back(d6);
c@0 215
c@0 216 return list;
c@0 217 }
c@0 218
c@0 219 float
c@14 220 TempogramPlugin::getParameter(string identifier) const
c@0 221 {
c@0 222 if (identifier == "C") {
c@13 223 return m_compressionConstant; // return the ACTUAL current value of your parameter here!
c@0 224 }
c@14 225 else if (identifier == "log2TN"){
c@13 226 return m_log2WindowLength;
c@9 227 }
c@14 228 else if (identifier == "log2HopSize"){
c@14 229 return m_log2HopSize;
c@14 230 }
c@14 231 else if (identifier == "log2FftLength"){
c@14 232 return m_log2FftLength;
c@14 233 }
c@14 234 else if (identifier == "minBPM") {
c@13 235 return m_minBPM;
c@9 236 }
c@14 237 else if (identifier == "maxBPM"){
c@13 238 return m_maxBPM;
c@0 239 }
c@0 240
c@0 241 return 0;
c@0 242 }
c@0 243
c@0 244 void
c@14 245 TempogramPlugin::setParameter(string identifier, float value)
c@0 246 {
c@9 247
c@0 248 if (identifier == "C") {
c@13 249 m_compressionConstant = value; // set the actual value of your parameter
c@0 250 }
c@14 251 else if (identifier == "log2TN") {
c@13 252 m_windowLength = pow(2,value);
c@13 253 m_log2WindowLength = value;
c@0 254 }
c@14 255 else if (identifier == "log2HopSize"){
c@14 256 m_hopSize = pow(2,value);
c@14 257 m_log2HopSize = value;
c@14 258 }
c@14 259 else if (identifier == "log2HopFftLength"){
c@14 260 m_fftLength = pow(2,value);
c@14 261 m_log2FftLength = value;
c@14 262 }
c@14 263 else if (identifier == "minBPM") {
c@13 264 m_minBPM = value;
c@9 265 }
c@14 266 else if (identifier == "maxBPM"){
c@13 267 m_maxBPM = value;
c@9 268 }
c@9 269
c@9 270 }
c@9 271
c@14 272 void TempogramPlugin::updateBPMParameters(){
c@9 273
c@0 274 }
c@0 275
c@14 276 TempogramPlugin::ProgramList
c@14 277 TempogramPlugin::getPrograms() const
c@0 278 {
c@0 279 ProgramList list;
c@0 280
c@0 281 // If you have no programs, return an empty list (or simply don't
c@0 282 // implement this function or getCurrentProgram/selectProgram)
c@0 283
c@0 284 return list;
c@0 285 }
c@0 286
c@0 287 string
c@14 288 TempogramPlugin::getCurrentProgram() const
c@0 289 {
c@0 290 return ""; // no programs
c@0 291 }
c@0 292
c@0 293 void
c@14 294 TempogramPlugin::selectProgram(string name)
c@0 295 {
c@0 296 }
c@0 297
c@14 298 string TempogramPlugin::floatToString(float value) const
c@9 299 {
c@9 300 ostringstream ss;
c@9 301
c@14 302 if(!(ss << value)) throw runtime_error("TempogramPlugin::floatToString(): invalid conversion from float to string");
c@9 303 return ss.str();
c@9 304 }
c@9 305
c@14 306 TempogramPlugin::OutputList
c@14 307 TempogramPlugin::getOutputDescriptors() const
c@0 308 {
c@0 309 OutputList list;
c@0 310
c@0 311 // See OutputDescriptor documentation for the possibilities here.
c@0 312 // Every plugin must have at least one output.
c@1 313
c@0 314 OutputDescriptor d;
c@7 315 float d_sampleRate;
c@9 316 float tempogramInputSampleRate = (float)m_inputSampleRate/m_stepSize;
c@7 317
c@1 318 d.identifier = "tempogram";
c@7 319 d.name = "Tempogram";
c@7 320 d.description = "Tempogram";
c@9 321 d.unit = "BPM";
c@1 322 d.hasFixedBinCount = true;
c@13 323 d.binCount = m_maxBin - m_minBin + 1;
c@0 324 d.hasKnownExtents = false;
c@0 325 d.isQuantized = false;
c@1 326 d.sampleType = OutputDescriptor::FixedSampleRate;
c@13 327 d_sampleRate = tempogramInputSampleRate/m_hopSize;
c@1 328 d.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
c@13 329 for(int i = m_minBin; i <= (int)m_maxBin; i++){
c@13 330 float w = ((float)i/m_fftLength)*(tempogramInputSampleRate);
c@9 331 d.binNames.push_back(floatToString(w*60));
c@9 332 }
c@0 333 d.hasDuration = false;
c@0 334 list.push_back(d);
c@7 335
c@1 336 d.identifier = "nc";
c@1 337 d.name = "Novelty Curve";
c@1 338 d.description = "Novelty Curve";
c@1 339 d.unit = "";
c@1 340 d.hasFixedBinCount = true;
c@1 341 d.binCount = 1;
c@1 342 d.hasKnownExtents = false;
c@1 343 d.isQuantized = false;
c@1 344 d.sampleType = OutputDescriptor::FixedSampleRate;
c@9 345 d_sampleRate = tempogramInputSampleRate;
c@1 346 d.sampleRate = d_sampleRate > 0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
c@1 347 d.hasDuration = false;
c@1 348 list.push_back(d);
c@1 349
c@0 350 return list;
c@0 351 }
c@0 352
c@0 353 bool
c@14 354 TempogramPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
c@0 355 {
c@0 356 if (channels < getMinChannelCount() ||
c@0 357 channels > getMaxChannelCount()) return false;
c@9 358
c@0 359 // Real initialisation work goes here!
c@0 360 m_blockSize = blockSize;
c@1 361 m_stepSize = stepSize;
c@13 362 m_minDB = pow(10,(float)-74/20);
c@0 363
c@14 364 if (m_fftLength < m_windowLength){
c@14 365 m_fftLength = m_windowLength;
c@14 366 }
c@13 367 if (m_minBPM > m_maxBPM){
c@13 368 m_minBPM = 30;
c@13 369 m_maxBPM = 480;
c@9 370 }
c@9 371 float tempogramInputSampleRate = (float)m_inputSampleRate/m_stepSize;
c@13 372 m_minBin = (unsigned int)(max(floor(((m_minBPM/60)/tempogramInputSampleRate)*m_fftLength), (float)0.0));
c@13 373 m_maxBin = (unsigned int)(min(ceil(((m_maxBPM/60)/tempogramInputSampleRate)*m_fftLength), (float)m_fftLength/2));
c@9 374
c@14 375 m_spectrogram = SpectrogramTransposed(m_blockSize/2 + 1);
c@4 376
c@0 377 return true;
c@0 378 }
c@0 379
c@14 380 void TempogramPlugin::cleanup(){
c@7 381
c@7 382 }
c@7 383
c@0 384 void
c@14 385 TempogramPlugin::reset()
c@0 386 {
c@0 387 // Clear buffers, reset stored values, etc
c@7 388 ncTimestamps.clear();
c@14 389 m_spectrogram = SpectrogramTransposed(m_blockSize/2 + 1);
c@0 390 }
c@0 391
c@14 392 TempogramPlugin::FeatureSet
c@14 393 TempogramPlugin::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
c@0 394 {
c@0 395 size_t n = m_blockSize/2 + 1;
c@0 396
c@0 397 FeatureSet featureSet;
c@0 398 Feature feature;
c@0 399
c@0 400 const float *in = inputBuffers[0];
c@3 401
c@9 402 //calculate magnitude of FrequencyDomain input
c@13 403 for (unsigned int i = 0; i < n; i++){
c@0 404 float magnitude = sqrt(in[2*i] * in[2*i] + in[2*i + 1] * in[2*i + 1]);
c@13 405 magnitude = magnitude > m_minDB ? magnitude : m_minDB;
c@13 406 m_spectrogram[i].push_back(magnitude);
c@0 407 }
c@0 408
c@9 409 ncTimestamps.push_back(timestamp); //save timestamp
c@7 410
c@2 411 return featureSet;
c@0 412 }
c@0 413
c@14 414 TempogramPlugin::FeatureSet
c@14 415 TempogramPlugin::getRemainingFeatures()
c@11 416 {
c@0 417
c@14 418 float * hannWindow = new float[m_windowLength];
c@13 419 for (unsigned int i = 0; i < m_windowLength; i++){
c@14 420 hannWindow[i] = 0.0;
c@4 421 }
c@11 422
c@1 423 FeatureSet featureSet;
c@0 424
c@13 425 //initialise m_noveltyCurve processor
c@14 426 size_t numberOfBlocks = m_spectrogram[0].size();
c@14 427 NoveltyCurveProcessor nc(m_inputSampleRate, m_blockSize, numberOfBlocks, m_compressionConstant);
c@13 428 m_noveltyCurve = nc.spectrogramToNoveltyCurve(m_spectrogram); //calculate novelty curve from magnitude data
c@4 429
c@9 430 //push novelty curve data to featureset 1 and set timestamps
c@14 431 for (unsigned int i = 0; i < numberOfBlocks; i++){
c@7 432 Feature feature;
c@13 433 feature.values.push_back(m_noveltyCurve[i]);
c@14 434 feature.hasTimestamp = false;
c@14 435 //feature.timestamp = ncTimestamps[i];
c@7 436 featureSet[1].push_back(feature);
c@4 437 }
c@4 438
c@9 439 //window function for spectrogram
c@14 440 WindowFunction::hanning(hannWindow, m_windowLength);
c@9 441
c@9 442 //initialise spectrogram processor
c@14 443 SpectrogramProcessor spectrogramProcessor(m_windowLength, m_fftLength, m_hopSize);
c@9 444 //compute spectrogram from novelty curve data (i.e., tempogram)
c@14 445 Spectrogram tempogram = spectrogramProcessor.process(&m_noveltyCurve[0], numberOfBlocks, hannWindow);
c@0 446
c@13 447 int timePointer = m_hopSize-m_windowLength/2;
c@14 448 int tempogramLength = tempogram.size();
c@7 449
c@9 450 //push tempogram data to featureset 0 and set timestamps.
c@7 451 for (int block = 0; block < tempogramLength; block++){
c@0 452 Feature feature;
c@0 453
c@14 454 //int timeMS = floor(1000*(m_stepSize*timePointer)/m_inputSampleRate + 0.5);
c@7 455
c@14 456 assert(tempogram[block].size() == (m_fftLength/2 + 1));
c@13 457 for(int k = m_minBin; k < (int)m_maxBin; k++){
c@14 458 feature.values.push_back(tempogram[block][k]);
c@13 459 //cout << tempogram[k][block] << endl;
c@0 460 }
c@14 461 feature.hasTimestamp = false;
c@14 462 //feature.timestamp = RealTime::fromMilliseconds(timeMS);
c@7 463 featureSet[0].push_back(feature);
c@4 464
c@13 465 timePointer += m_hopSize;
c@0 466 }
c@0 467
c@14 468 delete []hannWindow;
c@14 469 hannWindow = 0;
c@0 470
c@0 471 return featureSet;
c@0 472 }