Mercurial > hg > btrack
comparison src/BTrack.cpp @ 97:6a4dd7478954
Replaced C-style arrays with std vectors and modernised some code
author | Adam Stark <adamstark.uk@gmail.com> |
---|---|
date | Fri, 11 Aug 2017 18:18:33 +0100 |
parents | 4aa362058011 |
children | 3b24b01fbe15 |
comparison
equal
deleted
inserted
replaced
95:4ba1239fa120 | 97:6a4dd7478954 |
---|---|
19 */ | 19 */ |
20 //======================================================================= | 20 //======================================================================= |
21 | 21 |
22 #include <cmath> | 22 #include <cmath> |
23 #include <algorithm> | 23 #include <algorithm> |
24 #include <numeric> | |
24 #include "BTrack.h" | 25 #include "BTrack.h" |
25 #include "samplerate.h" | 26 #include "samplerate.h" |
26 #include <iostream> | 27 #include <iostream> |
27 | 28 |
28 //======================================================================= | 29 //======================================================================= |
32 initialise (512, 1024); | 33 initialise (512, 1024); |
33 } | 34 } |
34 | 35 |
35 //======================================================================= | 36 //======================================================================= |
36 BTrack::BTrack (int hopSize_) | 37 BTrack::BTrack (int hopSize_) |
37 : odf(hopSize_, 2*hopSize_, ComplexSpectralDifferenceHWR, HanningWindow) | 38 : odf (hopSize_, 2 * hopSize_, ComplexSpectralDifferenceHWR, HanningWindow) |
38 { | 39 { |
39 initialise (hopSize_, 2*hopSize_); | 40 initialise (hopSize_, 2 * hopSize_); |
40 } | 41 } |
41 | 42 |
42 //======================================================================= | 43 //======================================================================= |
43 BTrack::BTrack (int hopSize_, int frameSize_) | 44 BTrack::BTrack (int hopSize_, int frameSize_) |
44 : odf (hopSize_, frameSize_, ComplexSpectralDifferenceHWR, HanningWindow) | 45 : odf (hopSize_, frameSize_, ComplexSpectralDifferenceHWR, HanningWindow) |
86 | 87 |
87 | 88 |
88 //======================================================================= | 89 //======================================================================= |
89 void BTrack::initialise (int hopSize_, int frameSize_) | 90 void BTrack::initialise (int hopSize_, int frameSize_) |
90 { | 91 { |
92 // set vector sizes | |
93 resampledOnsetDF.resize (512); | |
94 acf.resize (512); | |
95 weightingVector.resize (128); | |
96 combFilterBankOutput.resize (128); | |
97 tempoObservationVector.resize (41); | |
98 delta.resize (41); | |
99 prevDelta.resize (41); | |
100 prevDeltaFixed.resize (41); | |
101 | |
91 double rayparam = 43; | 102 double rayparam = 43; |
92 double pi = 3.14159265; | 103 double pi = 3.14159265; |
93 | 104 |
94 | 105 |
95 // initialise parameters | 106 // initialise parameters |
109 for (int n = 0; n < 128; n++) | 120 for (int n = 0; n < 128; n++) |
110 { | 121 { |
111 weightingVector[n] = ((double) n / pow(rayparam,2)) * exp((-1*pow((double)-n,2)) / (2*pow(rayparam,2))); | 122 weightingVector[n] = ((double) n / pow(rayparam,2)) * exp((-1*pow((double)-n,2)) / (2*pow(rayparam,2))); |
112 } | 123 } |
113 | 124 |
114 // initialise prev_delta | 125 // initialise prev_delta |
115 for (int i = 0; i < 41; i++) | 126 std::fill (prevDelta.begin(), prevDelta.end(), 1); |
116 { | 127 |
117 prevDelta[i] = 1; | |
118 } | |
119 | |
120 double t_mu = 41/2; | 128 double t_mu = 41/2; |
121 double m_sig; | 129 double m_sig; |
122 double x; | 130 double x; |
123 // create tempo transition matrix | 131 // create tempo transition matrix |
124 m_sig = 41/8; | 132 m_sig = 41/8; |
164 | 172 |
165 //======================================================================= | 173 //======================================================================= |
166 void BTrack::setHopSize (int hopSize_) | 174 void BTrack::setHopSize (int hopSize_) |
167 { | 175 { |
168 hopSize = hopSize_; | 176 hopSize = hopSize_; |
169 onsetDFBufferSize = (512*512)/hopSize; // calculate df buffer size | 177 onsetDFBufferSize = (512 * 512) / hopSize; // calculate df buffer size |
170 | 178 |
171 beatPeriod = round(60/((((double) hopSize)/44100)*tempo)); | 179 beatPeriod = round(60/((((double) hopSize)/44100)*tempo)); |
172 | 180 |
173 // set size of onset detection function buffer | 181 // set size of onset detection function buffer |
174 onsetDF.resize (onsetDFBufferSize); | 182 onsetDF.resize (onsetDFBufferSize); |
252 onsetDF.addSampleToEnd (newSample); | 260 onsetDF.addSampleToEnd (newSample); |
253 | 261 |
254 // update cumulative score | 262 // update cumulative score |
255 updateCumulativeScore (newSample); | 263 updateCumulativeScore (newSample); |
256 | 264 |
257 // if we are halfway between beats | 265 // if we are halfway between beats, predict a beat |
258 if (m0 == 0) | 266 if (m0 == 0) |
259 { | 267 predictBeat(); |
260 predictBeat(); | |
261 } | |
262 | 268 |
263 // if we are at a beat | 269 // if we are at a beat |
264 if (beatCounter == 0) | 270 if (beatCounter == 0) |
265 { | 271 { |
266 beatDueInFrame = true; // indicate a beat should be output | 272 beatDueInFrame = true; // indicate a beat should be output |
271 } | 277 } |
272 } | 278 } |
273 | 279 |
274 //======================================================================= | 280 //======================================================================= |
275 void BTrack::setTempo (double tempo) | 281 void BTrack::setTempo (double tempo) |
276 { | 282 { |
277 | |
278 /////////// TEMPO INDICATION RESET ////////////////// | 283 /////////// TEMPO INDICATION RESET ////////////////// |
279 | 284 |
280 // firstly make sure tempo is between 80 and 160 bpm.. | 285 // firstly make sure tempo is between 80 and 160 bpm.. |
281 while (tempo > 160) | 286 while (tempo > 160) |
282 { | 287 tempo = tempo / 2; |
283 tempo = tempo/2; | |
284 } | |
285 | 288 |
286 while (tempo < 80) | 289 while (tempo < 80) |
287 { | 290 tempo = tempo * 2; |
288 tempo = tempo * 2; | |
289 } | |
290 | 291 |
291 // convert tempo from bpm value to integer index of tempo probability | 292 // convert tempo from bpm value to integer index of tempo probability |
292 int tempo_index = (int) round((tempo - 80)/2); | 293 int tempo_index = (int) round((tempo - 80)/2); |
293 | 294 |
294 // now set previous tempo observations to zero | 295 // now set previous tempo observations to zero and set desired tempo index to 1 |
295 for (int i=0;i < 41;i++) | 296 std::fill (prevDelta.begin(), prevDelta.end(), 0); |
296 { | |
297 prevDelta[i] = 0; | |
298 } | |
299 | |
300 // set desired tempo index to 1 | |
301 prevDelta[tempo_index] = 1; | 297 prevDelta[tempo_index] = 1; |
302 | 298 |
303 | |
304 /////////// CUMULATIVE SCORE ARTIFICAL TEMPO UPDATE ////////////////// | 299 /////////// CUMULATIVE SCORE ARTIFICAL TEMPO UPDATE ////////////////// |
305 | 300 |
306 // calculate new beat period | 301 // calculate new beat period |
307 int new_bperiod = (int) round(60/((((double) hopSize)/44100)*tempo)); | 302 int newBeatPeriod = (int) round (60 / ((((double) hopSize) / 44100) * tempo)); |
308 | 303 |
309 int bcounter = 1; | 304 int k = 1; |
310 // initialise df_buffer to zeros | 305 |
311 for (int i = (onsetDFBufferSize-1);i >= 0;i--) | 306 // initialise onset detection function with delta functions spaced |
312 { | 307 // at the new beat period |
313 if (bcounter == 1) | 308 for (int i = onsetDFBufferSize - 1; i >= 0; i--) |
309 { | |
310 if (k == 1) | |
314 { | 311 { |
315 cumulativeScore[i] = 150; | 312 cumulativeScore[i] = 150; |
316 onsetDF[i] = 150; | 313 onsetDF[i] = 150; |
317 } | 314 } |
318 else | 315 else |
319 { | 316 { |
320 cumulativeScore[i] = 10; | 317 cumulativeScore[i] = 10; |
321 onsetDF[i] = 10; | 318 onsetDF[i] = 10; |
322 } | 319 } |
323 | 320 |
324 bcounter++; | 321 k++; |
325 | 322 |
326 if (bcounter > new_bperiod) | 323 if (k > newBeatPeriod) |
327 { | 324 { |
328 bcounter = 1; | 325 k = 1; |
329 } | 326 } |
330 } | 327 } |
331 | 328 |
332 /////////// INDICATE THAT THIS IS A BEAT ////////////////// | 329 /////////// INDICATE THAT THIS IS A BEAT ////////////////// |
333 | 330 |
334 // beat is now | 331 // beat is now |
335 beatCounter = 0; | 332 beatCounter = 0; |
336 | 333 |
337 // offbeat is half of new beat period away | 334 // offbeat is half of new beat period away |
338 m0 = (int) round(((double) new_bperiod)/2); | 335 m0 = (int) round (((double) newBeatPeriod) / 2); |
339 } | 336 } |
340 | 337 |
341 //======================================================================= | 338 //======================================================================= |
342 void BTrack::fixTempo (double tempo) | 339 void BTrack::fixTempo (double tempo) |
343 { | 340 { |
377 | 374 |
378 //======================================================================= | 375 //======================================================================= |
379 void BTrack::resampleOnsetDetectionFunction() | 376 void BTrack::resampleOnsetDetectionFunction() |
380 { | 377 { |
381 float output[512]; | 378 float output[512]; |
382 | |
383 float input[onsetDFBufferSize]; | 379 float input[onsetDFBufferSize]; |
384 | 380 |
385 for (int i = 0;i < onsetDFBufferSize;i++) | 381 for (int i = 0;i < onsetDFBufferSize;i++) |
386 { | |
387 input[i] = (float) onsetDF[i]; | 382 input[i] = (float) onsetDF[i]; |
388 } | |
389 | 383 |
390 double src_ratio = 512.0/((double) onsetDFBufferSize); | 384 double ratio = 512.0 / ((double) onsetDFBufferSize); |
391 int BUFFER_LEN = onsetDFBufferSize; | 385 int bufferLength = onsetDFBufferSize; |
392 int output_len; | 386 int outputLength = 512; |
393 SRC_DATA src_data ; | 387 |
394 | 388 SRC_DATA src_data; |
395 //output_len = (int) floor (((double) BUFFER_LEN) * src_ratio) ; | |
396 output_len = 512; | |
397 | |
398 src_data.data_in = input; | 389 src_data.data_in = input; |
399 src_data.input_frames = BUFFER_LEN; | 390 src_data.input_frames = bufferLength; |
400 | 391 src_data.src_ratio = ratio; |
401 src_data.src_ratio = src_ratio; | |
402 | |
403 src_data.data_out = output; | 392 src_data.data_out = output; |
404 src_data.output_frames = output_len; | 393 src_data.output_frames = outputLength; |
405 | 394 |
406 src_simple (&src_data, SRC_SINC_BEST_QUALITY, 1); | 395 src_simple (&src_data, SRC_SINC_BEST_QUALITY, 1); |
407 | 396 |
408 for (int i = 0;i < output_len;i++) | 397 for (int i = 0; i < outputLength; i++) |
409 { | |
410 resampledOnsetDF[i] = (double) src_data.data_out[i]; | 398 resampledOnsetDF[i] = (double) src_data.data_out[i]; |
411 } | |
412 } | 399 } |
413 | 400 |
414 //======================================================================= | 401 //======================================================================= |
415 void BTrack::calculateTempo() | 402 void BTrack::calculateTempo() |
416 { | 403 { |
417 // adaptive threshold on input | 404 // adaptive threshold on input |
418 adaptiveThreshold (resampledOnsetDF,512); | 405 adaptiveThreshold (resampledOnsetDF, 512); |
419 | 406 |
420 // calculate auto-correlation function of detection function | 407 // calculate auto-correlation function of detection function |
421 calculateBalancedACF (resampledOnsetDF); | 408 calculateBalancedACF (&resampledOnsetDF[0]); |
422 | 409 |
423 // calculate output of comb filterbank | 410 // calculate output of comb filterbank |
424 calculateOutputOfCombFilterBank(); | 411 calculateOutputOfCombFilterBank(); |
425 | 412 |
426 // adaptive threshold on rcf | 413 // adaptive threshold on rcf |
427 adaptiveThreshold (combFilterBankOutput,128); | 414 adaptiveThreshold (combFilterBankOutput, 128); |
428 | 415 |
429 | 416 |
430 int t_index; | 417 int t_index; |
431 int t_index2; | 418 int t_index2; |
432 // calculate tempo observation vector from beat period observation vector | 419 // calculate tempo observation vector from beat period observation vector |
468 | 455 |
469 delta[j] = maxval * tempoObservationVector[j]; | 456 delta[j] = maxval * tempoObservationVector[j]; |
470 } | 457 } |
471 | 458 |
472 | 459 |
473 normaliseArray(delta,41); | 460 normaliseArray (delta); |
474 | 461 |
475 maxind = -1; | 462 maxind = -1; |
476 maxval = -1; | 463 maxval = -1; |
477 | 464 |
478 for (int j=0;j < 41;j++) | 465 for (int j=0;j < 41;j++) |
493 estimatedTempo = 60.0/((((double) hopSize) / 44100.0) * beatPeriod); | 480 estimatedTempo = 60.0/((((double) hopSize) / 44100.0) * beatPeriod); |
494 } | 481 } |
495 } | 482 } |
496 | 483 |
497 //======================================================================= | 484 //======================================================================= |
498 void BTrack::adaptiveThreshold (double* x, int N) | 485 void BTrack::adaptiveThreshold (std::vector<double>& x, int N) |
499 { | 486 { |
500 int i = 0; | 487 int i = 0; |
501 int k,t = 0; | 488 int k,t = 0; |
502 double x_thresh[N]; | 489 double x_thresh[N]; |
503 | 490 |
505 int p_pre = 8; | 492 int p_pre = 8; |
506 | 493 |
507 t = std::min(N,p_post); // what is smaller, p_post of df size. This is to avoid accessing outside of arrays | 494 t = std::min(N,p_post); // what is smaller, p_post of df size. This is to avoid accessing outside of arrays |
508 | 495 |
509 // find threshold for first 't' samples, where a full average cannot be computed yet | 496 // find threshold for first 't' samples, where a full average cannot be computed yet |
510 for (i = 0;i <= t;i++) | 497 for (i = 0; i <= t; i++) |
511 { | 498 { |
512 k = std::min ((i+p_pre),N); | 499 k = std::min ((i + p_pre), N); |
513 x_thresh[i] = calculateMeanOfArray (x,1,k); | 500 x_thresh[i] = calculateMeanOfArray (x, 1, k); |
514 } | 501 } |
515 // find threshold for bulk of samples across a moving average from [i-p_pre,i+p_post] | 502 // find threshold for bulk of samples across a moving average from [i-p_pre,i+p_post] |
516 for (i = t+1;i < N-p_post;i++) | 503 for (i = t + 1; i < N - p_post; i++) |
517 { | 504 { |
518 x_thresh[i] = calculateMeanOfArray (x,i-p_pre,i+p_post); | 505 x_thresh[i] = calculateMeanOfArray (x, i - p_pre, i + p_post); |
519 } | 506 } |
520 // for last few samples calculate threshold, again, not enough samples to do as above | 507 // for last few samples calculate threshold, again, not enough samples to do as above |
521 for (i = N-p_post;i < N;i++) | 508 for (i = N - p_post; i < N; i++) |
522 { | 509 { |
523 k = std::max ((i-p_post),1); | 510 k = std::max ((i - p_post), 1); |
524 x_thresh[i] = calculateMeanOfArray (x,k,N); | 511 x_thresh[i] = calculateMeanOfArray (x, k, N); |
525 } | 512 } |
526 | 513 |
527 // subtract the threshold from the detection function and check that it is not less than 0 | 514 // subtract the threshold from the detection function and check that it is not less than 0 |
528 for (i = 0; i < N; i++) | 515 for (i = 0; i < N; i++) |
529 { | 516 { |
650 lag = lag - 1.; | 637 lag = lag - 1.; |
651 } | 638 } |
652 } | 639 } |
653 | 640 |
654 //======================================================================= | 641 //======================================================================= |
655 double BTrack::calculateMeanOfArray (double* array, int startIndex, int endIndex) | 642 double BTrack::calculateMeanOfArray (std::vector<double>& array, int startIndex, int endIndex) |
656 { | 643 { |
657 int i; | |
658 double sum = 0; | |
659 | |
660 int length = endIndex - startIndex; | 644 int length = endIndex - startIndex; |
661 | 645 double sum = std::accumulate (array.begin() + startIndex, array.begin() + endIndex, 0.0); |
662 // find sum | 646 |
663 for (i = startIndex; i < endIndex; i++) | |
664 { | |
665 sum = sum + array[i]; | |
666 } | |
667 | |
668 if (length > 0) | 647 if (length > 0) |
648 return sum / static_cast<double> (length); // average and return | |
649 else | |
650 return 0; | |
651 } | |
652 | |
653 //======================================================================= | |
654 void BTrack::normaliseArray (std::vector<double>& array) | |
655 { | |
656 double sum = std::accumulate (array.begin(), array.end(), 0.0); | |
657 | |
658 if (sum > 0) | |
669 { | 659 { |
670 return sum / length; // average and return | 660 for (int i = 0; i < array.size(); i++) |
661 array[i] = array[i] / sum; | |
671 } | 662 } |
672 else | |
673 { | |
674 return 0; | |
675 } | |
676 } | |
677 | |
678 //======================================================================= | |
679 void BTrack::normaliseArray (double* array, int N) | |
680 { | |
681 double sum = 0; | |
682 | |
683 for (int i = 0; i < N; i++) | |
684 { | |
685 if (array[i] > 0) | |
686 { | |
687 sum = sum + array[i]; | |
688 } | |
689 } | |
690 | |
691 if (sum > 0) | |
692 { | |
693 for (int i = 0; i < N; i++) | |
694 { | |
695 array[i] = array[i] / sum; | |
696 } | |
697 } | |
698 } | 663 } |
699 | 664 |
700 //======================================================================= | 665 //======================================================================= |
701 void BTrack::updateCumulativeScore (double odfSample) | 666 void BTrack::updateCumulativeScore (double odfSample) |
702 { | 667 { |
703 int start, end, winsize; | 668 int start, end, winsize; |
704 double max; | 669 double max; |
705 | 670 |
706 start = onsetDFBufferSize - round (2 * beatPeriod); | 671 start = onsetDFBufferSize - round (2. * beatPeriod); |
707 end = onsetDFBufferSize - round (beatPeriod / 2); | 672 end = onsetDFBufferSize - round (beatPeriod / 2.); |
708 winsize = end-start+1; | 673 winsize = end - start + 1; |
709 | 674 |
710 double w1[winsize]; | 675 double w1[winsize]; |
711 double v = -2*beatPeriod; | 676 double v = -2. * beatPeriod; |
712 double wcumscore; | 677 double wcumscore; |
713 | 678 |
714 // create window | 679 // create window |
715 for (int i = 0; i < winsize; i++) | 680 for (int i = 0; i < winsize; i++) |
716 { | 681 { |
717 w1[i] = exp((-1 * pow (tightness * log (-v / beatPeriod), 2)) / 2); | 682 w1[i] = exp((-1 * pow (tightness * log (-v / beatPeriod), 2)) / 2); |
718 v = v+1; | 683 v = v + 1; |
719 } | 684 } |
720 | 685 |
721 // calculate new cumulative score value | 686 // calculate new cumulative score value |
722 max = 0; | 687 max = 0; |
723 int n = 0; | 688 int n = 0; |