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 }