Mercurial > hg > btrack
comparison src/BTrack.cpp @ 38:b7e3ed593fb0
flow: Created branch 'release/v0.9.0'.
author | Adam Stark <adamstark@users.noreply.github.com> |
---|---|
date | Tue, 21 Jan 2014 01:44:55 +0000 |
parents | |
children | bb3803edaa17 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 38:b7e3ed593fb0 |
---|---|
1 //======================================================================= | |
2 /** @file BTrack.cpp | |
3 * @brief BTrack - a real-time beat tracker | |
4 * @author Adam Stark | |
5 * @copyright Copyright (C) 2008-2014 Queen Mary University of London | |
6 * | |
7 * This program is free software: you can redistribute it and/or modify | |
8 * it under the terms of the GNU General Public License as published by | |
9 * the Free Software Foundation, either version 3 of the License, or | |
10 * (at your option) any later version. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, | |
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 * GNU General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU General Public License | |
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 */ | |
20 //======================================================================= | |
21 | |
22 #include <iostream> | |
23 #include <cmath> | |
24 #include "BTrack.h" | |
25 #include "samplerate.h" | |
26 using namespace std; | |
27 | |
28 | |
29 | |
30 | |
31 //------------------------------------------------------------------------------- | |
32 // Constructor | |
33 BTrack :: BTrack() | |
34 { | |
35 float rayparam = 43; | |
36 float pi = 3.14159265; | |
37 | |
38 | |
39 // initialise parameters | |
40 tightness = 5; | |
41 alpha = 0.9; | |
42 tempo = 120; | |
43 est_tempo = 120; | |
44 p_fact = 60.*44100./512.; | |
45 | |
46 m0 = 10; | |
47 beat = -1; | |
48 | |
49 playbeat = 0; | |
50 | |
51 | |
52 | |
53 | |
54 // create rayleigh weighting vector | |
55 for (int n = 0;n < 128;n++) | |
56 { | |
57 wv[n] = ((float) n / pow(rayparam,2)) * exp((-1*pow((float)-n,2)) / (2*pow(rayparam,2))); | |
58 } | |
59 | |
60 // initialise prev_delta | |
61 for (int i = 0;i < 41;i++) | |
62 { | |
63 prev_delta[i] = 1; | |
64 } | |
65 | |
66 float t_mu = 41/2; | |
67 float m_sig; | |
68 float x; | |
69 // create tempo transition matrix | |
70 m_sig = 41/8; | |
71 for (int i = 0;i < 41;i++) | |
72 { | |
73 for (int j = 0;j < 41;j++) | |
74 { | |
75 x = j+1; | |
76 t_mu = i+1; | |
77 t_tmat[i][j] = (1 / (m_sig * sqrt(2*pi))) * exp( (-1*pow((x-t_mu),2)) / (2*pow(m_sig,2)) ); | |
78 } | |
79 } | |
80 | |
81 // tempo is not fixed | |
82 tempofix = 0; | |
83 } | |
84 | |
85 //------------------------------------------------------------------------------- | |
86 // Destructor | |
87 BTrack :: ~BTrack() | |
88 { | |
89 | |
90 } | |
91 | |
92 //------------------------------------------------------------------------------- | |
93 // Initialise with frame size and set all frame sizes accordingly | |
94 void BTrack :: initialise(int fsize) | |
95 { | |
96 framesize = fsize; | |
97 dfbuffer_size = (512*512)/fsize; // calculate df buffer size | |
98 | |
99 bperiod = round(60/((((float) fsize)/44100)*tempo)); | |
100 | |
101 dfbuffer = new float[dfbuffer_size]; // create df_buffer | |
102 cumscore = new float[dfbuffer_size]; // create cumscore | |
103 | |
104 | |
105 // initialise df_buffer to zeros | |
106 for (int i = 0;i < dfbuffer_size;i++) | |
107 { | |
108 dfbuffer[i] = 0; | |
109 cumscore[i] = 0; | |
110 | |
111 | |
112 if ((i % ((int) round(bperiod))) == 0) | |
113 { | |
114 dfbuffer[i] = 1; | |
115 } | |
116 } | |
117 } | |
118 | |
119 //------------------------------------------------------------------------------- | |
120 // Add new sample to buffer and apply beat tracking | |
121 void BTrack :: process(float df_sample) | |
122 { | |
123 m0--; | |
124 beat--; | |
125 playbeat = 0; | |
126 | |
127 // move all samples back one step | |
128 for (int i=0;i < (dfbuffer_size-1);i++) | |
129 { | |
130 dfbuffer[i] = dfbuffer[i+1]; | |
131 } | |
132 | |
133 // add new sample at the end | |
134 dfbuffer[dfbuffer_size-1] = df_sample; | |
135 | |
136 // update cumulative score | |
137 updatecumscore(df_sample); | |
138 | |
139 // if we are halfway between beats | |
140 if (m0 == 0) | |
141 { | |
142 predictbeat(); | |
143 } | |
144 | |
145 // if we are at a beat | |
146 if (beat == 0) | |
147 { | |
148 playbeat = 1; // indicate a beat should be output | |
149 | |
150 // recalculate the tempo | |
151 dfconvert(); | |
152 calcTempo(); | |
153 } | |
154 } | |
155 | |
156 //------------------------------------------------------------------------------- | |
157 // Set the tempo of the beat tracker | |
158 void BTrack :: settempo(float tempo) | |
159 { | |
160 | |
161 /////////// TEMPO INDICATION RESET ////////////////// | |
162 | |
163 // firstly make sure tempo is between 80 and 160 bpm.. | |
164 while (tempo > 160) | |
165 { | |
166 tempo = tempo/2; | |
167 } | |
168 | |
169 while (tempo < 80) | |
170 { | |
171 tempo = tempo * 2; | |
172 } | |
173 | |
174 // convert tempo from bpm value to integer index of tempo probability | |
175 int tempo_index = (int) round((tempo - 80)/2); | |
176 | |
177 // now set previous tempo observations to zero | |
178 for (int i=0;i < 41;i++) | |
179 { | |
180 prev_delta[i] = 0; | |
181 } | |
182 | |
183 // set desired tempo index to 1 | |
184 prev_delta[tempo_index] = 1; | |
185 | |
186 | |
187 /////////// CUMULATIVE SCORE ARTIFICAL TEMPO UPDATE ////////////////// | |
188 | |
189 // calculate new beat period | |
190 int new_bperiod = (int) round(60/((((float) framesize)/44100)*tempo)); | |
191 | |
192 int bcounter = 1; | |
193 // initialise df_buffer to zeros | |
194 for (int i = (dfbuffer_size-1);i >= 0;i--) | |
195 { | |
196 if (bcounter == 1) | |
197 { | |
198 cumscore[i] = 150; | |
199 dfbuffer[i] = 150; | |
200 } | |
201 else | |
202 { | |
203 cumscore[i] = 10; | |
204 dfbuffer[i] = 10; | |
205 } | |
206 | |
207 bcounter++; | |
208 | |
209 if (bcounter > new_bperiod) | |
210 { | |
211 bcounter = 1; | |
212 } | |
213 } | |
214 | |
215 /////////// INDICATE THAT THIS IS A BEAT ////////////////// | |
216 | |
217 // beat is now | |
218 beat = 0; | |
219 | |
220 // offbeat is half of new beat period away | |
221 m0 = (int) round(((float) new_bperiod)/2); | |
222 } | |
223 | |
224 | |
225 //------------------------------------------------------------------------------- | |
226 // fix tempo to roughly around some value | |
227 void BTrack :: fixtempo(float tempo) | |
228 { | |
229 // firstly make sure tempo is between 80 and 160 bpm.. | |
230 while (tempo > 160) | |
231 { | |
232 tempo = tempo/2; | |
233 } | |
234 | |
235 while (tempo < 80) | |
236 { | |
237 tempo = tempo * 2; | |
238 } | |
239 | |
240 // convert tempo from bpm value to integer index of tempo probability | |
241 int tempo_index = (int) round((tempo - 80)/2); | |
242 | |
243 // now set previous fixed previous tempo observation values to zero | |
244 for (int i=0;i < 41;i++) | |
245 { | |
246 prev_delta_fix[i] = 0; | |
247 } | |
248 | |
249 // set desired tempo index to 1 | |
250 prev_delta_fix[tempo_index] = 1; | |
251 | |
252 // set the tempo fix flag | |
253 tempofix = 1; | |
254 } | |
255 | |
256 //------------------------------------------------------------------------------- | |
257 // do not fix the tempo anymore | |
258 void BTrack :: unfixtempo() | |
259 { | |
260 // set the tempo fix flag | |
261 tempofix = 0; | |
262 } | |
263 | |
264 //------------------------------------------------------------------------------- | |
265 // Convert detection function from N samples to 512 | |
266 void BTrack :: dfconvert() | |
267 { | |
268 float output[512]; | |
269 | |
270 double src_ratio = 512.0/((double) dfbuffer_size); | |
271 int BUFFER_LEN = dfbuffer_size; | |
272 int output_len; | |
273 SRC_DATA src_data ; | |
274 | |
275 //output_len = (int) floor (((double) BUFFER_LEN) * src_ratio) ; | |
276 output_len = 512; | |
277 | |
278 src_data.data_in = dfbuffer; | |
279 src_data.input_frames = BUFFER_LEN; | |
280 | |
281 src_data.src_ratio = src_ratio; | |
282 | |
283 src_data.data_out = output; | |
284 src_data.output_frames = output_len; | |
285 | |
286 src_simple (&src_data, SRC_SINC_BEST_QUALITY, 1); | |
287 | |
288 for (int i = 0;i < output_len;i++) | |
289 { | |
290 df512[i] = src_data.data_out[i]; | |
291 } | |
292 } | |
293 | |
294 //------------------------------------------------------------------------------- | |
295 // To calculate the current tempo expressed as the beat period in detection function samples | |
296 void BTrack :: calcTempo() | |
297 { | |
298 // adaptive threshold on input | |
299 adapt_thresh(df512,512); | |
300 | |
301 // calculate auto-correlation function of detection function | |
302 acf_bal(df512); | |
303 | |
304 // calculate output of comb filterbank | |
305 getrcfoutput(); | |
306 | |
307 | |
308 // adaptive threshold on rcf | |
309 adapt_thresh(rcf,128); | |
310 | |
311 | |
312 int t_index; | |
313 int t_index2; | |
314 // calculate tempo observation vector from bperiod observation vector | |
315 for (int i = 0;i < 41;i++) | |
316 { | |
317 t_index = (int) round(p_fact / ((float) ((2*i)+80))); | |
318 t_index2 = (int) round(p_fact / ((float) ((4*i)+160))); | |
319 | |
320 | |
321 t_obs[i] = rcf[t_index-1] + rcf[t_index2-1]; | |
322 } | |
323 | |
324 | |
325 float maxval; | |
326 float maxind; | |
327 float curval; | |
328 | |
329 // if tempo is fixed then always use a fixed set of tempi as the previous observation probability function | |
330 if (tempofix == 1) | |
331 { | |
332 for (int k = 0;k < 41;k++) | |
333 { | |
334 prev_delta[k] = prev_delta_fix[k]; | |
335 } | |
336 } | |
337 | |
338 for (int j=0;j < 41;j++) | |
339 { | |
340 maxval = -1; | |
341 for (int i = 0;i < 41;i++) | |
342 { | |
343 curval = prev_delta[i]*t_tmat[i][j]; | |
344 | |
345 if (curval > maxval) | |
346 { | |
347 maxval = curval; | |
348 } | |
349 } | |
350 | |
351 delta[j] = maxval*t_obs[j]; | |
352 } | |
353 | |
354 | |
355 normalise(delta,41); | |
356 | |
357 maxind = -1; | |
358 maxval = -1; | |
359 | |
360 for (int j=0;j < 41;j++) | |
361 { | |
362 if (delta[j] > maxval) | |
363 { | |
364 maxval = delta[j]; | |
365 maxind = j; | |
366 } | |
367 | |
368 prev_delta[j] = delta[j]; | |
369 } | |
370 | |
371 bperiod = round((60.0*44100.0)/(((2*maxind)+80)*((float) framesize))); | |
372 | |
373 if (bperiod > 0) | |
374 { | |
375 est_tempo = 60.0/((((float) framesize) / 44100.0)*bperiod); | |
376 } | |
377 | |
378 //cout << bperiod << endl; | |
379 } | |
380 | |
381 //------------------------------------------------------------------------------- | |
382 // calculates an adaptive threshold which is used to remove low level energy from detection function and emphasise peaks | |
383 void BTrack :: adapt_thresh(float x[],int N) | |
384 { | |
385 //int N = 512; // length of df | |
386 int i = 0; | |
387 int k,t = 0; | |
388 float x_thresh[N]; | |
389 | |
390 int p_post = 7; | |
391 int p_pre = 8; | |
392 | |
393 t = min(N,p_post); // what is smaller, p_post of df size. This is to avoid accessing outside of arrays | |
394 | |
395 // find threshold for first 't' samples, where a full average cannot be computed yet | |
396 for (i = 0;i <= t;i++) | |
397 { | |
398 k = min((i+p_pre),N); | |
399 x_thresh[i] = mean_array(x,1,k); | |
400 } | |
401 // find threshold for bulk of samples across a moving average from [i-p_pre,i+p_post] | |
402 for (i = t+1;i < N-p_post;i++) | |
403 { | |
404 x_thresh[i] = mean_array(x,i-p_pre,i+p_post); | |
405 } | |
406 // for last few samples calculate threshold, again, not enough samples to do as above | |
407 for (i = N-p_post;i < N;i++) | |
408 { | |
409 k = max((i-p_post),1); | |
410 x_thresh[i] = mean_array(x,k,N); | |
411 } | |
412 | |
413 // subtract the threshold from the detection function and check that it is not less than 0 | |
414 for (i = 0;i < N;i++) | |
415 { | |
416 x[i] = x[i] - x_thresh[i]; | |
417 if (x[i] < 0) | |
418 { | |
419 x[i] = 0; | |
420 } | |
421 } | |
422 } | |
423 | |
424 //------------------------------------------------------------------------------- | |
425 // returns the output of the comb filter | |
426 void BTrack :: getrcfoutput() | |
427 { | |
428 int numelem; | |
429 | |
430 for (int i = 0;i < 128;i++) | |
431 { | |
432 rcf[i] = 0; | |
433 } | |
434 | |
435 numelem = 4; | |
436 | |
437 for (int i = 2;i <= 127;i++) // max beat period | |
438 { | |
439 for (int a = 1;a <= numelem;a++) // number of comb elements | |
440 { | |
441 for (int b = 1-a;b <= a-1;b++) // general state using normalisation of comb elements | |
442 { | |
443 rcf[i-1] = rcf[i-1] + (acf[(a*i+b)-1]*wv[i-1])/(2*a-1); // calculate value for comb filter row | |
444 } | |
445 } | |
446 } | |
447 } | |
448 | |
449 //------------------------------------------------------------------------------- | |
450 // calculates the balanced autocorrelation of the smoothed detection function | |
451 void BTrack :: acf_bal(float df_thresh[]) | |
452 { | |
453 int l, n = 0; | |
454 float sum, tmp; | |
455 | |
456 // for l lags from 0-511 | |
457 for (l = 0;l < 512;l++) | |
458 { | |
459 sum = 0; | |
460 | |
461 // for n samples from 0 - (512-lag) | |
462 for (n = 0;n < (512-l);n++) | |
463 { | |
464 tmp = df_thresh[n] * df_thresh[n+l]; // multiply current sample n by sample (n+l) | |
465 sum = sum + tmp; // add to sum | |
466 } | |
467 | |
468 acf[l] = sum / (512-l); // weight by number of mults and add to acf buffer | |
469 } | |
470 } | |
471 | |
472 | |
473 //------------------------------------------------------------------------------- | |
474 // calculates the mean of values in an array from index locations [start,end] | |
475 float BTrack :: mean_array(float array[],int start,int end) | |
476 { | |
477 int i; | |
478 double sum = 0; | |
479 | |
480 int length = end - start; | |
481 | |
482 // find sum | |
483 for (i = start;i < end;i++) | |
484 { | |
485 sum = sum + array[i]; | |
486 } | |
487 | |
488 if (length > 0) | |
489 { | |
490 return sum / length; // average and return | |
491 } | |
492 else | |
493 { | |
494 return 0; | |
495 } | |
496 } | |
497 | |
498 //------------------------------------------------------------------------------- | |
499 // normalise the array | |
500 void BTrack :: normalise(float array[],int N) | |
501 { | |
502 double sum = 0; | |
503 | |
504 for (int i = 0;i < N;i++) | |
505 { | |
506 if (array[i] > 0) | |
507 { | |
508 sum = sum + array[i]; | |
509 } | |
510 } | |
511 | |
512 if (sum > 0) | |
513 { | |
514 for (int i = 0;i < N;i++) | |
515 { | |
516 array[i] = array[i] / sum; | |
517 } | |
518 } | |
519 } | |
520 | |
521 //------------------------------------------------------------------------------- | |
522 // plot contents of detection function buffer | |
523 void BTrack :: plotdfbuffer() | |
524 { | |
525 for (int i=0;i < dfbuffer_size;i++) | |
526 { | |
527 cout << dfbuffer[i] << endl; | |
528 } | |
529 | |
530 cout << "--------------------------------" << endl; | |
531 } | |
532 | |
533 //------------------------------------------------------------------------------- | |
534 // update the cumulative score | |
535 void BTrack :: updatecumscore(float df_sample) | |
536 { | |
537 int start, end, winsize; | |
538 float max; | |
539 | |
540 start = dfbuffer_size - round(2*bperiod); | |
541 end = dfbuffer_size - round(bperiod/2); | |
542 winsize = end-start+1; | |
543 | |
544 float w1[winsize]; | |
545 float v = -2*bperiod; | |
546 float wcumscore; | |
547 | |
548 | |
549 // create window | |
550 for (int i = 0;i < winsize;i++) | |
551 { | |
552 w1[i] = exp((-1*pow(tightness*log(-v/bperiod),2))/2); | |
553 v = v+1; | |
554 } | |
555 | |
556 // calculate new cumulative score value | |
557 max = 0; | |
558 int n = 0; | |
559 for (int i=start;i <= end;i++) | |
560 { | |
561 wcumscore = cumscore[i]*w1[n]; | |
562 | |
563 if (wcumscore > max) | |
564 { | |
565 max = wcumscore; | |
566 } | |
567 n++; | |
568 } | |
569 | |
570 | |
571 // shift cumulative score back one | |
572 for (int i = 0;i < (dfbuffer_size-1);i++) | |
573 { | |
574 cumscore[i] = cumscore[i+1]; | |
575 } | |
576 | |
577 // add new value to cumulative score | |
578 cumscore[dfbuffer_size-1] = ((1-alpha)*df_sample) + (alpha*max); | |
579 | |
580 cscoreval = cumscore[dfbuffer_size-1]; | |
581 | |
582 //cout << cumscore[dfbuffer_size-1] << endl; | |
583 | |
584 } | |
585 | |
586 //------------------------------------------------------------------------------- | |
587 // plot contents of detection function buffer | |
588 void BTrack :: predictbeat() | |
589 { | |
590 int winsize = (int) bperiod; | |
591 float fcumscore[dfbuffer_size + winsize]; | |
592 float w2[winsize]; | |
593 // copy cumscore to first part of fcumscore | |
594 for (int i = 0;i < dfbuffer_size;i++) | |
595 { | |
596 fcumscore[i] = cumscore[i]; | |
597 } | |
598 | |
599 // create future window | |
600 float v = 1; | |
601 for (int i = 0;i < winsize;i++) | |
602 { | |
603 w2[i] = exp((-1*pow((v - (bperiod/2)),2)) / (2*pow((bperiod/2) ,2))); | |
604 v++; | |
605 } | |
606 | |
607 // create past window | |
608 v = -2*bperiod; | |
609 int start = dfbuffer_size - round(2*bperiod); | |
610 int end = dfbuffer_size - round(bperiod/2); | |
611 int pastwinsize = end-start+1; | |
612 float w1[pastwinsize]; | |
613 | |
614 for (int i = 0;i < pastwinsize;i++) | |
615 { | |
616 w1[i] = exp((-1*pow(tightness*log(-v/bperiod),2))/2); | |
617 v = v+1; | |
618 } | |
619 | |
620 | |
621 | |
622 // calculate future cumulative score | |
623 float max; | |
624 int n; | |
625 float wcumscore; | |
626 for (int i = dfbuffer_size;i < (dfbuffer_size+winsize);i++) | |
627 { | |
628 start = i - round(2*bperiod); | |
629 end = i - round(bperiod/2); | |
630 | |
631 max = 0; | |
632 n = 0; | |
633 for (int k=start;k <= end;k++) | |
634 { | |
635 wcumscore = fcumscore[k]*w1[n]; | |
636 | |
637 if (wcumscore > max) | |
638 { | |
639 max = wcumscore; | |
640 } | |
641 n++; | |
642 } | |
643 | |
644 fcumscore[i] = max; | |
645 } | |
646 | |
647 | |
648 // predict beat | |
649 max = 0; | |
650 n = 0; | |
651 | |
652 for (int i = dfbuffer_size;i < (dfbuffer_size+winsize);i++) | |
653 { | |
654 wcumscore = fcumscore[i]*w2[n]; | |
655 | |
656 if (wcumscore > max) | |
657 { | |
658 max = wcumscore; | |
659 beat = n; | |
660 } | |
661 | |
662 n++; | |
663 } | |
664 | |
665 | |
666 // set beat | |
667 beat = beat; | |
668 | |
669 // set next prediction time | |
670 m0 = beat+round(bperiod/2); | |
671 | |
672 | |
673 } |