Mercurial > hg > btrack
view src/OnsetDetectionFunction.cpp @ 52:45231107c9d6
Reformatted comments, removed the OnsetDetectionFunction constructor with no arguments, removed a number of unused variables and made changes to the python module to fix some casting problems and removed some unused variables there also. Still getting the same results, so no overall changes to the algorithm.
author | Adam Stark <adamstark@users.noreply.github.com> |
---|---|
date | Wed, 22 Jan 2014 01:13:45 +0000 |
parents | b7e3ed593fb0 |
children | 73c64ca0ed23 |
line wrap: on
line source
//======================================================================= /** @file OnsetDetectionFunction.cpp * @brief A class for calculating onset detection functions * @author Adam Stark * @copyright Copyright (C) 2008-2014 Queen Mary University of London * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ //======================================================================= #include <math.h> #include "OnsetDetectionFunction.h" //======================================================================= OnsetDetectionFunction :: OnsetDetectionFunction(int arg_hsize,int arg_fsize,int arg_df_type,int arg_win_type) { // indicate that we have not initialised yet initialised = 0; // set pi pi = 3.14159265358979; // initialise with arguments to constructor initialise(arg_hsize,arg_fsize,arg_df_type,arg_win_type); } //======================================================================= OnsetDetectionFunction :: ~OnsetDetectionFunction() { // destroy fft plan fftw_destroy_plan(p); fftw_free(in); fftw_free(out); // deallocate memory delete [] frame; frame = NULL; delete [] window; window = NULL; delete [] wframe; wframe = NULL; delete [] mag; mag = NULL; delete [] mag_old; mag_old = NULL; delete [] phase; phase = NULL; delete [] phase_old; phase_old = NULL; delete [] phase_old_2; phase_old_2 = NULL; } //======================================================================= void OnsetDetectionFunction :: initialise(int arg_hsize,int arg_fsize,int arg_df_type,int arg_win_type) { if (initialised == 1) // if we have already initialised some buffers and an FFT plan { ////////////////////////////////// // TIDY UP FIRST - If initialise is called after the class has been initialised // then we want to free up memory and cancel existing FFT plans // destroy fft plan fftw_destroy_plan(p); fftw_free(in); fftw_free(out); // deallocate memory delete [] frame; frame = NULL; delete [] window; window = NULL; delete [] wframe; wframe = NULL; delete [] mag; mag = NULL; delete [] mag_old; mag_old = NULL; delete [] phase; phase = NULL; delete [] phase_old; phase_old = NULL; delete [] phase_old_2; phase_old_2 = NULL; ////// END TIDY UP /////////////// ////////////////////////////////// } hopsize = arg_hsize; // set hopsize framesize = arg_fsize; // set framesize df_type = arg_df_type; // set detection function type // initialise buffers frame = new double[framesize]; window = new double[framesize]; wframe = new double[framesize]; mag = new double[framesize]; mag_old = new double[framesize]; phase = new double[framesize]; phase_old = new double[framesize]; phase_old_2 = new double[framesize]; // set the window to the specified type switch (arg_win_type){ case 0: set_win_rectangular(); // Rectangular window break; case 1: set_win_hanning(); // Hanning Window break; case 2: set_win_hamming(); // Hamming Window break; case 3: set_win_blackman(); // Blackman Window break; case 4: set_win_tukey(); // Tukey Window break; default: set_win_hanning(); // DEFAULT: Hanning Window } // initialise previous magnitude spectrum to zero for (int i = 0;i < framesize;i++) { mag_old[i] = 0.0; phase_old[i] = 0.0; phase_old_2[i] = 0.0; frame[i] = 0.0; } energy_sum_old = 0.0; // initialise previous energy sum value to zero /* Init fft */ in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * framesize); // complex array to hold fft data out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * framesize); // complex array to hold fft data p = fftw_plan_dft_1d(framesize, in, out, FFTW_FORWARD, FFTW_ESTIMATE); // FFT plan initialisation initialised = 1; } //======================================================================= void OnsetDetectionFunction :: set_df_type(int arg_df_type) { df_type = arg_df_type; // set detection function type } //======================================================================= double OnsetDetectionFunction :: getDFsample(double inputbuffer[]) { double df_sample; // shift audio samples back in frame by hop size for (int i = 0; i < (framesize-hopsize);i++) { frame[i] = frame[i+hopsize]; } // add new samples to frame from input buffer int j = 0; for (int i = (framesize-hopsize);i < framesize;i++) { frame[i] = inputbuffer[j]; j++; } switch (df_type){ case 0: df_sample = energy_envelope(); // calculate energy envelope detection function sample break; case 1: df_sample = energy_difference(); // calculate half-wave rectified energy difference detection function sample break; case 2: df_sample = spectral_difference(); // calculate spectral difference detection function sample break; case 3: df_sample = spectral_difference_hwr(); // calculate spectral difference detection function sample (half wave rectified) break; case 4: df_sample = phase_deviation(); // calculate phase deviation detection function sample (half wave rectified) break; case 5: df_sample = complex_spectral_difference(); // calcualte complex spectral difference detection function sample break; case 6: df_sample = complex_spectral_difference_hwr(); // calcualte complex spectral difference detection function sample (half-wave rectified) break; case 7: df_sample = high_frequency_content(); // calculate high frequency content detection function sample break; case 8: df_sample = high_frequency_spectral_difference(); // calculate high frequency spectral difference detection function sample break; case 9: df_sample = high_frequency_spectral_difference_hwr(); // calculate high frequency spectral difference detection function (half-wave rectified) break; default: df_sample = 1.0; } return df_sample; } //======================================================================= void OnsetDetectionFunction :: perform_FFT() { int fsize2 = (framesize/2); // window frame and copy to complex array, swapping the first and second half of the signal for (int i = 0;i < fsize2;i++) { in[i][0] = frame[i+fsize2] * window[i+fsize2]; in[i][1] = 0.0; in[i+fsize2][0] = frame[i] * window[i]; in[i+fsize2][1] = 0.0; } // perform the fft fftw_execute(p); } //////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////// Methods for Detection Functions ///////////////////////////////// //======================================================================= double OnsetDetectionFunction :: energy_envelope() { double sum; sum = 0; // initialise sum // sum the squares of the samples for (int i = 0;i < framesize;i++) { sum = sum + (frame[i]*frame[i]); } return sum; // return sum } //======================================================================= double OnsetDetectionFunction :: energy_difference() { double sum; double sample; sum = 0; // initialise sum // sum the squares of the samples for (int i = 0;i < framesize;i++) { sum = sum + (frame[i]*frame[i]); } sample = sum - energy_sum_old; // sample is first order difference in energy energy_sum_old = sum; // store energy value for next calculation if (sample > 0) { return sample; // return difference } else { return 0; } } //======================================================================= double OnsetDetectionFunction :: spectral_difference() { double diff; double sum; // perform the FFT perform_FFT(); // compute first (N/2)+1 mag values for (int i = 0;i < (framesize/2)+1;i++) { mag[i] = sqrt(pow(out[i][0],2) + pow(out[i][1],2)); } // mag spec symmetric above (N/2)+1 so copy previous values for (int i = (framesize/2)+1;i < framesize;i++) { mag[i] = mag[framesize-i]; } sum = 0; // initialise sum to zero for (int i = 0;i < framesize;i++) { // calculate difference diff = mag[i] - mag_old[i]; // ensure all difference values are positive if (diff < 0) { diff = diff*-1; } // add difference to sum sum = sum+diff; // store magnitude spectrum bin for next detection function sample calculation mag_old[i] = mag[i]; } return sum; } //======================================================================= double OnsetDetectionFunction :: spectral_difference_hwr() { double diff; double sum; // perform the FFT perform_FFT(); // compute first (N/2)+1 mag values for (int i = 0;i < (framesize/2)+1;i++) { mag[i] = sqrt(pow(out[i][0],2) + pow(out[i][1],2)); } // mag spec symmetric above (N/2)+1 so copy previous values for (int i = (framesize/2)+1;i < framesize;i++) { mag[i] = mag[framesize-i]; } sum = 0; // initialise sum to zero for (int i = 0;i < framesize;i++) { // calculate difference diff = mag[i] - mag_old[i]; // only add up positive differences if (diff > 0) { // add difference to sum sum = sum+diff; } // store magnitude spectrum bin for next detection function sample calculation mag_old[i] = mag[i]; } return sum; } //======================================================================= double OnsetDetectionFunction :: phase_deviation() { double dev,pdev; double sum; // perform the FFT perform_FFT(); sum = 0; // initialise sum to zero // compute phase values from fft output and sum deviations for (int i = 0;i < framesize;i++) { // calculate phase value phase[i] = atan2(out[i][1],out[i][0]); // calculate magnitude value mag[i] = sqrt(pow(out[i][0],2) + pow(out[i][1],2)); // if bin is not just a low energy bin then examine phase deviation if (mag[i] > 0.1) { dev = phase[i] - (2*phase_old[i]) + phase_old_2[i]; // phase deviation pdev = princarg(dev); // wrap into [-pi,pi] range // make all values positive if (pdev < 0) { pdev = pdev*-1; } // add to sum sum = sum + pdev; } // store values for next calculation phase_old_2[i] = phase_old[i]; phase_old[i] = phase[i]; } return sum; } //======================================================================= double OnsetDetectionFunction :: complex_spectral_difference() { double dev,pdev; double sum; double mag_diff,phase_diff; double value; // perform the FFT perform_FFT(); sum = 0; // initialise sum to zero // compute phase values from fft output and sum deviations for (int i = 0;i < framesize;i++) { // calculate phase value phase[i] = atan2(out[i][1],out[i][0]); // calculate magnitude value mag[i] = sqrt(pow(out[i][0],2) + pow(out[i][1],2)); // phase deviation dev = phase[i] - (2*phase_old[i]) + phase_old_2[i]; // wrap into [-pi,pi] range pdev = princarg(dev); // calculate magnitude difference (real part of Euclidean distance between complex frames) mag_diff = mag[i] - mag_old[i]; // calculate phase difference (imaginary part of Euclidean distance between complex frames) phase_diff = -mag[i]*sin(pdev); // square real and imaginary parts, sum and take square root value = sqrt(pow(mag_diff,2) + pow(phase_diff,2)); // add to sum sum = sum + value; // store values for next calculation phase_old_2[i] = phase_old[i]; phase_old[i] = phase[i]; mag_old[i] = mag[i]; } return sum; } //======================================================================= double OnsetDetectionFunction :: complex_spectral_difference_hwr() { double dev,pdev; double sum; double mag_diff,phase_diff; double value; // perform the FFT perform_FFT(); sum = 0; // initialise sum to zero // compute phase values from fft output and sum deviations for (int i = 0;i < framesize;i++) { // calculate phase value phase[i] = atan2(out[i][1],out[i][0]); // calculate magnitude value mag[i] = sqrt(pow(out[i][0],2) + pow(out[i][1],2)); // phase deviation dev = phase[i] - (2*phase_old[i]) + phase_old_2[i]; // wrap into [-pi,pi] range pdev = princarg(dev); // calculate magnitude difference (real part of Euclidean distance between complex frames) mag_diff = mag[i] - mag_old[i]; // if we have a positive change in magnitude, then include in sum, otherwise ignore (half-wave rectification) if (mag_diff > 0) { // calculate phase difference (imaginary part of Euclidean distance between complex frames) phase_diff = -mag[i]*sin(pdev); // square real and imaginary parts, sum and take square root value = sqrt(pow(mag_diff,2) + pow(phase_diff,2)); // add to sum sum = sum + value; } // store values for next calculation phase_old_2[i] = phase_old[i]; phase_old[i] = phase[i]; mag_old[i] = mag[i]; } return sum; } //======================================================================= double OnsetDetectionFunction :: high_frequency_content() { double sum; // perform the FFT perform_FFT(); sum = 0; // initialise sum to zero // compute phase values from fft output and sum deviations for (int i = 0;i < framesize;i++) { // calculate magnitude value mag[i] = sqrt(pow(out[i][0],2) + pow(out[i][1],2)); sum = sum + (mag[i]*((double) (i+1))); // store values for next calculation mag_old[i] = mag[i]; } return sum; } //======================================================================= double OnsetDetectionFunction :: high_frequency_spectral_difference() { double sum; double mag_diff; // perform the FFT perform_FFT(); sum = 0; // initialise sum to zero // compute phase values from fft output and sum deviations for (int i = 0;i < framesize;i++) { // calculate magnitude value mag[i] = sqrt(pow(out[i][0],2) + pow(out[i][1],2)); // calculate difference mag_diff = mag[i] - mag_old[i]; if (mag_diff < 0) { mag_diff = -mag_diff; } sum = sum + (mag_diff*((double) (i+1))); // store values for next calculation mag_old[i] = mag[i]; } return sum; } //======================================================================= double OnsetDetectionFunction :: high_frequency_spectral_difference_hwr() { double sum; double mag_diff; // perform the FFT perform_FFT(); sum = 0; // initialise sum to zero // compute phase values from fft output and sum deviations for (int i = 0;i < framesize;i++) { // calculate magnitude value mag[i] = sqrt(pow(out[i][0],2) + pow(out[i][1],2)); // calculate difference mag_diff = mag[i] - mag_old[i]; if (mag_diff > 0) { sum = sum + (mag_diff*((double) (i+1))); } // store values for next calculation mag_old[i] = mag[i]; } return sum; } //////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////// Methods to Calculate Windows //////////////////////////////////// //======================================================================= void OnsetDetectionFunction :: set_win_hanning() { double N; // variable to store framesize minus 1 N = (double) (framesize-1); // framesize minus 1 // Hanning window calculation for (int n = 0;n < framesize;n++) { window[n] = 0.5*(1-cos(2*pi*(n/N))); } } //======================================================================= void OnsetDetectionFunction :: set_win_hamming() { double N; // variable to store framesize minus 1 double n_val; // double version of index 'n' N = (double) (framesize-1); // framesize minus 1 n_val = 0; // Hamming window calculation for (int n = 0;n < framesize;n++) { window[n] = 0.54 - (0.46*cos(2*pi*(n_val/N))); n_val = n_val+1; } } //======================================================================= void OnsetDetectionFunction :: set_win_blackman() { double N; // variable to store framesize minus 1 double n_val; // double version of index 'n' N = (double) (framesize-1); // framesize minus 1 n_val = 0; // Blackman window calculation for (int n = 0;n < framesize;n++) { window[n] = 0.42 - (0.5*cos(2*pi*(n_val/N))) + (0.08*cos(4*pi*(n_val/N))); n_val = n_val+1; } } //======================================================================= void OnsetDetectionFunction :: set_win_tukey() { double N; // variable to store framesize minus 1 double n_val; // double version of index 'n' double alpha; // alpha [default value = 0.5]; alpha = 0.5; N = (double) (framesize-1); // framesize minus 1 // Tukey window calculation n_val = (double) (-1*((framesize/2)))+1; for (int n = 0;n < framesize;n++) // left taper { if ((n_val >= 0) && (n_val <= (alpha*(N/2)))) { window[n] = 1.0; } else if ((n_val <= 0) && (n_val >= (-1*alpha*(N/2)))) { window[n] = 1.0; } else { window[n] = 0.5*(1+cos(pi*(((2*n_val)/(alpha*N))-1))); } n_val = n_val+1; } } //======================================================================= void OnsetDetectionFunction :: set_win_rectangular() { // Rectangular window calculation for (int n = 0;n < framesize;n++) { window[n] = 1.0; } } //////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// Other Handy Methods ////////////////////////////////////////// //======================================================================= double OnsetDetectionFunction :: princarg(double phaseval) { // if phase value is less than or equal to -pi then add 2*pi while (phaseval <= (-pi)) { phaseval = phaseval + (2*pi); } // if phase value is larger than pi, then subtract 2*pi while (phaseval > pi) { phaseval = phaseval - (2*pi); } return phaseval; }