Mercurial > hg > btrack
comparison src/BTrack.cpp @ 20:baf35f208814 develop
Replaced switch statements in OnsetDetectionFunction with enums. Renamed lots of functions so that they have better names, in camel case. Added some unit tests for initialisation of BTrack.
author | Adam <adamstark.uk@gmail.com> |
---|---|
date | Thu, 23 Jan 2014 15:31:11 +0000 |
parents | 88c8d3862eee |
children | ef4721e7466c |
comparison
equal
deleted
inserted
replaced
19:88c8d3862eee | 20:baf35f208814 |
---|---|
23 #include <algorithm> | 23 #include <algorithm> |
24 #include "BTrack.h" | 24 #include "BTrack.h" |
25 #include "samplerate.h" | 25 #include "samplerate.h" |
26 | 26 |
27 //======================================================================= | 27 //======================================================================= |
28 BTrack::BTrack() : odf(512,1024,6,1) | 28 BTrack::BTrack() : odf(512,1024,ComplexSpectralDifferenceHWR,HanningWindow) |
29 { | 29 { |
30 initialise(512, 1024); | 30 initialise(512, 1024); |
31 } | 31 } |
32 | 32 |
33 //======================================================================= | 33 //======================================================================= |
34 BTrack::BTrack(int hopSize) : odf(hopSize,2*hopSize,6,1) | 34 BTrack::BTrack(int hopSize_) : odf(hopSize_,2*hopSize_,ComplexSpectralDifferenceHWR,HanningWindow) |
35 { | 35 { |
36 initialise(hopSize, 2*hopSize); | 36 initialise(hopSize_, 2*hopSize_); |
37 } | 37 } |
38 | 38 |
39 //======================================================================= | 39 //======================================================================= |
40 BTrack::BTrack(int hopSize,int frameSize) : odf(hopSize,frameSize,6,1) | 40 BTrack::BTrack(int hopSize_,int frameSize_) : odf(hopSize_,frameSize_,ComplexSpectralDifferenceHWR,HanningWindow) |
41 { | 41 { |
42 initialise(hopSize, frameSize); | 42 initialise(hopSize_, frameSize_); |
43 } | 43 } |
44 | 44 |
45 //======================================================================= | 45 //======================================================================= |
46 double BTrack::getBeatTimeInSeconds(long frameNumber,int hopSize,int fs) | 46 double BTrack::getBeatTimeInSeconds(long frameNumber,int hopSize,int fs) |
47 { | 47 { |
61 } | 61 } |
62 | 62 |
63 | 63 |
64 | 64 |
65 //======================================================================= | 65 //======================================================================= |
66 void BTrack::initialise(int hopSize, int frameSize) | 66 void BTrack::initialise(int hopSize_, int frameSize_) |
67 { | 67 { |
68 double rayparam = 43; | 68 double rayparam = 43; |
69 double pi = 3.14159265; | 69 double pi = 3.14159265; |
70 | 70 |
71 | 71 |
77 p_fact = 60.*44100./512.; | 77 p_fact = 60.*44100./512.; |
78 | 78 |
79 m0 = 10; | 79 m0 = 10; |
80 beat = -1; | 80 beat = -1; |
81 | 81 |
82 playbeat = 0; | 82 beatDueInFrame = false; |
83 | 83 |
84 | 84 |
85 | 85 |
86 | 86 |
87 // create rayleigh weighting vector | 87 // create rayleigh weighting vector |
113 | 113 |
114 // tempo is not fixed | 114 // tempo is not fixed |
115 tempofix = 0; | 115 tempofix = 0; |
116 | 116 |
117 // initialise algorithm given the hopsize | 117 // initialise algorithm given the hopsize |
118 setHopSize(hopSize); | 118 setHopSize(hopSize_); |
119 } | 119 } |
120 | 120 |
121 //======================================================================= | 121 //======================================================================= |
122 void BTrack :: setHopSize(int hopSize) | 122 void BTrack::setHopSize(int hopSize_) |
123 { | 123 { |
124 framesize = hopSize; | 124 hopSize = hopSize_; |
125 dfbuffer_size = (512*512)/hopSize; // calculate df buffer size | 125 dfbuffer_size = (512*512)/hopSize; // calculate df buffer size |
126 | 126 |
127 bperiod = round(60/((((double) hopSize)/44100)*tempo)); | 127 beatPeriod = round(60/((((double) hopSize)/44100)*tempo)); |
128 | 128 |
129 dfbuffer = new double[dfbuffer_size]; // create df_buffer | 129 dfbuffer = new double[dfbuffer_size]; // create df_buffer |
130 cumscore = new double[dfbuffer_size]; // create cumscore | 130 cumscore = new double[dfbuffer_size]; // create cumscore |
131 | 131 |
132 | 132 |
135 { | 135 { |
136 dfbuffer[i] = 0; | 136 dfbuffer[i] = 0; |
137 cumscore[i] = 0; | 137 cumscore[i] = 0; |
138 | 138 |
139 | 139 |
140 if ((i % ((int) round(bperiod))) == 0) | 140 if ((i % ((int) round(beatPeriod))) == 0) |
141 { | 141 { |
142 dfbuffer[i] = 1; | 142 dfbuffer[i] = 1; |
143 } | 143 } |
144 } | 144 } |
145 } | |
146 | |
147 //======================================================================= | |
148 bool BTrack::beatDueInCurrentFrame() | |
149 { | |
150 return beatDueInFrame; | |
151 } | |
152 | |
153 //======================================================================= | |
154 int BTrack::getHopSize() | |
155 { | |
156 return hopSize; | |
145 } | 157 } |
146 | 158 |
147 //======================================================================= | 159 //======================================================================= |
148 void BTrack::processAudioFrame(double *frame) | 160 void BTrack::processAudioFrame(double *frame) |
149 { | 161 { |
167 // to zero. this is to avoid problems further down the line | 179 // to zero. this is to avoid problems further down the line |
168 newSample = newSample + 0.0001; | 180 newSample = newSample + 0.0001; |
169 | 181 |
170 m0--; | 182 m0--; |
171 beat--; | 183 beat--; |
172 playbeat = 0; | 184 beatDueInFrame = false; |
173 | 185 |
174 // move all samples back one step | 186 // move all samples back one step |
175 for (int i=0;i < (dfbuffer_size-1);i++) | 187 for (int i=0;i < (dfbuffer_size-1);i++) |
176 { | 188 { |
177 dfbuffer[i] = dfbuffer[i+1]; | 189 dfbuffer[i] = dfbuffer[i+1]; |
179 | 191 |
180 // add new sample at the end | 192 // add new sample at the end |
181 dfbuffer[dfbuffer_size-1] = newSample; | 193 dfbuffer[dfbuffer_size-1] = newSample; |
182 | 194 |
183 // update cumulative score | 195 // update cumulative score |
184 updatecumscore(newSample); | 196 updateCumulativeScore(newSample); |
185 | 197 |
186 // if we are halfway between beats | 198 // if we are halfway between beats |
187 if (m0 == 0) | 199 if (m0 == 0) |
188 { | 200 { |
189 predictbeat(); | 201 predictBeat(); |
190 } | 202 } |
191 | 203 |
192 // if we are at a beat | 204 // if we are at a beat |
193 if (beat == 0) | 205 if (beat == 0) |
194 { | 206 { |
195 playbeat = 1; // indicate a beat should be output | 207 beatDueInFrame = true; // indicate a beat should be output |
196 | 208 |
197 // recalculate the tempo | 209 // recalculate the tempo |
198 dfconvert(); | 210 resampleOnsetDetectionFunction(); |
199 calcTempo(); | 211 calculateTempo(); |
200 } | 212 } |
201 } | 213 } |
202 | 214 |
203 //======================================================================= | 215 //======================================================================= |
204 void BTrack :: settempo(double tempo) | 216 void BTrack::setTempo(double tempo) |
205 { | 217 { |
206 | 218 |
207 /////////// TEMPO INDICATION RESET ////////////////// | 219 /////////// TEMPO INDICATION RESET ////////////////// |
208 | 220 |
209 // firstly make sure tempo is between 80 and 160 bpm.. | 221 // firstly make sure tempo is between 80 and 160 bpm.. |
231 | 243 |
232 | 244 |
233 /////////// CUMULATIVE SCORE ARTIFICAL TEMPO UPDATE ////////////////// | 245 /////////// CUMULATIVE SCORE ARTIFICAL TEMPO UPDATE ////////////////// |
234 | 246 |
235 // calculate new beat period | 247 // calculate new beat period |
236 int new_bperiod = (int) round(60/((((double) framesize)/44100)*tempo)); | 248 int new_bperiod = (int) round(60/((((double) hopSize)/44100)*tempo)); |
237 | 249 |
238 int bcounter = 1; | 250 int bcounter = 1; |
239 // initialise df_buffer to zeros | 251 // initialise df_buffer to zeros |
240 for (int i = (dfbuffer_size-1);i >= 0;i--) | 252 for (int i = (dfbuffer_size-1);i >= 0;i--) |
241 { | 253 { |
266 // offbeat is half of new beat period away | 278 // offbeat is half of new beat period away |
267 m0 = (int) round(((double) new_bperiod)/2); | 279 m0 = (int) round(((double) new_bperiod)/2); |
268 } | 280 } |
269 | 281 |
270 //======================================================================= | 282 //======================================================================= |
271 void BTrack :: fixtempo(double tempo) | 283 void BTrack::fixTempo(double tempo) |
272 { | 284 { |
273 // firstly make sure tempo is between 80 and 160 bpm.. | 285 // firstly make sure tempo is between 80 and 160 bpm.. |
274 while (tempo > 160) | 286 while (tempo > 160) |
275 { | 287 { |
276 tempo = tempo/2; | 288 tempo = tempo/2; |
296 // set the tempo fix flag | 308 // set the tempo fix flag |
297 tempofix = 1; | 309 tempofix = 1; |
298 } | 310 } |
299 | 311 |
300 //======================================================================= | 312 //======================================================================= |
301 void BTrack :: unfixtempo() | 313 void BTrack::doNotFixTempo() |
302 { | 314 { |
303 // set the tempo fix flag | 315 // set the tempo fix flag |
304 tempofix = 0; | 316 tempofix = 0; |
305 } | 317 } |
306 | 318 |
307 //======================================================================= | 319 //======================================================================= |
308 void BTrack :: dfconvert() | 320 void BTrack::resampleOnsetDetectionFunction() |
309 { | 321 { |
310 float output[512]; | 322 float output[512]; |
311 float input[dfbuffer_size]; | 323 float input[dfbuffer_size]; |
312 | 324 |
313 for (int i = 0;i < dfbuffer_size;i++) | 325 for (int i = 0;i < dfbuffer_size;i++) |
338 df512[i] = (double) src_data.data_out[i]; | 350 df512[i] = (double) src_data.data_out[i]; |
339 } | 351 } |
340 } | 352 } |
341 | 353 |
342 //======================================================================= | 354 //======================================================================= |
343 void BTrack :: calcTempo() | 355 void BTrack::calculateTempo() |
344 { | 356 { |
345 // adaptive threshold on input | 357 // adaptive threshold on input |
346 adapt_thresh(df512,512); | 358 adaptiveThreshold(df512,512); |
347 | 359 |
348 // calculate auto-correlation function of detection function | 360 // calculate auto-correlation function of detection function |
349 acf_bal(df512); | 361 calculateBalancedACF(df512); |
350 | 362 |
351 // calculate output of comb filterbank | 363 // calculate output of comb filterbank |
352 getrcfoutput(); | 364 calculateOutputOfCombFilterBank(); |
353 | 365 |
354 | 366 |
355 // adaptive threshold on rcf | 367 // adaptive threshold on rcf |
356 adapt_thresh(rcf,128); | 368 adaptiveThreshold(rcf,128); |
357 | 369 |
358 | 370 |
359 int t_index; | 371 int t_index; |
360 int t_index2; | 372 int t_index2; |
361 // calculate tempo observation vector from bperiod observation vector | 373 // calculate tempo observation vector from bperiod observation vector |
397 | 409 |
398 delta[j] = maxval*t_obs[j]; | 410 delta[j] = maxval*t_obs[j]; |
399 } | 411 } |
400 | 412 |
401 | 413 |
402 normalise(delta,41); | 414 normaliseArray(delta,41); |
403 | 415 |
404 maxind = -1; | 416 maxind = -1; |
405 maxval = -1; | 417 maxval = -1; |
406 | 418 |
407 for (int j=0;j < 41;j++) | 419 for (int j=0;j < 41;j++) |
413 } | 425 } |
414 | 426 |
415 prev_delta[j] = delta[j]; | 427 prev_delta[j] = delta[j]; |
416 } | 428 } |
417 | 429 |
418 bperiod = round((60.0*44100.0)/(((2*maxind)+80)*((double) framesize))); | 430 beatPeriod = round((60.0*44100.0)/(((2*maxind)+80)*((double) hopSize))); |
419 | 431 |
420 if (bperiod > 0) | 432 if (beatPeriod > 0) |
421 { | 433 { |
422 est_tempo = 60.0/((((double) framesize) / 44100.0)*bperiod); | 434 est_tempo = 60.0/((((double) hopSize) / 44100.0)*beatPeriod); |
423 } | 435 } |
424 | 436 } |
425 //cout << bperiod << endl; | 437 |
426 } | 438 //======================================================================= |
427 | 439 void BTrack::adaptiveThreshold(double *x,int N) |
428 //======================================================================= | |
429 void BTrack :: adapt_thresh(double *x,int N) | |
430 { | 440 { |
431 //int N = 512; // length of df | 441 //int N = 512; // length of df |
432 int i = 0; | 442 int i = 0; |
433 int k,t = 0; | 443 int k,t = 0; |
434 double x_thresh[N]; | 444 double x_thresh[N]; |
440 | 450 |
441 // find threshold for first 't' samples, where a full average cannot be computed yet | 451 // find threshold for first 't' samples, where a full average cannot be computed yet |
442 for (i = 0;i <= t;i++) | 452 for (i = 0;i <= t;i++) |
443 { | 453 { |
444 k = std::min((i+p_pre),N); | 454 k = std::min((i+p_pre),N); |
445 x_thresh[i] = mean_array(x,1,k); | 455 x_thresh[i] = calculateMeanOfArray(x,1,k); |
446 } | 456 } |
447 // find threshold for bulk of samples across a moving average from [i-p_pre,i+p_post] | 457 // find threshold for bulk of samples across a moving average from [i-p_pre,i+p_post] |
448 for (i = t+1;i < N-p_post;i++) | 458 for (i = t+1;i < N-p_post;i++) |
449 { | 459 { |
450 x_thresh[i] = mean_array(x,i-p_pre,i+p_post); | 460 x_thresh[i] = calculateMeanOfArray(x,i-p_pre,i+p_post); |
451 } | 461 } |
452 // for last few samples calculate threshold, again, not enough samples to do as above | 462 // for last few samples calculate threshold, again, not enough samples to do as above |
453 for (i = N-p_post;i < N;i++) | 463 for (i = N-p_post;i < N;i++) |
454 { | 464 { |
455 k = std::max((i-p_post),1); | 465 k = std::max((i-p_post),1); |
456 x_thresh[i] = mean_array(x,k,N); | 466 x_thresh[i] = calculateMeanOfArray(x,k,N); |
457 } | 467 } |
458 | 468 |
459 // subtract the threshold from the detection function and check that it is not less than 0 | 469 // subtract the threshold from the detection function and check that it is not less than 0 |
460 for (i = 0;i < N;i++) | 470 for (i = 0;i < N;i++) |
461 { | 471 { |
466 } | 476 } |
467 } | 477 } |
468 } | 478 } |
469 | 479 |
470 //======================================================================= | 480 //======================================================================= |
471 void BTrack :: getrcfoutput() | 481 void BTrack::calculateOutputOfCombFilterBank() |
472 { | 482 { |
473 int numelem; | 483 int numelem; |
474 | 484 |
475 for (int i = 0;i < 128;i++) | 485 for (int i = 0;i < 128;i++) |
476 { | 486 { |
490 } | 500 } |
491 } | 501 } |
492 } | 502 } |
493 | 503 |
494 //======================================================================= | 504 //======================================================================= |
495 void BTrack :: acf_bal(double *df_thresh) | 505 void BTrack::calculateBalancedACF(double *df_thresh) |
496 { | 506 { |
497 int l, n = 0; | 507 int l, n = 0; |
498 double sum, tmp; | 508 double sum, tmp; |
499 | 509 |
500 // for l lags from 0-511 | 510 // for l lags from 0-511 |
512 acf[l] = sum / (512-l); // weight by number of mults and add to acf buffer | 522 acf[l] = sum / (512-l); // weight by number of mults and add to acf buffer |
513 } | 523 } |
514 } | 524 } |
515 | 525 |
516 //======================================================================= | 526 //======================================================================= |
517 double BTrack :: mean_array(double *array,int start,int end) | 527 double BTrack::calculateMeanOfArray(double *array,int start,int end) |
518 { | 528 { |
519 int i; | 529 int i; |
520 double sum = 0; | 530 double sum = 0; |
521 | 531 |
522 int length = end - start; | 532 int length = end - start; |
536 return 0; | 546 return 0; |
537 } | 547 } |
538 } | 548 } |
539 | 549 |
540 //======================================================================= | 550 //======================================================================= |
541 void BTrack :: normalise(double *array,int N) | 551 void BTrack::normaliseArray(double *array,int N) |
542 { | 552 { |
543 double sum = 0; | 553 double sum = 0; |
544 | 554 |
545 for (int i = 0;i < N;i++) | 555 for (int i = 0;i < N;i++) |
546 { | 556 { |
558 } | 568 } |
559 } | 569 } |
560 } | 570 } |
561 | 571 |
562 //======================================================================= | 572 //======================================================================= |
563 void BTrack :: updatecumscore(double df_sample) | 573 void BTrack::updateCumulativeScore(double df_sample) |
564 { | 574 { |
565 int start, end, winsize; | 575 int start, end, winsize; |
566 double max; | 576 double max; |
567 | 577 |
568 start = dfbuffer_size - round(2*bperiod); | 578 start = dfbuffer_size - round(2*beatPeriod); |
569 end = dfbuffer_size - round(bperiod/2); | 579 end = dfbuffer_size - round(beatPeriod/2); |
570 winsize = end-start+1; | 580 winsize = end-start+1; |
571 | 581 |
572 double w1[winsize]; | 582 double w1[winsize]; |
573 double v = -2*bperiod; | 583 double v = -2*beatPeriod; |
574 double wcumscore; | 584 double wcumscore; |
575 | 585 |
576 | 586 |
577 // create window | 587 // create window |
578 for (int i = 0;i < winsize;i++) | 588 for (int i = 0;i < winsize;i++) |
579 { | 589 { |
580 w1[i] = exp((-1*pow(tightness*log(-v/bperiod),2))/2); | 590 w1[i] = exp((-1*pow(tightness*log(-v/beatPeriod),2))/2); |
581 v = v+1; | 591 v = v+1; |
582 } | 592 } |
583 | 593 |
584 // calculate new cumulative score value | 594 // calculate new cumulative score value |
585 max = 0; | 595 max = 0; |
610 //cout << cumscore[dfbuffer_size-1] << endl; | 620 //cout << cumscore[dfbuffer_size-1] << endl; |
611 | 621 |
612 } | 622 } |
613 | 623 |
614 //======================================================================= | 624 //======================================================================= |
615 void BTrack :: predictbeat() | 625 void BTrack::predictBeat() |
616 { | 626 { |
617 int winsize = (int) bperiod; | 627 int winsize = (int) beatPeriod; |
618 double fcumscore[dfbuffer_size + winsize]; | 628 double fcumscore[dfbuffer_size + winsize]; |
619 double w2[winsize]; | 629 double w2[winsize]; |
620 // copy cumscore to first part of fcumscore | 630 // copy cumscore to first part of fcumscore |
621 for (int i = 0;i < dfbuffer_size;i++) | 631 for (int i = 0;i < dfbuffer_size;i++) |
622 { | 632 { |
625 | 635 |
626 // create future window | 636 // create future window |
627 double v = 1; | 637 double v = 1; |
628 for (int i = 0;i < winsize;i++) | 638 for (int i = 0;i < winsize;i++) |
629 { | 639 { |
630 w2[i] = exp((-1*pow((v - (bperiod/2)),2)) / (2*pow((bperiod/2) ,2))); | 640 w2[i] = exp((-1*pow((v - (beatPeriod/2)),2)) / (2*pow((beatPeriod/2) ,2))); |
631 v++; | 641 v++; |
632 } | 642 } |
633 | 643 |
634 // create past window | 644 // create past window |
635 v = -2*bperiod; | 645 v = -2*beatPeriod; |
636 int start = dfbuffer_size - round(2*bperiod); | 646 int start = dfbuffer_size - round(2*beatPeriod); |
637 int end = dfbuffer_size - round(bperiod/2); | 647 int end = dfbuffer_size - round(beatPeriod/2); |
638 int pastwinsize = end-start+1; | 648 int pastwinsize = end-start+1; |
639 double w1[pastwinsize]; | 649 double w1[pastwinsize]; |
640 | 650 |
641 for (int i = 0;i < pastwinsize;i++) | 651 for (int i = 0;i < pastwinsize;i++) |
642 { | 652 { |
643 w1[i] = exp((-1*pow(tightness*log(-v/bperiod),2))/2); | 653 w1[i] = exp((-1*pow(tightness*log(-v/beatPeriod),2))/2); |
644 v = v+1; | 654 v = v+1; |
645 } | 655 } |
646 | 656 |
647 | 657 |
648 | 658 |
650 double max; | 660 double max; |
651 int n; | 661 int n; |
652 double wcumscore; | 662 double wcumscore; |
653 for (int i = dfbuffer_size;i < (dfbuffer_size+winsize);i++) | 663 for (int i = dfbuffer_size;i < (dfbuffer_size+winsize);i++) |
654 { | 664 { |
655 start = i - round(2*bperiod); | 665 start = i - round(2*beatPeriod); |
656 end = i - round(bperiod/2); | 666 end = i - round(beatPeriod/2); |
657 | 667 |
658 max = 0; | 668 max = 0; |
659 n = 0; | 669 n = 0; |
660 for (int k=start;k <= end;k++) | 670 for (int k=start;k <= end;k++) |
661 { | 671 { |
688 | 698 |
689 n++; | 699 n++; |
690 } | 700 } |
691 | 701 |
692 // set next prediction time | 702 // set next prediction time |
693 m0 = beat+round(bperiod/2); | 703 m0 = beat+round(beatPeriod/2); |
694 | 704 |
695 | 705 |
696 } | 706 } |