annotate Tempogram.cpp @ 7:21147df9cb2d

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