comparison src/OnsetDetectionFunction.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 2b94d3d2fb9d
comparison
equal deleted inserted replaced
-1:000000000000 38:b7e3ed593fb0
1 //=======================================================================
2 /** @file OnsetDetectionFunction.cpp
3 * @brief A class for calculating onset detection functions
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 <math.h>
23 #include <iostream>
24 #include "OnsetDetectionFunction.h"
25 using namespace std;
26
27 //-------------------------------------------------------------------------------
28 // Constructor
29 OnsetDetectionFunction :: OnsetDetectionFunction()
30 {
31 // indicate that we have not initialised yet
32 initialised = 0;
33
34 // set pi
35 pi = 3.14159265358979;
36
37 // initialise with hopsize = 512, framesize = 1024, complex spectral difference DF and hanning window
38 initialise(512,1024,6,1);
39 }
40
41
42 //-------------------------------------------------------------------------------
43 // Constructor (with arguments)
44 OnsetDetectionFunction :: OnsetDetectionFunction(int arg_hsize,int arg_fsize,int arg_df_type,int arg_win_type)
45 {
46 // indicate that we have not initialised yet
47 initialised = 0;
48
49 // set pi
50 pi = 3.14159265358979;
51
52 // initialise with arguments to constructor
53 initialise(arg_hsize,arg_fsize,arg_df_type,arg_win_type);
54 }
55
56
57 //--------------------------------------------------------------------------------------
58 // Destructor
59 OnsetDetectionFunction :: ~OnsetDetectionFunction()
60 {
61 // destroy fft plan
62 fftw_destroy_plan(p);
63 fftw_free(in);
64 fftw_free(out);
65
66 // deallocate memory
67 delete [] frame;
68 frame = NULL;
69 delete [] window;
70 window = NULL;
71 delete [] wframe;
72 wframe = NULL;
73 delete [] mag;
74 mag = NULL;
75 delete [] mag_old;
76 mag_old = NULL;
77 delete [] phase;
78 phase = NULL;
79 delete [] phase_old;
80 phase_old = NULL;
81 delete [] phase_old_2;
82 phase_old_2 = NULL;
83 }
84
85 //-------------------------------------------------------------------------------
86 // Initialisation
87 void OnsetDetectionFunction :: initialise(int arg_hsize,int arg_fsize,int arg_df_type,int arg_win_type)
88 {
89 if (initialised == 1) // if we have already initialised some buffers and an FFT plan
90 {
91 //////////////////////////////////
92 // TIDY UP FIRST - If initialise is called after the class has been initialised
93 // then we want to free up memory and cancel existing FFT plans
94
95 // destroy fft plan
96 fftw_destroy_plan(p);
97 fftw_free(in);
98 fftw_free(out);
99
100
101 // deallocate memory
102 delete [] frame;
103 frame = NULL;
104 delete [] window;
105 window = NULL;
106 delete [] wframe;
107 wframe = NULL;
108 delete [] mag;
109 mag = NULL;
110 delete [] mag_old;
111 mag_old = NULL;
112 delete [] phase;
113 phase = NULL;
114 delete [] phase_old;
115 phase_old = NULL;
116 delete [] phase_old_2;
117 phase_old_2 = NULL;
118
119 ////// END TIDY UP ///////////////
120 //////////////////////////////////
121 }
122
123 hopsize = arg_hsize; // set hopsize
124 framesize = arg_fsize; // set framesize
125
126 df_type = arg_df_type; // set detection function type
127
128 // initialise buffers
129 frame = new double[framesize];
130 window = new double[framesize];
131 wframe = new double[framesize];
132
133 mag = new double[framesize];
134 mag_old = new double[framesize];
135
136 phase = new double[framesize];
137 phase_old = new double[framesize];
138 phase_old_2 = new double[framesize];
139
140
141 // set the window to the specified type
142 switch (arg_win_type){
143 case 0:
144 set_win_rectangular(); // Rectangular window
145 break;
146 case 1:
147 set_win_hanning(); // Hanning Window
148 break;
149 case 2:
150 set_win_hamming(); // Hamming Window
151 break;
152 case 3:
153 set_win_blackman(); // Blackman Window
154 break;
155 case 4:
156 set_win_tukey(); // Tukey Window
157 break;
158 default:
159 set_win_hanning(); // DEFAULT: Hanning Window
160 }
161
162
163
164
165 // initialise previous magnitude spectrum to zero
166 for (int i = 0;i < framesize;i++)
167 {
168 mag_old[i] = 0.0;
169 phase_old[i] = 0.0;
170 phase_old_2[i] = 0.0;
171 frame[i] = 0.0;
172 }
173
174 energy_sum_old = 0.0; // initialise previous energy sum value to zero
175
176 /* Init fft */
177 in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * framesize); // complex array to hold fft data
178 out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * framesize); // complex array to hold fft data
179 p = fftw_plan_dft_1d(framesize, in, out, FFTW_FORWARD, FFTW_ESTIMATE); // FFT plan initialisation
180
181 initialised = 1;
182 }
183
184 //--------------------------------------------------------------------------------------
185 // set the detection function type to that specified by the argument
186 void OnsetDetectionFunction :: set_df_type(int arg_df_type)
187 {
188 df_type = arg_df_type; // set detection function type
189 }
190
191
192 //--------------------------------------------------------------------------------------
193 // calculates a single detection function sample from a single audio frame.
194 double OnsetDetectionFunction :: getDFsample(double inputbuffer[])
195 {
196 double df_sample;
197
198 // shift audio samples back in frame by hop size
199 for (int i = 0; i < (framesize-hopsize);i++)
200 {
201 frame[i] = frame[i+hopsize];
202 }
203
204 // add new samples to frame from input buffer
205 int j = 0;
206 for (int i = (framesize-hopsize);i < framesize;i++)
207 {
208 frame[i] = inputbuffer[j];
209 j++;
210 }
211
212 switch (df_type){
213 case 0:
214 df_sample = energy_envelope(); // calculate energy envelope detection function sample
215 break;
216 case 1:
217 df_sample = energy_difference(); // calculate half-wave rectified energy difference detection function sample
218 break;
219 case 2:
220 df_sample = spectral_difference(); // calculate spectral difference detection function sample
221 break;
222 case 3:
223 df_sample = spectral_difference_hwr(); // calculate spectral difference detection function sample (half wave rectified)
224 break;
225 case 4:
226 df_sample = phase_deviation(); // calculate phase deviation detection function sample (half wave rectified)
227 break;
228 case 5:
229 df_sample = complex_spectral_difference(); // calcualte complex spectral difference detection function sample
230 break;
231 case 6:
232 df_sample = complex_spectral_difference_hwr(); // calcualte complex spectral difference detection function sample (half-wave rectified)
233 break;
234 case 7:
235 df_sample = high_frequency_content(); // calculate high frequency content detection function sample
236 break;
237 case 8:
238 df_sample = high_frequency_spectral_difference(); // calculate high frequency spectral difference detection function sample
239 break;
240 case 9:
241 df_sample = high_frequency_spectral_difference_hwr(); // calculate high frequency spectral difference detection function (half-wave rectified)
242 break;
243 default:
244 df_sample = 1.0;
245 }
246
247 return df_sample;
248 }
249
250
251 //--------------------------------------------------------------------------------------
252 // performs the fft, storing the complex result in 'out'
253 void OnsetDetectionFunction :: perform_FFT()
254 {
255 int fsize2 = (framesize/2);
256
257 // window frame and copy to complex array, swapping the first and second half of the signal
258 for (int i = 0;i < fsize2;i++)
259 {
260 in[i][0] = frame[i+fsize2] * window[i+fsize2];
261 in[i][1] = 0.0;
262 in[i+fsize2][0] = frame[i] * window[i];
263 in[i+fsize2][1] = 0.0;
264 }
265
266 // perform the fft
267 fftw_execute(p);
268 }
269
270 ////////////////////////////////////////////////////////////////////////////////////////////////
271 ////////////////////////////////////////////////////////////////////////////////////////////////
272 ////////////////////////////// Methods for Detection Functions /////////////////////////////////
273
274 //--------------------------------------------------------------------------------------
275 // calculates an energy envelope detection function sample
276 double OnsetDetectionFunction :: energy_envelope()
277 {
278 double sum;
279
280 sum = 0; // initialise sum
281
282 // sum the squares of the samples
283 for (int i = 0;i < framesize;i++)
284 {
285 sum = sum + (frame[i]*frame[i]);
286 }
287
288 return sum; // return sum
289 }
290
291 //--------------------------------------------------------------------------------------
292 // calculates a half-wave rectified energy difference detection function sample
293 double OnsetDetectionFunction :: energy_difference()
294 {
295 double sum;
296 double sample;
297
298 sum = 0; // initialise sum
299
300 // sum the squares of the samples
301 for (int i = 0;i < framesize;i++)
302 {
303 sum = sum + (frame[i]*frame[i]);
304 }
305
306 sample = sum - energy_sum_old; // sample is first order difference in energy
307
308 energy_sum_old = sum; // store energy value for next calculation
309
310 if (sample > 0)
311 {
312 return sample; // return difference
313 }
314 else
315 {
316 return 0;
317 }
318 }
319
320 //--------------------------------------------------------------------------------------
321 // calculates a spectral difference detection function sample
322 double OnsetDetectionFunction :: spectral_difference()
323 {
324 double diff;
325 double sum;
326
327 // perform the FFT
328 perform_FFT();
329
330 // compute first (N/2)+1 mag values
331 for (int i = 0;i < (framesize/2)+1;i++)
332 {
333 mag[i] = sqrt(pow(out[i][0],2) + pow(out[i][1],2));
334 }
335 // mag spec symmetric above (N/2)+1 so copy previous values
336 for (int i = (framesize/2)+1;i < framesize;i++)
337 {
338 mag[i] = mag[framesize-i];
339 }
340
341 sum = 0; // initialise sum to zero
342
343 for (int i = 0;i < framesize;i++)
344 {
345 // calculate difference
346 diff = mag[i] - mag_old[i];
347
348 // ensure all difference values are positive
349 if (diff < 0)
350 {
351 diff = diff*-1;
352 }
353
354 // add difference to sum
355 sum = sum+diff;
356
357 // store magnitude spectrum bin for next detection function sample calculation
358 mag_old[i] = mag[i];
359 }
360
361 return sum;
362 }
363
364 //--------------------------------------------------------------------------------------
365 // calculates a spectral difference detection function sample
366 double OnsetDetectionFunction :: spectral_difference_hwr()
367 {
368 double diff;
369 double sum;
370
371 // perform the FFT
372 perform_FFT();
373
374 // compute first (N/2)+1 mag values
375 for (int i = 0;i < (framesize/2)+1;i++)
376 {
377 mag[i] = sqrt(pow(out[i][0],2) + pow(out[i][1],2));
378 }
379 // mag spec symmetric above (N/2)+1 so copy previous values
380 for (int i = (framesize/2)+1;i < framesize;i++)
381 {
382 mag[i] = mag[framesize-i];
383 }
384
385 sum = 0; // initialise sum to zero
386
387 for (int i = 0;i < framesize;i++)
388 {
389 // calculate difference
390 diff = mag[i] - mag_old[i];
391
392 // only add up positive differences
393 if (diff > 0)
394 {
395 // add difference to sum
396 sum = sum+diff;
397 }
398
399
400
401 // store magnitude spectrum bin for next detection function sample calculation
402 mag_old[i] = mag[i];
403 }
404
405 return sum;
406 }
407
408
409 //--------------------------------------------------------------------------------------
410 // calculates a phase deviation detection function sample
411 double OnsetDetectionFunction :: phase_deviation()
412 {
413 double dev,pdev;
414 double sum;
415
416 // perform the FFT
417 perform_FFT();
418
419 sum = 0; // initialise sum to zero
420
421 // compute phase values from fft output and sum deviations
422 for (int i = 0;i < framesize;i++)
423 {
424 // calculate phase value
425 phase[i] = atan2(out[i][1],out[i][0]);
426
427 // calculate magnitude value
428 mag[i] = sqrt(pow(out[i][0],2) + pow(out[i][1],2));
429
430
431 // if bin is not just a low energy bin then examine phase deviation
432 if (mag[i] > 0.1)
433 {
434 dev = phase[i] - (2*phase_old[i]) + phase_old_2[i]; // phase deviation
435 pdev = princarg(dev); // wrap into [-pi,pi] range
436
437 // make all values positive
438 if (pdev < 0)
439 {
440 pdev = pdev*-1;
441 }
442
443 // add to sum
444 sum = sum + pdev;
445 }
446
447 // store values for next calculation
448 phase_old_2[i] = phase_old[i];
449 phase_old[i] = phase[i];
450 }
451
452 return sum;
453 }
454
455 //--------------------------------------------------------------------------------------
456 // calculates a complex spectral difference detection function sample
457 double OnsetDetectionFunction :: complex_spectral_difference()
458 {
459 double dev,pdev;
460 double sum;
461 double mag_diff,phase_diff;
462 double value;
463
464 // perform the FFT
465 perform_FFT();
466
467 sum = 0; // initialise sum to zero
468
469 // compute phase values from fft output and sum deviations
470 for (int i = 0;i < framesize;i++)
471 {
472 // calculate phase value
473 phase[i] = atan2(out[i][1],out[i][0]);
474
475 // calculate magnitude value
476 mag[i] = sqrt(pow(out[i][0],2) + pow(out[i][1],2));
477
478
479 // phase deviation
480 dev = phase[i] - (2*phase_old[i]) + phase_old_2[i];
481
482 // wrap into [-pi,pi] range
483 pdev = princarg(dev);
484
485
486 // calculate magnitude difference (real part of Euclidean distance between complex frames)
487 mag_diff = mag[i] - mag_old[i];
488
489 // calculate phase difference (imaginary part of Euclidean distance between complex frames)
490 phase_diff = -mag[i]*sin(pdev);
491
492
493
494 // square real and imaginary parts, sum and take square root
495 value = sqrt(pow(mag_diff,2) + pow(phase_diff,2));
496
497
498 // add to sum
499 sum = sum + value;
500
501
502 // store values for next calculation
503 phase_old_2[i] = phase_old[i];
504 phase_old[i] = phase[i];
505 mag_old[i] = mag[i];
506 }
507
508 return sum;
509 }
510
511 //--------------------------------------------------------------------------------------
512 // calculates a complex spectral difference detection function sample (half-wave rectified)
513 double OnsetDetectionFunction :: complex_spectral_difference_hwr()
514 {
515 double dev,pdev;
516 double sum;
517 double mag_diff,phase_diff;
518 double value;
519
520 // perform the FFT
521 perform_FFT();
522
523 sum = 0; // initialise sum to zero
524
525 // compute phase values from fft output and sum deviations
526 for (int i = 0;i < framesize;i++)
527 {
528 // calculate phase value
529 phase[i] = atan2(out[i][1],out[i][0]);
530
531 // calculate magnitude value
532 mag[i] = sqrt(pow(out[i][0],2) + pow(out[i][1],2));
533
534
535 // phase deviation
536 dev = phase[i] - (2*phase_old[i]) + phase_old_2[i];
537
538 // wrap into [-pi,pi] range
539 pdev = princarg(dev);
540
541
542 // calculate magnitude difference (real part of Euclidean distance between complex frames)
543 mag_diff = mag[i] - mag_old[i];
544
545 // if we have a positive change in magnitude, then include in sum, otherwise ignore (half-wave rectification)
546 if (mag_diff > 0)
547 {
548 // calculate phase difference (imaginary part of Euclidean distance between complex frames)
549 phase_diff = -mag[i]*sin(pdev);
550
551 // square real and imaginary parts, sum and take square root
552 value = sqrt(pow(mag_diff,2) + pow(phase_diff,2));
553
554 // add to sum
555 sum = sum + value;
556 }
557
558 // store values for next calculation
559 phase_old_2[i] = phase_old[i];
560 phase_old[i] = phase[i];
561 mag_old[i] = mag[i];
562 }
563
564 return sum;
565 }
566
567
568 //--------------------------------------------------------------------------------------
569 // calculates a high frequency content detection function sample
570 double OnsetDetectionFunction :: high_frequency_content()
571 {
572 double sum;
573 double mag_diff;
574
575 // perform the FFT
576 perform_FFT();
577
578 sum = 0; // initialise sum to zero
579
580 // compute phase values from fft output and sum deviations
581 for (int i = 0;i < framesize;i++)
582 {
583 // calculate magnitude value
584 mag[i] = sqrt(pow(out[i][0],2) + pow(out[i][1],2));
585
586
587 sum = sum + (mag[i]*((double) (i+1)));
588
589 // store values for next calculation
590 mag_old[i] = mag[i];
591 }
592
593 return sum;
594 }
595
596 //--------------------------------------------------------------------------------------
597 // calculates a high frequency spectral difference detection function sample
598 double OnsetDetectionFunction :: high_frequency_spectral_difference()
599 {
600 double sum;
601 double mag_diff;
602
603 // perform the FFT
604 perform_FFT();
605
606 sum = 0; // initialise sum to zero
607
608 // compute phase values from fft output and sum deviations
609 for (int i = 0;i < framesize;i++)
610 {
611 // calculate magnitude value
612 mag[i] = sqrt(pow(out[i][0],2) + pow(out[i][1],2));
613
614 // calculate difference
615 mag_diff = mag[i] - mag_old[i];
616
617 if (mag_diff < 0)
618 {
619 mag_diff = -mag_diff;
620 }
621
622 sum = sum + (mag_diff*((double) (i+1)));
623
624 // store values for next calculation
625 mag_old[i] = mag[i];
626 }
627
628 return sum;
629 }
630
631 //--------------------------------------------------------------------------------------
632 // calculates a high frequency spectral difference detection function sample (half-wave rectified)
633 double OnsetDetectionFunction :: high_frequency_spectral_difference_hwr()
634 {
635 double sum;
636 double mag_diff;
637
638 // perform the FFT
639 perform_FFT();
640
641 sum = 0; // initialise sum to zero
642
643 // compute phase values from fft output and sum deviations
644 for (int i = 0;i < framesize;i++)
645 {
646 // calculate magnitude value
647 mag[i] = sqrt(pow(out[i][0],2) + pow(out[i][1],2));
648
649 // calculate difference
650 mag_diff = mag[i] - mag_old[i];
651
652 if (mag_diff > 0)
653 {
654 sum = sum + (mag_diff*((double) (i+1)));
655 }
656
657 // store values for next calculation
658 mag_old[i] = mag[i];
659 }
660
661 return sum;
662 }
663
664
665 ////////////////////////////////////////////////////////////////////////////////////////////////
666 ////////////////////////////////////////////////////////////////////////////////////////////////
667 ////////////////////////////// Methods to Calculate Windows ////////////////////////////////////
668
669 //--------------------------------------------------------------------------------------
670 // HANNING: set the window in the buffer 'window' to a Hanning window
671 void OnsetDetectionFunction :: set_win_hanning()
672 {
673 double N; // variable to store framesize minus 1
674
675 N = (double) (framesize-1); // framesize minus 1
676
677 // Hanning window calculation
678 for (int n = 0;n < framesize;n++)
679 {
680 window[n] = 0.5*(1-cos(2*pi*(n/N)));
681 }
682 }
683
684 //--------------------------------------------------------------------------------------
685 // HAMMING: set the window in the buffer 'window' to a Hanning window
686 void OnsetDetectionFunction :: set_win_hamming()
687 {
688 double N; // variable to store framesize minus 1
689 double n_val; // double version of index 'n'
690
691 N = (double) (framesize-1); // framesize minus 1
692 n_val = 0;
693
694 // Hamming window calculation
695 for (int n = 0;n < framesize;n++)
696 {
697 window[n] = 0.54 - (0.46*cos(2*pi*(n_val/N)));
698 n_val = n_val+1;
699 }
700 }
701
702 //--------------------------------------------------------------------------------------
703 // BLACKMAN: set the window in the buffer 'window' to a Blackman window
704 void OnsetDetectionFunction :: set_win_blackman()
705 {
706 double N; // variable to store framesize minus 1
707 double n_val; // double version of index 'n'
708
709 N = (double) (framesize-1); // framesize minus 1
710 n_val = 0;
711
712 // Blackman window calculation
713 for (int n = 0;n < framesize;n++)
714 {
715 window[n] = 0.42 - (0.5*cos(2*pi*(n_val/N))) + (0.08*cos(4*pi*(n_val/N)));
716 n_val = n_val+1;
717 }
718 }
719
720 //--------------------------------------------------------------------------------------
721 // TUKEY: set the window in the buffer 'window' to a Tukey window
722 void OnsetDetectionFunction :: set_win_tukey()
723 {
724 double N; // variable to store framesize minus 1
725 double n_val; // double version of index 'n'
726 double alpha; // alpha [default value = 0.5];
727
728 int lim1,lim2;
729
730 alpha = 0.5;
731
732 N = (double) (framesize-1); // framesize minus 1
733
734 // Tukey window calculation
735
736 n_val = (double) (-1*((framesize/2)))+1;
737
738 for (int n = 0;n < framesize;n++) // left taper
739 {
740 if ((n_val >= 0) && (n_val <= (alpha*(N/2))))
741 {
742 window[n] = 1.0;
743 }
744 else if ((n_val <= 0) && (n_val >= (-1*alpha*(N/2))))
745 {
746 window[n] = 1.0;
747 }
748 else
749 {
750 window[n] = 0.5*(1+cos(pi*(((2*n_val)/(alpha*N))-1)));
751 }
752
753 n_val = n_val+1;
754 }
755
756 }
757
758 //--------------------------------------------------------------------------------------
759 // RECTANGULAR: set the window in the buffer 'window' to a Rectangular window
760 void OnsetDetectionFunction :: set_win_rectangular()
761 {
762 // Rectangular window calculation
763 for (int n = 0;n < framesize;n++)
764 {
765 window[n] = 1.0;
766 }
767 }
768
769
770
771 ////////////////////////////////////////////////////////////////////////////////////////////////
772 ////////////////////////////////////////////////////////////////////////////////////////////////
773 ///////////////////////////////// Other Handy Methods //////////////////////////////////////////
774
775 //--------------------------------------------------------------------------------------
776 // set phase values to the range [-pi,pi]
777 double OnsetDetectionFunction :: princarg(double phaseval)
778 {
779 // if phase value is less than or equal to -pi then add 2*pi
780 while (phaseval <= (-pi))
781 {
782 phaseval = phaseval + (2*pi);
783 }
784
785 // if phase value is larger than pi, then subtract 2*pi
786 while (phaseval > pi)
787 {
788 phaseval = phaseval - (2*pi);
789 }
790
791 return phaseval;
792 }
793
794
795
796
797
798
799
800
801
802
803
804
805