diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/OnsetDetectionFunction.cpp	Tue Jan 21 01:44:55 2014 +0000
@@ -0,0 +1,805 @@
+//=======================================================================
+/** @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 <iostream>
+#include "OnsetDetectionFunction.h"
+using namespace std;
+
+//-------------------------------------------------------------------------------
+// Constructor
+OnsetDetectionFunction :: OnsetDetectionFunction()
+{	
+	// indicate that we have not initialised yet
+	initialised = 0;		
+	
+	// set pi
+	pi = 3.14159265358979;	
+	
+	// initialise with hopsize = 512, framesize = 1024, complex spectral difference DF and hanning window
+	initialise(512,1024,6,1);	
+}
+
+
+//-------------------------------------------------------------------------------
+// Constructor (with arguments)
+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);
+}
+
+
+//--------------------------------------------------------------------------------------
+// Destructor
+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;
+}
+
+//-------------------------------------------------------------------------------
+// Initialisation
+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;
+}
+
+//--------------------------------------------------------------------------------------
+// set the detection function type to that specified by the argument
+void OnsetDetectionFunction :: set_df_type(int arg_df_type)
+{
+	df_type = arg_df_type; // set detection function type
+}
+
+
+//--------------------------------------------------------------------------------------
+// calculates a single detection function sample from a single audio frame.
+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;
+}
+
+
+//--------------------------------------------------------------------------------------
+// performs the fft, storing the complex result in 'out'
+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 /////////////////////////////////
+
+//--------------------------------------------------------------------------------------
+// calculates an energy envelope detection function sample
+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
+}
+
+//--------------------------------------------------------------------------------------
+// calculates a half-wave rectified energy difference detection function sample
+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;
+	}
+}
+
+//--------------------------------------------------------------------------------------
+// calculates a spectral difference detection function sample
+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;		
+}
+
+//--------------------------------------------------------------------------------------
+// calculates a spectral difference detection function sample
+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;		
+}
+
+
+//--------------------------------------------------------------------------------------
+// calculates a phase deviation detection function sample
+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;		
+}
+
+//--------------------------------------------------------------------------------------
+// calculates a complex spectral difference detection function sample
+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;		
+}
+
+//--------------------------------------------------------------------------------------
+// calculates a complex spectral difference detection function sample (half-wave rectified)
+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;		
+}
+
+
+//--------------------------------------------------------------------------------------
+// calculates a high frequency content detection function sample
+double OnsetDetectionFunction :: high_frequency_content()
+{
+	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));
+		
+		
+		sum = sum + (mag[i]*((double) (i+1)));
+		
+		// store values for next calculation
+		mag_old[i] = mag[i];
+	}
+	
+	return sum;		
+}
+
+//--------------------------------------------------------------------------------------
+// calculates a high frequency spectral difference detection function sample
+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;		
+}
+
+//--------------------------------------------------------------------------------------
+// calculates a high frequency spectral difference detection function sample (half-wave rectified)
+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 ////////////////////////////////////
+
+//--------------------------------------------------------------------------------------
+// HANNING: set the window in the buffer 'window' to a Hanning window
+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)));
+	}
+}
+
+//--------------------------------------------------------------------------------------
+// HAMMING: set the window in the buffer 'window' to a Hanning window
+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;
+	}
+}
+
+//--------------------------------------------------------------------------------------
+// BLACKMAN: set the window in the buffer 'window' to a Blackman window
+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;
+	}
+}
+
+//--------------------------------------------------------------------------------------
+// TUKEY: set the window in the buffer 'window' to a Tukey window
+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];
+	
+	int lim1,lim2;
+	
+	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;			 
+	}
+
+}
+
+//--------------------------------------------------------------------------------------
+// RECTANGULAR: set the window in the buffer 'window' to a Rectangular window
+void OnsetDetectionFunction :: set_win_rectangular()
+{
+	// Rectangular window calculation
+	for (int n = 0;n < framesize;n++)
+	{
+		window[n] = 1.0;
+	}
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////// Other Handy Methods //////////////////////////////////////////
+
+//--------------------------------------------------------------------------------------
+// set phase values to the range [-pi,pi]
+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;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+