annotate Tempogram.cpp @ 0:31d2a7e07786

Moved all to folder "tempogram".
author Carl Bussey <c.bussey@se10.qmul.ac.uk>
date Mon, 07 Jul 2014 10:08:14 +0100
parents
children 3fd1a41b089b
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@0 10 #include <vamp-sdk/FFT.h>
c@0 11 #include <cmath>
c@0 12 #include <fstream>
c@0 13 #include <assert.h>
c@0 14 using Vamp::FFT;
c@0 15 using namespace std;
c@0 16
c@0 17 Tempogram::Tempogram(float inputSampleRate) :
c@0 18 Plugin(inputSampleRate),
c@0 19 m_blockSize(0),
c@0 20 compressionConstant(1000), //make param
c@0 21 previousY(NULL),
c@0 22 currentY(NULL),
c@0 23 tN(1024), //make param
c@0 24 thopSize(512), //make param
c@0 25 fftInput(NULL),
c@0 26 fftOutputReal(NULL),
c@0 27 fftOutputImag(NULL),
c@0 28 ncLength(0)
c@0 29
c@0 30 // Also be sure to set your plugin parameters (presumably stored
c@0 31 // in member variables) to their default values here -- the host
c@0 32 // will not do that for you
c@0 33 {
c@0 34 }
c@0 35
c@0 36 Tempogram::~Tempogram()
c@0 37 {
c@0 38 //delete stuff
c@0 39 }
c@0 40
c@0 41 string
c@0 42 Tempogram::getIdentifier() const
c@0 43 {
c@0 44 return "tempogram";
c@0 45 }
c@0 46
c@0 47 string
c@0 48 Tempogram::getName() const
c@0 49 {
c@0 50 return "Tempogram";
c@0 51 }
c@0 52
c@0 53 string
c@0 54 Tempogram::getDescription() const
c@0 55 {
c@0 56 // Return something helpful here!
c@0 57 return "Cyclic Tempogram as described by Peter Grosche and Meinard Muller";
c@0 58 }
c@0 59
c@0 60 string
c@0 61 Tempogram::getMaker() const
c@0 62 {
c@0 63 //Your name here
c@0 64 return "Carl Bussey";
c@0 65 }
c@0 66
c@0 67 int
c@0 68 Tempogram::getPluginVersion() const
c@0 69 {
c@0 70 // Increment this each time you release a version that behaves
c@0 71 // differently from the previous one
c@0 72 return 1;
c@0 73 }
c@0 74
c@0 75 string
c@0 76 Tempogram::getCopyright() const
c@0 77 {
c@0 78 // This function is not ideally named. It does not necessarily
c@0 79 // need to say who made the plugin -- getMaker does that -- but it
c@0 80 // should indicate the terms under which it is distributed. For
c@0 81 // example, "Copyright (year). All Rights Reserved", or "GPL"
c@0 82 return "";
c@0 83 }
c@0 84
c@0 85 Tempogram::InputDomain
c@0 86 Tempogram::getInputDomain() const
c@0 87 {
c@0 88 return FrequencyDomain;
c@0 89 }
c@0 90
c@0 91 size_t
c@0 92 Tempogram::getPreferredBlockSize() const
c@0 93 {
c@0 94 return 0; // 0 means "I can handle any block size"
c@0 95 }
c@0 96
c@0 97 size_t
c@0 98 Tempogram::getPreferredStepSize() const
c@0 99 {
c@0 100 //23 ms?
c@0 101 return 0; // 0 means "anything sensible"; in practice this
c@0 102 // means the same as the block size for TimeDomain
c@0 103 // plugins, or half of it for FrequencyDomain plugins
c@0 104 }
c@0 105
c@0 106 size_t
c@0 107 Tempogram::getMinChannelCount() const
c@0 108 {
c@0 109 return 1;
c@0 110 }
c@0 111
c@0 112 size_t
c@0 113 Tempogram::getMaxChannelCount() const
c@0 114 {
c@0 115 return 1;
c@0 116 }
c@0 117
c@0 118 Tempogram::ParameterList
c@0 119 Tempogram::getParameterDescriptors() const
c@0 120 {
c@0 121 ParameterList list;
c@0 122
c@0 123 // If the plugin has no adjustable parameters, return an empty
c@0 124 // list here (and there's no need to provide implementations of
c@0 125 // getParameter and setParameter in that case either).
c@0 126
c@0 127 // Note that it is your responsibility to make sure the parameters
c@0 128 // start off having their default values (e.g. in the constructor
c@0 129 // above). The host needs to know the default value so it can do
c@0 130 // things like provide a "reset to default" function, but it will
c@0 131 // not explicitly set your parameters to their defaults for you if
c@0 132 // they have not changed in the mean time.
c@0 133
c@0 134 ParameterDescriptor C;
c@0 135 C.identifier = "C";
c@0 136 C.name = "C";
c@0 137 C.description = "Spectrogram compression constant, C";
c@0 138 C.unit = "";
c@0 139 C.minValue = 2;
c@0 140 C.maxValue = 10000;
c@0 141 C.defaultValue = 1000;
c@0 142 C.isQuantized = false;
c@0 143 list.push_back(C);
c@0 144
c@0 145 ParameterDescriptor tN;
c@0 146 tN.identifier = "tN";
c@0 147 tN.name = "Tempogram FFT length";
c@0 148 tN.description = "Tempogram FFT length.";
c@0 149 tN.unit = "";
c@0 150 tN.minValue = 128;
c@0 151 tN.maxValue = 4096;
c@0 152 tN.defaultValue = 1024;
c@0 153 tN.isQuantized = true;
c@0 154 tN.quantizeStep = 128;
c@0 155 list.push_back(tN);
c@0 156
c@0 157 return list;
c@0 158 }
c@0 159
c@0 160 float
c@0 161 Tempogram::getParameter(string identifier) const
c@0 162 {
c@0 163 if (identifier == "C") {
c@0 164 return compressionConstant; // return the ACTUAL current value of your parameter here!
c@0 165 }
c@0 166 if (identifier == "tN"){
c@0 167 return tN;
c@0 168 }
c@0 169
c@0 170 return 0;
c@0 171 }
c@0 172
c@0 173 void
c@0 174 Tempogram::setParameter(string identifier, float value)
c@0 175 {
c@0 176 if (identifier == "C") {
c@0 177 compressionConstant = value;// set the actual value of your parameter
c@0 178 }
c@0 179 if (identifier == "tN") {
c@0 180 tN = value;
c@0 181 }
c@0 182 }
c@0 183
c@0 184 Tempogram::ProgramList
c@0 185 Tempogram::getPrograms() const
c@0 186 {
c@0 187 ProgramList list;
c@0 188
c@0 189 // If you have no programs, return an empty list (or simply don't
c@0 190 // implement this function or getCurrentProgram/selectProgram)
c@0 191
c@0 192 return list;
c@0 193 }
c@0 194
c@0 195 string
c@0 196 Tempogram::getCurrentProgram() const
c@0 197 {
c@0 198 return ""; // no programs
c@0 199 }
c@0 200
c@0 201 void
c@0 202 Tempogram::selectProgram(string name)
c@0 203 {
c@0 204 }
c@0 205
c@0 206 Tempogram::OutputList
c@0 207 Tempogram::getOutputDescriptors() const
c@0 208 {
c@0 209 OutputList list;
c@0 210
c@0 211 // See OutputDescriptor documentation for the possibilities here.
c@0 212 // Every plugin must have at least one output.
c@0 213
c@0 214 OutputDescriptor d;
c@0 215 d.identifier = "output";
c@0 216 d.name = "Cyclic Tempogram";
c@0 217 d.description = "Cyclic Tempogram";
c@0 218 d.unit = "";
c@0 219 d.hasFixedBinCount = false;
c@0 220 //d.binCount = 1;
c@0 221 d.hasKnownExtents = false;
c@0 222 d.isQuantized = false;
c@0 223 d.sampleType = OutputDescriptor::VariableSampleRate;
c@0 224 d.sampleRate = 0.0;
c@0 225 d.hasDuration = false;
c@0 226 list.push_back(d);
c@0 227
c@0 228 return list;
c@0 229 }
c@0 230
c@0 231 bool
c@0 232 Tempogram::initialise(size_t channels, size_t stepSize, size_t blockSize)
c@0 233 {
c@0 234 if (channels < getMinChannelCount() ||
c@0 235 channels > getMaxChannelCount()) return false;
c@0 236
c@0 237 // Real initialisation work goes here!
c@0 238 m_blockSize = blockSize;
c@0 239 currentY = new float[m_blockSize];
c@0 240 previousY = new float[m_blockSize];
c@0 241
c@0 242 return true;
c@0 243 }
c@0 244
c@0 245 void
c@0 246 Tempogram::reset()
c@0 247 {
c@0 248 // Clear buffers, reset stored values, etc
c@0 249 }
c@0 250
c@0 251 Tempogram::FeatureSet
c@0 252 Tempogram::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
c@0 253 {
c@0 254 size_t n = m_blockSize/2 + 1;
c@0 255
c@0 256 FeatureSet featureSet;
c@0 257 Feature feature;
c@0 258 feature.hasTimestamp = false;
c@0 259
c@0 260 const float *in = inputBuffers[0];
c@0 261
c@0 262 //Calculate log magnitude
c@0 263 float sum = 0;
c@0 264 for (int i = 0; i < n; i++){
c@0 265 float magnitude = sqrt(in[2*i] * in[2*i] + in[2*i + 1] * in[2*i + 1]);
c@0 266 currentY[i] = log(1+compressionConstant*magnitude); //should be 1+C*magnitude
c@0 267 if(currentY[i] >= previousY[i]){
c@0 268 sum += (currentY[i] - previousY[i]);
c@0 269 }
c@0 270 }
c@0 271
c@0 272 noveltyCurve.push_back(sum);
c@0 273
c@0 274 float *tmpY = currentY;
c@0 275 currentY = previousY;
c@0 276 previousY = tmpY;
c@0 277
c@0 278 ncTimestamps.push_back(timestamp);
c@0 279
c@0 280 return FeatureSet();
c@0 281 }
c@0 282
c@0 283 void
c@0 284 Tempogram::initialiseForGRF(){
c@0 285 hannN = 129;
c@0 286 hannWindow = new float[hannN];
c@0 287 hannWindowtN = new float[tN];
c@0 288 fftInput = new double[tN];
c@0 289 fftOutputReal = new double[tN];
c@0 290 fftOutputImag = new double[tN];
c@0 291 ncLength = noveltyCurve.size();
c@0 292
c@0 293 WindowFunction::hanning(hannWindow, hannN, true);
c@0 294 }
c@0 295
c@0 296 void
c@0 297 Tempogram::cleanupForGRF(){
c@0 298 delete []hannWindow;
c@0 299 hannWindow = NULL;
c@0 300 delete []hannWindowtN;
c@0 301 hannWindowtN = NULL;
c@0 302 delete []fftInput;
c@0 303 fftInput = NULL;
c@0 304 delete []fftOutputReal;
c@0 305 fftOutputReal = NULL;
c@0 306 delete []fftOutputImag;
c@0 307 fftOutputImag = NULL;
c@0 308 }
c@0 309
c@0 310 Tempogram::FeatureSet
c@0 311 Tempogram::getRemainingFeatures()
c@0 312 {
c@0 313 //Make sure this is called at the beginning of the function
c@0 314 initialiseForGRF();
c@0 315
c@0 316 vector<float> noveltyCurveLocalAverage(ncLength);
c@0 317
c@0 318 FIRFilter *filter = new FIRFilter(ncLength, hannN);
c@0 319 filter->process(&noveltyCurve[0], hannWindow, &noveltyCurveLocalAverage[0]);
c@0 320 delete filter;
c@0 321
c@0 322 for(int i = 0; i < ncLength; i++){
c@0 323 noveltyCurve[i] -= noveltyCurveLocalAverage[i];
c@0 324 noveltyCurve[i] = noveltyCurve[i] >= 0 ? noveltyCurve[i] : 0;
c@0 325 }
c@0 326
c@0 327 int i=0;
c@0 328 WindowFunction::hanning(hannWindowtN, tN);
c@0 329
c@0 330 int index;
c@0 331 int start = floor(tN/2 + 0.5);
c@0 332 int timestampInc = floor((((float)ncTimestamps[1].nsec - ncTimestamps[0].nsec)/1e9)*(thopSize) + 0.5);
c@0 333 //cout << timestampInc << endl;
c@0 334
c@0 335 FeatureSet featureSet;
c@0 336
c@0 337 while(i < ncLength){
c@0 338 Feature feature;
c@0 339 Vamp::RealTime timestamp;
c@0 340
c@0 341 for (int n = start; n < tN; n++){
c@0 342 index = i + n - tN/2;
c@0 343 assert (index >= 0);
c@0 344
c@0 345 if(index < ncLength){
c@0 346 fftInput[n] = noveltyCurve[i + n] * hannWindowtN[n];
c@0 347 }
c@0 348 else if(index >= ncLength){
c@0 349 fftInput[n] = 0.0; //pad the end with zeros
c@0 350 }
c@0 351 //cout << fftInput[n] << endl;
c@0 352 }
c@0 353 if (i+tN/2 > ncLength){
c@0 354 timestamp = Vamp::RealTime::fromSeconds(ncTimestamps[i].sec + timestampInc);
c@0 355 }
c@0 356 else{
c@0 357 timestamp = ncTimestamps[i + tN/2];
c@0 358 }
c@0 359
c@0 360 FFT::forward(tN, fftInput, NULL, fftOutputReal, fftOutputImag);
c@0 361
c@0 362 //TODO: sample at logarithmic spacing
c@0 363 for(int k = 0; k < tN; k++){
c@0 364 double fftOutputPower = (fftOutputReal[k]*fftOutputReal[k] + fftOutputImag[k]*fftOutputImag[k]); //Magnitude or power?
c@0 365 assert (!isinf(fftOutputPower));
c@0 366
c@0 367 feature.values.push_back(fftOutputPower);
c@0 368 }
c@0 369
c@0 370 i += thopSize;
c@0 371 start = 0;
c@0 372
c@0 373 feature.timestamp = timestamp;
c@0 374 feature.hasTimestamp = true;
c@0 375 featureSet[0].push_back(feature);
c@0 376 }
c@0 377
c@0 378 //Make sure this is called at the end of the function
c@0 379 cleanupForGRF();
c@0 380
c@0 381 return featureSet;
c@0 382 }