annotate Tempogram.cpp @ 4:597f033fa7a2

* Some cleaning and rearranging to prepare for band processing implementation
author Carl Bussey <c.bussey@se10.qmul.ac.uk>
date Tue, 05 Aug 2014 15:56:59 +0100
parents 5125d34fda67
children 21147df9cb2d
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@0 8 #include "FIRFilter.h"
c@0 9 #include "WindowFunction.h"
c@4 10 #include "NoveltyCurve.h"
c@0 11 #include <vamp-sdk/FFT.h>
c@0 12 #include <cmath>
c@0 13 #include <fstream>
c@0 14 #include <assert.h>
c@4 15
c@0 16 using Vamp::FFT;
c@0 17 using namespace std;
c@0 18
c@0 19 Tempogram::Tempogram(float inputSampleRate) :
c@0 20 Plugin(inputSampleRate),
c@0 21 m_blockSize(0),
c@1 22 m_stepSize(0),
c@0 23 compressionConstant(1000), //make param
c@3 24 specMax(0),
c@0 25 previousY(NULL),
c@0 26 currentY(NULL),
c@4 27 spectrogram(NULL),
c@3 28 minDB(0),
c@4 29 tN(256), //make param
c@4 30 thopSize(128), //make param
c@0 31 fftInput(NULL),
c@0 32 fftOutputReal(NULL),
c@0 33 fftOutputImag(NULL),
c@4 34 numberOfBlocks(0)
c@0 35
c@0 36 // Also be sure to set your plugin parameters (presumably stored
c@0 37 // in member variables) to their default values here -- the host
c@0 38 // will not do that for you
c@0 39 {
c@0 40 }
c@0 41
c@0 42 Tempogram::~Tempogram()
c@0 43 {
c@0 44 //delete stuff
c@0 45 }
c@0 46
c@0 47 string
c@0 48 Tempogram::getIdentifier() const
c@0 49 {
c@0 50 return "tempogram";
c@0 51 }
c@0 52
c@0 53 string
c@0 54 Tempogram::getName() const
c@0 55 {
c@0 56 return "Tempogram";
c@0 57 }
c@0 58
c@0 59 string
c@0 60 Tempogram::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@0 67 Tempogram::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@0 74 Tempogram::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@0 82 Tempogram::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@0 91 Tempogram::InputDomain
c@0 92 Tempogram::getInputDomain() const
c@0 93 {
c@0 94 return FrequencyDomain;
c@0 95 }
c@0 96
c@0 97 size_t
c@0 98 Tempogram::getPreferredBlockSize() const
c@0 99 {
c@0 100 return 0; // 0 means "I can handle any block size"
c@0 101 }
c@0 102
c@0 103 size_t
c@0 104 Tempogram::getPreferredStepSize() const
c@0 105 {
c@0 106 return 0; // 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@0 112 Tempogram::getMinChannelCount() const
c@0 113 {
c@0 114 return 1;
c@0 115 }
c@0 116
c@0 117 size_t
c@0 118 Tempogram::getMaxChannelCount() const
c@0 119 {
c@0 120 return 1;
c@0 121 }
c@0 122
c@0 123 Tempogram::ParameterList
c@0 124 Tempogram::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@0 139 ParameterDescriptor C;
c@0 140 C.identifier = "C";
c@0 141 C.name = "C";
c@0 142 C.description = "Spectrogram compression constant, C";
c@0 143 C.unit = "";
c@0 144 C.minValue = 2;
c@0 145 C.maxValue = 10000;
c@0 146 C.defaultValue = 1000;
c@0 147 C.isQuantized = false;
c@0 148 list.push_back(C);
c@0 149
c@0 150 ParameterDescriptor tN;
c@0 151 tN.identifier = "tN";
c@0 152 tN.name = "Tempogram FFT length";
c@0 153 tN.description = "Tempogram FFT length.";
c@0 154 tN.unit = "";
c@0 155 tN.minValue = 128;
c@0 156 tN.maxValue = 4096;
c@0 157 tN.defaultValue = 1024;
c@0 158 tN.isQuantized = true;
c@0 159 tN.quantizeStep = 128;
c@0 160 list.push_back(tN);
c@0 161
c@0 162 return list;
c@0 163 }
c@0 164
c@0 165 float
c@0 166 Tempogram::getParameter(string identifier) const
c@0 167 {
c@0 168 if (identifier == "C") {
c@0 169 return compressionConstant; // return the ACTUAL current value of your parameter here!
c@0 170 }
c@0 171 if (identifier == "tN"){
c@0 172 return tN;
c@0 173 }
c@0 174
c@0 175 return 0;
c@0 176 }
c@0 177
c@0 178 void
c@0 179 Tempogram::setParameter(string identifier, float value)
c@0 180 {
c@0 181 if (identifier == "C") {
c@1 182 compressionConstant = value; // set the actual value of your parameter
c@0 183 }
c@0 184 if (identifier == "tN") {
c@0 185 tN = value;
c@0 186 }
c@0 187 }
c@0 188
c@0 189 Tempogram::ProgramList
c@0 190 Tempogram::getPrograms() const
c@0 191 {
c@0 192 ProgramList list;
c@0 193
c@0 194 // If you have no programs, return an empty list (or simply don't
c@0 195 // implement this function or getCurrentProgram/selectProgram)
c@0 196
c@0 197 return list;
c@0 198 }
c@0 199
c@0 200 string
c@0 201 Tempogram::getCurrentProgram() const
c@0 202 {
c@0 203 return ""; // no programs
c@0 204 }
c@0 205
c@0 206 void
c@0 207 Tempogram::selectProgram(string name)
c@0 208 {
c@0 209 }
c@0 210
c@0 211 Tempogram::OutputList
c@0 212 Tempogram::getOutputDescriptors() const
c@0 213 {
c@0 214 OutputList list;
c@0 215
c@0 216 // See OutputDescriptor documentation for the possibilities here.
c@0 217 // Every plugin must have at least one output.
c@1 218
c@0 219 OutputDescriptor d;
c@1 220 d.identifier = "tempogram";
c@0 221 d.name = "Cyclic Tempogram";
c@0 222 d.description = "Cyclic Tempogram";
c@0 223 d.unit = "";
c@1 224 d.hasFixedBinCount = true;
c@1 225 d.binCount = tN;
c@0 226 d.hasKnownExtents = false;
c@0 227 d.isQuantized = false;
c@1 228 d.sampleType = OutputDescriptor::FixedSampleRate;
c@1 229 float d_sampleRate = m_inputSampleRate/(m_stepSize * thopSize);
c@1 230 d.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
c@0 231 d.hasDuration = false;
c@0 232 list.push_back(d);
c@0 233
c@1 234 d.identifier = "nc";
c@1 235 d.name = "Novelty Curve";
c@1 236 d.description = "Novelty Curve";
c@1 237 d.unit = "";
c@1 238 d.hasFixedBinCount = true;
c@1 239 d.binCount = 1;
c@1 240 d.hasKnownExtents = false;
c@1 241 d.isQuantized = false;
c@1 242 d.sampleType = OutputDescriptor::FixedSampleRate;
c@1 243 d_sampleRate = m_inputSampleRate/m_stepSize;
c@1 244 d.sampleRate = d_sampleRate > 0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
c@1 245 d.hasDuration = false;
c@1 246 list.push_back(d);
c@1 247
c@2 248 d.identifier = "spect";
c@2 249 d.name = "spect";
c@2 250 d.description = "spect";
c@2 251 d.unit = "";
c@2 252 d.hasFixedBinCount = true;
c@2 253 d.binCount = m_blockSize/2;
c@2 254 d.hasKnownExtents = false;
c@2 255 d.isQuantized = false;
c@2 256 d.sampleType = OutputDescriptor::OneSamplePerStep;
c@2 257 d.hasDuration = false;
c@2 258 list.push_back(d);
c@2 259
c@0 260 return list;
c@0 261 }
c@0 262
c@0 263 bool
c@0 264 Tempogram::initialise(size_t channels, size_t stepSize, size_t blockSize)
c@0 265 {
c@0 266 if (channels < getMinChannelCount() ||
c@0 267 channels > getMaxChannelCount()) return false;
c@0 268
c@0 269 // Real initialisation work goes here!
c@0 270 m_blockSize = blockSize;
c@1 271 m_stepSize = stepSize;
c@0 272 currentY = new float[m_blockSize];
c@0 273 previousY = new float[m_blockSize];
c@3 274 minDB = pow((float)10,(float)-74/20);
c@0 275
c@4 276 specData = vector< vector<float> >(m_blockSize/2 + 1);
c@4 277 spectrogram = new float * [m_blockSize/2 + 1];
c@4 278
c@0 279 return true;
c@0 280 }
c@0 281
c@0 282 void
c@0 283 Tempogram::reset()
c@0 284 {
c@0 285 // Clear buffers, reset stored values, etc
c@0 286 }
c@0 287
c@0 288 Tempogram::FeatureSet
c@0 289 Tempogram::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
c@0 290 {
c@4 291
c@0 292 size_t n = m_blockSize/2 + 1;
c@0 293
c@0 294 FeatureSet featureSet;
c@0 295 Feature feature;
c@0 296
c@0 297 const float *in = inputBuffers[0];
c@3 298
c@0 299 for (int i = 0; i < n; i++){
c@0 300 float magnitude = sqrt(in[2*i] * in[2*i] + in[2*i + 1] * in[2*i + 1]);
c@3 301 magnitude = magnitude > minDB ? magnitude : minDB;
c@4 302 specData[i].push_back(magnitude);
c@2 303 feature.values.push_back(magnitude);
c@0 304 }
c@0 305
c@3 306 numberOfBlocks++;
c@0 307 ncTimestamps.push_back(timestamp);
c@2 308 featureSet[2].push_back(feature);
c@0 309
c@2 310 return featureSet;
c@0 311 }
c@0 312
c@0 313 void
c@0 314 Tempogram::initialiseForGRF(){
c@0 315 hannWindowtN = new float[tN];
c@0 316 fftInput = new double[tN];
c@0 317 fftOutputReal = new double[tN];
c@0 318 fftOutputImag = new double[tN];
c@0 319
c@4 320 for (int i = 0; i < (m_blockSize/2 + 1); i ++){
c@4 321 spectrogram[i] = &specData[i][0];
c@4 322 }
c@0 323 }
c@0 324
c@0 325 void
c@0 326 Tempogram::cleanupForGRF(){
c@0 327 delete []hannWindowtN;
c@0 328 hannWindowtN = NULL;
c@0 329 delete []fftInput;
c@0 330 fftInput = NULL;
c@0 331 delete []fftOutputReal;
c@0 332 fftOutputReal = NULL;
c@0 333 delete []fftOutputImag;
c@0 334 fftOutputImag = NULL;
c@0 335 }
c@0 336
c@0 337 Tempogram::FeatureSet
c@0 338 Tempogram::getRemainingFeatures()
c@0 339 {
c@0 340 //Make sure this is called at the beginning of the function
c@0 341 initialiseForGRF();
c@1 342 FeatureSet featureSet;
c@0 343
c@4 344 NoveltyCurve nc(m_inputSampleRate, m_blockSize, numberOfBlocks, compressionConstant);
c@4 345 noveltyCurve = nc.spectrogramToNoveltyCurve(spectrogram);
c@4 346
c@4 347 for (int i = 0; i < numberOfBlocks; i++){
c@4 348 Feature featureNC;
c@4 349 cout << "nc:" << noveltyCurve[i] << endl;
c@4 350 featureNC.values.push_back(noveltyCurve[i]);
c@4 351 featureNC.hasTimestamp = true;
c@4 352 featureNC.timestamp = ncTimestamps[i];
c@4 353 featureSet[1].push_back(featureNC);
c@4 354 }
c@4 355
c@0 356 WindowFunction::hanning(hannWindowtN, tN);
c@0 357
c@2 358 int timestampInc = floor((((float)ncTimestamps[1].nsec - ncTimestamps[0].nsec)/1e9)*(thopSize) + 0.5);
c@3 359 int i = 0;
c@0 360 int index;
c@4 361 int frameBeginOffset = thopSize;
c@0 362
c@3 363 while(i < numberOfBlocks){
c@0 364 Feature feature;
c@0 365
c@1 366 for (int n = frameBeginOffset; n < tN; n++){
c@4 367 index = i + n - thopSize;
c@0 368 assert (index >= 0);
c@0 369
c@3 370 if(index < numberOfBlocks){
c@0 371 fftInput[n] = noveltyCurve[i + n] * hannWindowtN[n];
c@0 372 }
c@3 373 else if(index >= numberOfBlocks){
c@0 374 fftInput[n] = 0.0; //pad the end with zeros
c@0 375 }
c@0 376 }
c@4 377
c@4 378 if (i+thopSize > numberOfBlocks){
c@1 379 feature.timestamp = Vamp::RealTime::fromSeconds(ncTimestamps[i].sec + timestampInc);
c@0 380 }
c@0 381 else{
c@4 382 feature.timestamp = ncTimestamps[i + thopSize];
c@0 383 }
c@0 384
c@0 385 FFT::forward(tN, fftInput, NULL, fftOutputReal, fftOutputImag);
c@0 386
c@4 387 //@todo: sample at logarithmic spacing? Leave for host?
c@0 388 for(int k = 0; k < tN; k++){
c@1 389 float fftOutputPower = (fftOutputReal[k]*fftOutputReal[k] + fftOutputImag[k]*fftOutputImag[k]); //Magnitude or power?
c@0 390
c@0 391 feature.values.push_back(fftOutputPower);
c@0 392 }
c@0 393
c@0 394 i += thopSize;
c@1 395 frameBeginOffset = 0;
c@0 396
c@0 397 feature.hasTimestamp = true;
c@0 398 featureSet[0].push_back(feature);
c@0 399 }
c@0 400
c@0 401 //Make sure this is called at the end of the function
c@0 402 cleanupForGRF();
c@0 403
c@0 404 return featureSet;
c@0 405 }