comparison TempogramPlugin.cpp @ 18:89bc9e5199d7

* Added Cyclic Tempogram output - very buggy right now!
author Carl Bussey <c.bussey@se10.qmul.ac.uk>
date Thu, 14 Aug 2014 16:43:26 +0100
parents 203551cbad47
children e90a4797e579
comparison
equal deleted inserted replaced
17:1e4c02ca8b81 18:89bc9e5199d7
12 using Vamp::RealTime; 12 using Vamp::RealTime;
13 using namespace std; 13 using namespace std;
14 14
15 TempogramPlugin::TempogramPlugin(float inputSampleRate) : 15 TempogramPlugin::TempogramPlugin(float inputSampleRate) :
16 Plugin(inputSampleRate), 16 Plugin(inputSampleRate),
17 m_blockSize(0), 17 m_inputBlockSize(0), //host parameter
18 m_stepSize(0), 18 m_inputStepSize(0), //host parameter
19 m_compressionConstant(1000), //parameter 19 m_noveltyCurveMinDB(0), //set in initialise()
20 m_minDB(0), 20 m_noveltyCurveCompressionConstant(1000), //parameter
21 m_log2WindowLength(10), //parameter 21 m_tempogramLog2WindowLength(10), //parameter
22 m_windowLength(pow((float)2,m_log2WindowLength)), 22 m_tempogramWindowLength(pow((float)2,m_tempogramLog2WindowLength)),
23 m_log2FftLength(m_log2WindowLength), 23 m_tempogramLog2FftLength(m_tempogramLog2WindowLength), //parameter
24 m_fftLength(m_windowLength), 24 m_tempogramFftLength(m_tempogramWindowLength),
25 m_log2HopSize(6), //parameter 25 m_tempogramLog2HopSize(6), //parameter
26 m_hopSize(pow((float)2,m_log2HopSize)), 26 m_tempogramHopSize(pow((float)2,m_tempogramLog2HopSize)),
27 m_minBPM(30), 27 m_tempogramMinBPM(30), //parameter
28 m_maxBPM(480), 28 m_tempogramMaxBPM(480), //parameter
29 m_minBin(0), //set in initialise() 29 m_tempogramMinBin(0), //set in initialise()
30 m_maxBin(0) //set in initialise() 30 m_tempogramMaxBin(0), //set in initialise()
31 m_cyclicTempogramMinBPM(30), //reset in initialise()
32 m_cyclicTempogramNumberOfOctaves(0), //set in initialise()
33 m_cyclicTempogramOctaveDivider(30) //parameter
31 34
32 // Also be sure to set your plugin parameters (presumably stored 35 // Also be sure to set your plugin parameters (presumably stored
33 // in member variables) to their default values here -- the host 36 // in member variables) to their default values here -- the host
34 // will not do that for you 37 // will not do that for you
35 { 38 {
189 } 192 }
190 list.push_back(d4); 193 list.push_back(d4);
191 194
192 ParameterDescriptor d5; 195 ParameterDescriptor d5;
193 d5.identifier = "minBPM"; 196 d5.identifier = "minBPM";
194 d5.name = "Tempogram Minimum BPM"; 197 d5.name = "(Cyclic) Tempogram Minimum BPM";
195 d5.description = "The minimum BPM of the tempogram output bins."; 198 d5.description = "The minimum BPM of the tempogram output bins.";
196 d5.unit = ""; 199 d5.unit = "";
197 d5.minValue = 0; 200 d5.minValue = 0;
198 d5.maxValue = 2000; 201 d5.maxValue = 2000;
199 d5.defaultValue = 30; 202 d5.defaultValue = 30;
201 d5.quantizeStep = 5; 204 d5.quantizeStep = 5;
202 list.push_back(d5); 205 list.push_back(d5);
203 206
204 ParameterDescriptor d6; 207 ParameterDescriptor d6;
205 d6.identifier = "maxBPM"; 208 d6.identifier = "maxBPM";
206 d6.name = "Tempogram Maximum BPM"; 209 d6.name = "(Cyclic) Tempogram Maximum BPM";
207 d6.description = "The minimum BPM of the tempogram output bins."; 210 d6.description = "The maximum BPM of the tempogram output bins.";
208 d6.unit = ""; 211 d6.unit = "";
209 d6.minValue = 30; 212 d6.minValue = 30;
210 d6.maxValue = 2000; 213 d6.maxValue = 2000;
211 d6.defaultValue = 480; 214 d6.defaultValue = 480;
212 d6.isQuantized = true; 215 d6.isQuantized = true;
213 d6.quantizeStep = 5; 216 d6.quantizeStep = 5;
214 list.push_back(d6); 217 list.push_back(d6);
218
219 ParameterDescriptor d7;
220 d7.identifier = "octDiv";
221 d7.name = "Cyclic Tempogram Octave Divider";
222 d7.description = "The number bins within each octave.";
223 d7.unit = "";
224 d7.minValue = 5;
225 d7.maxValue = 60;
226 d7.defaultValue = 30;
227 d7.isQuantized = true;
228 d7.quantizeStep = 1;
229 list.push_back(d7);
215 230
216 return list; 231 return list;
217 } 232 }
218 233
219 float 234 float
220 TempogramPlugin::getParameter(string identifier) const 235 TempogramPlugin::getParameter(string identifier) const
221 { 236 {
222 if (identifier == "C") { 237 if (identifier == "C") {
223 return m_compressionConstant; // return the ACTUAL current value of your parameter here! 238 return m_noveltyCurveCompressionConstant; // return the ACTUAL current value of your parameter here!
224 } 239 }
225 else if (identifier == "log2TN"){ 240 else if (identifier == "log2TN"){
226 return m_log2WindowLength; 241 return m_tempogramLog2WindowLength;
227 } 242 }
228 else if (identifier == "log2HopSize"){ 243 else if (identifier == "log2HopSize"){
229 return m_log2HopSize; 244 return m_tempogramLog2HopSize;
230 } 245 }
231 else if (identifier == "log2FftLength"){ 246 else if (identifier == "log2FftLength"){
232 return m_log2FftLength; 247 return m_tempogramLog2FftLength;
233 } 248 }
234 else if (identifier == "minBPM") { 249 else if (identifier == "minBPM") {
235 return m_minBPM; 250 return m_tempogramMinBPM;
236 } 251 }
237 else if (identifier == "maxBPM"){ 252 else if (identifier == "maxBPM"){
238 return m_maxBPM; 253 return m_tempogramMaxBPM;
254 }
255 else if (identifier == "octDiv"){
256 return m_cyclicTempogramOctaveDivider;
239 } 257 }
240 258
241 return 0; 259 return 0;
242 } 260 }
243 261
244 void 262 void
245 TempogramPlugin::setParameter(string identifier, float value) 263 TempogramPlugin::setParameter(string identifier, float value)
246 { 264 {
247 265
248 if (identifier == "C") { 266 if (identifier == "C") {
249 m_compressionConstant = value; // set the actual value of your parameter 267 m_noveltyCurveCompressionConstant = value; // set the actual value of your parameter
250 } 268 }
251 else if (identifier == "log2TN") { 269 else if (identifier == "log2TN") {
252 m_windowLength = pow(2,value); 270 m_tempogramWindowLength = pow(2,value);
253 m_log2WindowLength = value; 271 m_tempogramLog2WindowLength = value;
254 } 272 }
255 else if (identifier == "log2HopSize"){ 273 else if (identifier == "log2HopSize"){
256 m_hopSize = pow(2,value); 274 m_tempogramHopSize = pow(2,value);
257 m_log2HopSize = value; 275 m_tempogramLog2HopSize = value;
258 } 276 }
259 else if (identifier == "log2HopFftLength"){ 277 else if (identifier == "log2FftLength"){
260 m_fftLength = pow(2,value); 278 m_tempogramFftLength = pow(2,value);
261 m_log2FftLength = value; 279 m_tempogramLog2FftLength = value;
262 } 280 }
263 else if (identifier == "minBPM") { 281 else if (identifier == "minBPM") {
264 m_minBPM = value; 282 m_tempogramMinBPM = value;
265 } 283 }
266 else if (identifier == "maxBPM"){ 284 else if (identifier == "maxBPM"){
267 m_maxBPM = value; 285 m_tempogramMaxBPM = value;
286 }
287 else if (identifier == "octDiv"){
288 m_cyclicTempogramOctaveDivider = value;
268 } 289 }
269 290
270 } 291 }
271 292
272 void TempogramPlugin::updateBPMParameters(){ 293 void TempogramPlugin::updateBPMParameters(){
309 OutputList list; 330 OutputList list;
310 331
311 // See OutputDescriptor documentation for the possibilities here. 332 // See OutputDescriptor documentation for the possibilities here.
312 // Every plugin must have at least one output. 333 // Every plugin must have at least one output.
313 334
314 OutputDescriptor d; 335 OutputDescriptor d1;
315 float d_sampleRate; 336 float d_sampleRate;
316 float tempogramInputSampleRate = (float)m_inputSampleRate/m_stepSize; 337 float tempogramInputSampleRate = (float)m_inputSampleRate/m_inputStepSize;
317 338
318 d.identifier = "tempogram"; 339 d1.identifier = "tempogram";
319 d.name = "Tempogram"; 340 d1.name = "Tempogram";
320 d.description = "Tempogram"; 341 d1.description = "Tempogram";
321 d.unit = "BPM"; 342 d1.unit = "BPM";
322 d.hasFixedBinCount = true; 343 d1.hasFixedBinCount = true;
323 d.binCount = m_maxBin - m_minBin + 1; 344 d1.binCount = m_tempogramMaxBin - m_tempogramMinBin + 1;
324 d.hasKnownExtents = false; 345 d1.hasKnownExtents = false;
325 d.isQuantized = false; 346 d1.isQuantized = false;
326 d.sampleType = OutputDescriptor::FixedSampleRate; 347 d1.sampleType = OutputDescriptor::FixedSampleRate;
327 d_sampleRate = tempogramInputSampleRate/m_hopSize; 348 d_sampleRate = tempogramInputSampleRate/m_tempogramHopSize;
328 d.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0; 349 d1.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
329 for(int i = m_minBin; i <= (int)m_maxBin; i++){ 350 for(int i = m_tempogramMinBin; i <= (int)m_tempogramMaxBin; i++){
330 float w = ((float)i/m_fftLength)*(tempogramInputSampleRate); 351 float w = ((float)i/m_tempogramFftLength)*(tempogramInputSampleRate);
331 d.binNames.push_back(floatToString(w*60)); 352 d1.binNames.push_back(floatToString(w*60));
332 } 353 }
333 d.hasDuration = false; 354 d1.hasDuration = false;
334 list.push_back(d); 355 list.push_back(d1);
335 356
336 d.identifier = "nc"; 357 OutputDescriptor d2;
337 d.name = "Novelty Curve"; 358 d2.identifier = "nc";
338 d.description = "Novelty Curve"; 359 d2.name = "Novelty Curve";
339 d.unit = ""; 360 d2.description = "Novelty Curve";
340 d.hasFixedBinCount = true; 361 d2.unit = "";
341 d.binCount = 1; 362 d2.hasFixedBinCount = true;
342 d.hasKnownExtents = false; 363 d2.binCount = 1;
343 d.isQuantized = false; 364 d2.hasKnownExtents = false;
344 d.sampleType = OutputDescriptor::FixedSampleRate; 365 d2.isQuantized = false;
366 d2.sampleType = OutputDescriptor::FixedSampleRate;
345 d_sampleRate = tempogramInputSampleRate; 367 d_sampleRate = tempogramInputSampleRate;
346 d.sampleRate = d_sampleRate > 0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0; 368 d2.sampleRate = d_sampleRate > 0 && !isnan(d_sampleRate) ? d_sampleRate : 0;
347 d.hasDuration = false; 369 d2.hasDuration = false;
348 list.push_back(d); 370 list.push_back(d2);
371
372 OutputDescriptor d3;
373 d3.identifier = "cyclicTempogram";
374 d3.name = "Cyclic Tempogram";
375 d3.description = "Cyclic Tempogram";
376 d3.unit = "";
377 d3.hasFixedBinCount = true;
378 d3.binCount = m_cyclicTempogramOctaveDivider > 0 && !isnan(m_cyclicTempogramOctaveDivider) ? m_cyclicTempogramOctaveDivider : 0;
379 d3.hasKnownExtents = false;
380 d3.isQuantized = false;
381 d3.sampleType = OutputDescriptor::FixedSampleRate;
382 d_sampleRate = tempogramInputSampleRate/m_tempogramHopSize;
383 d3.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0;
384 d3.hasDuration = false;
385 list.push_back(d3);
349 386
350 return list; 387 return list;
351 } 388 }
352 389
353 bool 390 bool
355 { 392 {
356 if (channels < getMinChannelCount() || 393 if (channels < getMinChannelCount() ||
357 channels > getMaxChannelCount()) return false; 394 channels > getMaxChannelCount()) return false;
358 395
359 // Real initialisation work goes here! 396 // Real initialisation work goes here!
360 m_blockSize = blockSize; 397 m_inputBlockSize = blockSize;
361 m_stepSize = stepSize; 398 m_inputStepSize = stepSize;
362 m_minDB = pow(10,(float)-74/20); 399 m_noveltyCurveMinDB = pow(10,(float)-74/20);
363 400
364 if (m_fftLength < m_windowLength){ 401 if (m_tempogramFftLength < m_tempogramWindowLength){
365 m_fftLength = m_windowLength; 402 m_tempogramFftLength = m_tempogramWindowLength;
366 } 403 }
367 if (m_minBPM > m_maxBPM){ 404 if (m_tempogramMinBPM > m_tempogramMaxBPM){
368 m_minBPM = 30; 405 m_tempogramMinBPM = 30;
369 m_maxBPM = 480; 406 m_tempogramMaxBPM = 480;
370 } 407 }
371 float tempogramInputSampleRate = (float)m_inputSampleRate/m_stepSize; 408
372 m_minBin = (unsigned int)(max(floor(((m_minBPM/60)/tempogramInputSampleRate)*m_fftLength), (float)0.0)); 409 float tempogramInputSampleRate = (float)m_inputSampleRate/m_inputStepSize;
373 m_maxBin = (unsigned int)(min(ceil(((m_maxBPM/60)/tempogramInputSampleRate)*m_fftLength), (float)m_fftLength/2)); 410 m_tempogramMinBin = (unsigned int)(max(floor(((m_tempogramMinBPM/60)/tempogramInputSampleRate)*m_tempogramFftLength), (float)0.0));
374 411 m_tempogramMaxBin = (unsigned int)(min(ceil(((m_tempogramMaxBPM/60)/tempogramInputSampleRate)*m_tempogramFftLength), (float)m_tempogramFftLength/2));
375 m_spectrogram = SpectrogramTransposed(m_blockSize/2 + 1); 412
413 if (m_tempogramMinBPM > m_cyclicTempogramMinBPM) m_cyclicTempogramMinBPM = m_tempogramMinBPM;
414 float cyclicTempogramMaxBPM = 480;
415 if (m_tempogramMaxBPM < cyclicTempogramMaxBPM) cyclicTempogramMaxBPM = m_tempogramMaxBPM;
416
417 m_spectrogram = SpectrogramTransposed(m_inputBlockSize/2 + 1);
418
419 m_cyclicTempogramNumberOfOctaves = floor(log2(cyclicTempogramMaxBPM/m_cyclicTempogramMinBPM));
420 int numberOfBinsInFirstOctave = bpmToBin(m_cyclicTempogramMinBPM);
421 if (m_cyclicTempogramOctaveDivider < numberOfBinsInFirstOctave) m_cyclicTempogramOctaveDivider = numberOfBinsInFirstOctave;
376 422
377 return true; 423 return true;
378 } 424 }
379 425
380 void TempogramPlugin::cleanup(){ 426 void TempogramPlugin::cleanup(){
383 429
384 void 430 void
385 TempogramPlugin::reset() 431 TempogramPlugin::reset()
386 { 432 {
387 // Clear buffers, reset stored values, etc 433 // Clear buffers, reset stored values, etc
388 ncTimestamps.clear(); 434 m_spectrogram = SpectrogramTransposed(m_inputBlockSize/2 + 1);
389 m_spectrogram = SpectrogramTransposed(m_blockSize/2 + 1);
390 } 435 }
391 436
392 TempogramPlugin::FeatureSet 437 TempogramPlugin::FeatureSet
393 TempogramPlugin::process(const float *const *inputBuffers, Vamp::RealTime timestamp) 438 TempogramPlugin::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
394 { 439 {
395 size_t n = m_blockSize/2 + 1; 440 size_t n = m_inputBlockSize/2 + 1;
396 441
397 FeatureSet featureSet; 442 FeatureSet featureSet;
398 Feature feature; 443 Feature feature;
399 444
400 const float *in = inputBuffers[0]; 445 const float *in = inputBuffers[0];
401 446
402 //calculate magnitude of FrequencyDomain input 447 //calculate magnitude of FrequencyDomain input
403 for (unsigned int i = 0; i < n; i++){ 448 for (unsigned int i = 0; i < n; i++){
404 float magnitude = sqrt(in[2*i] * in[2*i] + in[2*i + 1] * in[2*i + 1]); 449 float magnitude = sqrt(in[2*i] * in[2*i] + in[2*i + 1] * in[2*i + 1]);
405 magnitude = magnitude > m_minDB ? magnitude : m_minDB; 450 magnitude = magnitude > m_noveltyCurveMinDB ? magnitude : m_noveltyCurveMinDB;
406 m_spectrogram[i].push_back(magnitude); 451 m_spectrogram[i].push_back(magnitude);
407 } 452 }
408
409 ncTimestamps.push_back(timestamp); //save timestamp
410 453
411 return featureSet; 454 return featureSet;
455 }
456
457 vector<unsigned int> TempogramPlugin::calculateTempogramNearestNeighbourLogBins() const
458 {
459 vector<unsigned int> logBins;
460
461 for (int i = 0; i < (int)ceil(m_cyclicTempogramNumberOfOctaves*m_cyclicTempogramOctaveDivider); i++){
462 float bpm = m_cyclicTempogramMinBPM*pow(2.0f, (float)i/m_cyclicTempogramOctaveDivider);
463 int bin = bpmToBin(bpm);
464
465 logBins.push_back(bin);
466 }
467
468 return logBins;
469 }
470
471 int TempogramPlugin::bpmToBin(const float &bpm) const
472 {
473 float w = (float)bpm/60;
474 float sampleRate = m_inputSampleRate/m_inputStepSize;
475 int bin = floor((float)m_tempogramFftLength*w/sampleRate + 0.5);
476
477 if(bin < 0) bin = 0;
478 else if(bin > m_tempogramFftLength/2) bin = m_tempogramFftLength;
479
480 return bin;
412 } 481 }
413 482
414 TempogramPlugin::FeatureSet 483 TempogramPlugin::FeatureSet
415 TempogramPlugin::getRemainingFeatures() 484 TempogramPlugin::getRemainingFeatures()
416 { 485 {
417 486
418 float * hannWindow = new float[m_windowLength]; 487 float * hannWindow = new float[m_tempogramWindowLength];
419 for (unsigned int i = 0; i < m_windowLength; i++){ 488 for (unsigned int i = 0; i < m_tempogramWindowLength; i++){
420 hannWindow[i] = 0.0; 489 hannWindow[i] = 0.0;
421 } 490 }
422 491
423 FeatureSet featureSet; 492 FeatureSet featureSet;
424 493
425 //initialise m_noveltyCurve processor 494 //initialise m_noveltyCurve processor
426 size_t numberOfBlocks = m_spectrogram[0].size(); 495 size_t numberOfBlocks = m_spectrogram[0].size();
427 NoveltyCurveProcessor nc(m_inputSampleRate, m_blockSize, numberOfBlocks, m_compressionConstant); 496 NoveltyCurveProcessor nc(m_inputSampleRate, m_inputBlockSize, numberOfBlocks, m_noveltyCurveCompressionConstant);
428 m_noveltyCurve = nc.spectrogramToNoveltyCurve(m_spectrogram); //calculate novelty curve from magnitude data 497 m_noveltyCurve = nc.spectrogramToNoveltyCurve(m_spectrogram); //calculate novelty curve from magnitude data
429 498
430 //push novelty curve data to featureset 1 and set timestamps 499 //push novelty curve data to featureset 1 and set timestamps
431 for (unsigned int i = 0; i < numberOfBlocks; i++){ 500 for (unsigned int i = 0; i < numberOfBlocks; i++){
432 Feature feature; 501 Feature feature;
435 //feature.timestamp = ncTimestamps[i]; 504 //feature.timestamp = ncTimestamps[i];
436 featureSet[1].push_back(feature); 505 featureSet[1].push_back(feature);
437 } 506 }
438 507
439 //window function for spectrogram 508 //window function for spectrogram
440 WindowFunction::hanning(hannWindow, m_windowLength); 509 WindowFunction::hanning(hannWindow, m_tempogramWindowLength);
441 510
442 //initialise spectrogram processor 511 //initialise spectrogram processor
443 SpectrogramProcessor spectrogramProcessor(m_windowLength, m_fftLength, m_hopSize); 512 SpectrogramProcessor spectrogramProcessor(m_tempogramWindowLength, m_tempogramFftLength, m_tempogramHopSize);
444 //compute spectrogram from novelty curve data (i.e., tempogram) 513 //compute spectrogram from novelty curve data (i.e., tempogram)
445 Spectrogram tempogram = spectrogramProcessor.process(&m_noveltyCurve[0], numberOfBlocks, hannWindow); 514 Tempogram tempogram = spectrogramProcessor.process(&m_noveltyCurve[0], numberOfBlocks, hannWindow);
446 515 delete []hannWindow;
447 int timePointer = m_hopSize-m_windowLength/2; 516 hannWindow = 0;
517
448 int tempogramLength = tempogram.size(); 518 int tempogramLength = tempogram.size();
449 519
450 //push tempogram data to featureset 0 and set timestamps. 520 //push tempogram data to featureset 0 and set timestamps.
451 for (int block = 0; block < tempogramLength; block++){ 521 for (int block = 0; block < tempogramLength; block++){
452 Feature feature; 522 Feature feature;
453 523
454 //int timeMS = floor(1000*(m_stepSize*timePointer)/m_inputSampleRate + 0.5); 524 assert(tempogram[block].size() == (m_tempogramFftLength/2 + 1));
455 525 for(int k = m_tempogramMinBin; k < (int)m_tempogramMaxBin; k++){
456 assert(tempogram[block].size() == (m_fftLength/2 + 1));
457 for(int k = m_minBin; k < (int)m_maxBin; k++){
458 feature.values.push_back(tempogram[block][k]); 526 feature.values.push_back(tempogram[block][k]);
459 //cout << tempogram[k][block] << endl;
460 } 527 }
461 feature.hasTimestamp = false; 528 feature.hasTimestamp = false;
462 //feature.timestamp = RealTime::fromMilliseconds(timeMS);
463 featureSet[0].push_back(feature); 529 featureSet[0].push_back(feature);
530 }
531
532 //Calculate cyclic tempogram
533 vector<unsigned int> logBins = calculateTempogramNearestNeighbourLogBins();
534 Tempogram cyclicTempogram;
535
536 for (int block = 0; block < tempogramLength; block++){
537 Feature feature;
464 538
465 timePointer += m_hopSize; 539 for (int i = 0; i < m_cyclicTempogramOctaveDivider; i++){
466 } 540 float sum = 0;
467 541 for (int j = 0; j < m_cyclicTempogramNumberOfOctaves; j++){
468 delete []hannWindow; 542 sum += tempogram[block][logBins[i+j*m_cyclicTempogramOctaveDivider]];
469 hannWindow = 0; 543 }
544 feature.values.push_back(sum/m_cyclicTempogramNumberOfOctaves);
545 }
546
547 feature.hasTimestamp = false;
548
549 featureSet[2].push_back(feature);
550 }
470 551
471 return featureSet; 552 return featureSet;
472 } 553 }