annotate Tempogram.cpp @ 13:7680cc4c0073

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