# HG changeset patch # User Adam Stark # Date 1390491071 0 # Node ID 296af6af6c3d5fa369605f3ca0910be6a9070eba # Parent b6d440942ff6e53b919078754fbf24a9203f1341 Replaced switch statements in OnsetDetectionFunction with enums. Renamed lots of functions so that they have better names, in camel case. Added some unit tests for initialisation of BTrack. diff -r b6d440942ff6 -r 296af6af6c3d modules-and-plug-ins/python-module/btrack_python_module.cpp --- a/modules-and-plug-ins/python-module/btrack_python_module.cpp Thu Jan 23 12:17:06 2014 +0000 +++ b/modules-and-plug-ins/python-module/btrack_python_module.cpp Thu Jan 23 15:31:11 2014 +0000 @@ -151,9 +151,8 @@ b.processAudioFrame(buffer); // if a beat is currently scheduled - if (b.playbeat == 1) + if (b.beatDueInCurrentFrame()) { - //beats[beatnum] = (((double) hopSize) / 44100) * ((double) i); beats[beatnum] = BTrack::getBeatTimeInSeconds(i,hopSize,44100); beatnum = beatnum + 1; } @@ -241,9 +240,8 @@ b.processOnsetDetectionFunctionSample(df_val); // process df sample in beat tracker - if (b.playbeat == 1) + if (b.beatDueInCurrentFrame()) { - //beats[beatnum] = (((double) hopSize) / 44100) * ((double) i); beats[beatnum] = BTrack::getBeatTimeInSeconds(i,hopSize,44100); beatnum = beatnum + 1; } diff -r b6d440942ff6 -r 296af6af6c3d src/BTrack.cpp --- a/src/BTrack.cpp Thu Jan 23 12:17:06 2014 +0000 +++ b/src/BTrack.cpp Thu Jan 23 15:31:11 2014 +0000 @@ -25,21 +25,21 @@ #include "samplerate.h" //======================================================================= -BTrack::BTrack() : odf(512,1024,6,1) +BTrack::BTrack() : odf(512,1024,ComplexSpectralDifferenceHWR,HanningWindow) { initialise(512, 1024); } //======================================================================= -BTrack::BTrack(int hopSize) : odf(hopSize,2*hopSize,6,1) +BTrack::BTrack(int hopSize_) : odf(hopSize_,2*hopSize_,ComplexSpectralDifferenceHWR,HanningWindow) { - initialise(hopSize, 2*hopSize); + initialise(hopSize_, 2*hopSize_); } //======================================================================= -BTrack::BTrack(int hopSize,int frameSize) : odf(hopSize,frameSize,6,1) +BTrack::BTrack(int hopSize_,int frameSize_) : odf(hopSize_,frameSize_,ComplexSpectralDifferenceHWR,HanningWindow) { - initialise(hopSize, frameSize); + initialise(hopSize_, frameSize_); } //======================================================================= @@ -63,7 +63,7 @@ //======================================================================= -void BTrack::initialise(int hopSize, int frameSize) +void BTrack::initialise(int hopSize_, int frameSize_) { double rayparam = 43; double pi = 3.14159265; @@ -79,7 +79,7 @@ m0 = 10; beat = -1; - playbeat = 0; + beatDueInFrame = false; @@ -115,16 +115,16 @@ tempofix = 0; // initialise algorithm given the hopsize - setHopSize(hopSize); + setHopSize(hopSize_); } //======================================================================= -void BTrack :: setHopSize(int hopSize) +void BTrack::setHopSize(int hopSize_) { - framesize = hopSize; + hopSize = hopSize_; dfbuffer_size = (512*512)/hopSize; // calculate df buffer size - bperiod = round(60/((((double) hopSize)/44100)*tempo)); + beatPeriod = round(60/((((double) hopSize)/44100)*tempo)); dfbuffer = new double[dfbuffer_size]; // create df_buffer cumscore = new double[dfbuffer_size]; // create cumscore @@ -137,7 +137,7 @@ cumscore[i] = 0; - if ((i % ((int) round(bperiod))) == 0) + if ((i % ((int) round(beatPeriod))) == 0) { dfbuffer[i] = 1; } @@ -145,6 +145,18 @@ } //======================================================================= +bool BTrack::beatDueInCurrentFrame() +{ + return beatDueInFrame; +} + +//======================================================================= +int BTrack::getHopSize() +{ + return hopSize; +} + +//======================================================================= void BTrack::processAudioFrame(double *frame) { // calculate the onset detection function sample for the frame @@ -169,7 +181,7 @@ m0--; beat--; - playbeat = 0; + beatDueInFrame = false; // move all samples back one step for (int i=0;i < (dfbuffer_size-1);i++) @@ -181,27 +193,27 @@ dfbuffer[dfbuffer_size-1] = newSample; // update cumulative score - updatecumscore(newSample); + updateCumulativeScore(newSample); // if we are halfway between beats if (m0 == 0) { - predictbeat(); + predictBeat(); } // if we are at a beat if (beat == 0) { - playbeat = 1; // indicate a beat should be output + beatDueInFrame = true; // indicate a beat should be output // recalculate the tempo - dfconvert(); - calcTempo(); + resampleOnsetDetectionFunction(); + calculateTempo(); } } //======================================================================= -void BTrack :: settempo(double tempo) +void BTrack::setTempo(double tempo) { /////////// TEMPO INDICATION RESET ////////////////// @@ -233,7 +245,7 @@ /////////// CUMULATIVE SCORE ARTIFICAL TEMPO UPDATE ////////////////// // calculate new beat period - int new_bperiod = (int) round(60/((((double) framesize)/44100)*tempo)); + int new_bperiod = (int) round(60/((((double) hopSize)/44100)*tempo)); int bcounter = 1; // initialise df_buffer to zeros @@ -268,7 +280,7 @@ } //======================================================================= -void BTrack :: fixtempo(double tempo) +void BTrack::fixTempo(double tempo) { // firstly make sure tempo is between 80 and 160 bpm.. while (tempo > 160) @@ -298,14 +310,14 @@ } //======================================================================= -void BTrack :: unfixtempo() +void BTrack::doNotFixTempo() { // set the tempo fix flag tempofix = 0; } //======================================================================= -void BTrack :: dfconvert() +void BTrack::resampleOnsetDetectionFunction() { float output[512]; float input[dfbuffer_size]; @@ -340,20 +352,20 @@ } //======================================================================= -void BTrack :: calcTempo() +void BTrack::calculateTempo() { // adaptive threshold on input - adapt_thresh(df512,512); + adaptiveThreshold(df512,512); // calculate auto-correlation function of detection function - acf_bal(df512); + calculateBalancedACF(df512); // calculate output of comb filterbank - getrcfoutput(); + calculateOutputOfCombFilterBank(); // adaptive threshold on rcf - adapt_thresh(rcf,128); + adaptiveThreshold(rcf,128); int t_index; @@ -399,7 +411,7 @@ } - normalise(delta,41); + normaliseArray(delta,41); maxind = -1; maxval = -1; @@ -415,18 +427,16 @@ prev_delta[j] = delta[j]; } - bperiod = round((60.0*44100.0)/(((2*maxind)+80)*((double) framesize))); + beatPeriod = round((60.0*44100.0)/(((2*maxind)+80)*((double) hopSize))); - if (bperiod > 0) + if (beatPeriod > 0) { - est_tempo = 60.0/((((double) framesize) / 44100.0)*bperiod); + est_tempo = 60.0/((((double) hopSize) / 44100.0)*beatPeriod); } - - //cout << bperiod << endl; } //======================================================================= -void BTrack :: adapt_thresh(double *x,int N) +void BTrack::adaptiveThreshold(double *x,int N) { //int N = 512; // length of df int i = 0; @@ -442,18 +452,18 @@ for (i = 0;i <= t;i++) { k = std::min((i+p_pre),N); - x_thresh[i] = mean_array(x,1,k); + x_thresh[i] = calculateMeanOfArray(x,1,k); } // find threshold for bulk of samples across a moving average from [i-p_pre,i+p_post] for (i = t+1;i < N-p_post;i++) { - x_thresh[i] = mean_array(x,i-p_pre,i+p_post); + x_thresh[i] = calculateMeanOfArray(x,i-p_pre,i+p_post); } // for last few samples calculate threshold, again, not enough samples to do as above for (i = N-p_post;i < N;i++) { k = std::max((i-p_post),1); - x_thresh[i] = mean_array(x,k,N); + x_thresh[i] = calculateMeanOfArray(x,k,N); } // subtract the threshold from the detection function and check that it is not less than 0 @@ -468,7 +478,7 @@ } //======================================================================= -void BTrack :: getrcfoutput() +void BTrack::calculateOutputOfCombFilterBank() { int numelem; @@ -492,7 +502,7 @@ } //======================================================================= -void BTrack :: acf_bal(double *df_thresh) +void BTrack::calculateBalancedACF(double *df_thresh) { int l, n = 0; double sum, tmp; @@ -514,7 +524,7 @@ } //======================================================================= -double BTrack :: mean_array(double *array,int start,int end) +double BTrack::calculateMeanOfArray(double *array,int start,int end) { int i; double sum = 0; @@ -538,7 +548,7 @@ } //======================================================================= -void BTrack :: normalise(double *array,int N) +void BTrack::normaliseArray(double *array,int N) { double sum = 0; @@ -560,24 +570,24 @@ } //======================================================================= -void BTrack :: updatecumscore(double df_sample) +void BTrack::updateCumulativeScore(double df_sample) { int start, end, winsize; double max; - start = dfbuffer_size - round(2*bperiod); - end = dfbuffer_size - round(bperiod/2); + start = dfbuffer_size - round(2*beatPeriod); + end = dfbuffer_size - round(beatPeriod/2); winsize = end-start+1; double w1[winsize]; - double v = -2*bperiod; + double v = -2*beatPeriod; double wcumscore; // create window for (int i = 0;i < winsize;i++) { - w1[i] = exp((-1*pow(tightness*log(-v/bperiod),2))/2); + w1[i] = exp((-1*pow(tightness*log(-v/beatPeriod),2))/2); v = v+1; } @@ -612,9 +622,9 @@ } //======================================================================= -void BTrack :: predictbeat() +void BTrack::predictBeat() { - int winsize = (int) bperiod; + int winsize = (int) beatPeriod; double fcumscore[dfbuffer_size + winsize]; double w2[winsize]; // copy cumscore to first part of fcumscore @@ -627,20 +637,20 @@ double v = 1; for (int i = 0;i < winsize;i++) { - w2[i] = exp((-1*pow((v - (bperiod/2)),2)) / (2*pow((bperiod/2) ,2))); + w2[i] = exp((-1*pow((v - (beatPeriod/2)),2)) / (2*pow((beatPeriod/2) ,2))); v++; } // create past window - v = -2*bperiod; - int start = dfbuffer_size - round(2*bperiod); - int end = dfbuffer_size - round(bperiod/2); + v = -2*beatPeriod; + int start = dfbuffer_size - round(2*beatPeriod); + int end = dfbuffer_size - round(beatPeriod/2); int pastwinsize = end-start+1; double w1[pastwinsize]; for (int i = 0;i < pastwinsize;i++) { - w1[i] = exp((-1*pow(tightness*log(-v/bperiod),2))/2); + w1[i] = exp((-1*pow(tightness*log(-v/beatPeriod),2))/2); v = v+1; } @@ -652,8 +662,8 @@ double wcumscore; for (int i = dfbuffer_size;i < (dfbuffer_size+winsize);i++) { - start = i - round(2*bperiod); - end = i - round(bperiod/2); + start = i - round(2*beatPeriod); + end = i - round(beatPeriod/2); max = 0; n = 0; @@ -690,7 +700,7 @@ } // set next prediction time - m0 = beat+round(bperiod/2); + m0 = beat+round(beatPeriod/2); } \ No newline at end of file diff -r b6d440942ff6 -r 296af6af6c3d src/BTrack.h --- a/src/BTrack.h Thu Jan 23 12:17:06 2014 +0000 +++ b/src/BTrack.h Thu Jan 23 15:31:11 2014 +0000 @@ -34,13 +34,13 @@ /** constructor assuming frame size will be double hopSize * @param hopSize the step size in audio samples by which we will receive audio frames */ - BTrack(int hopSize); + BTrack(int hopSize_); /** constructor taking both hopSize and frameSize * @param hopSize the step size in audio samples by which we will receive audio frames * @param frameSize the audio frame size in audio samples */ - BTrack(int hopSize,int frameSize); + BTrack(int hopSize_,int frameSize_); /** Process a single audio frame */ void processAudioFrame(double *frame); @@ -48,69 +48,74 @@ /** Add new onset detection function sample to buffer and apply beat tracking */ void processOnsetDetectionFunctionSample(double sample); + /** @returns the current hop size being used by the beat tracker */ + int getHopSize(); + /** Set the tempo of the beat tracker */ - void settempo(double tempo); + void setTempo(double tempo); /** fix tempo to roughly around some value */ - void fixtempo(double tempo); + void fixTempo(double tempo); /** do not fix the tempo anymore */ - void unfixtempo(); + void doNotFixTempo(); static double getBeatTimeInSeconds(long frameNumber,int hopSize,int fs); static double getBeatTimeInSeconds(int frameNumber,int hopSize,int fs); - int playbeat; + /** @returns true if a beat should occur in the current audio frame */ + bool beatDueInCurrentFrame(); + double cscoreval; double est_tempo; private: - void initialise(int hopSize,int frameSize); + void initialise(int hopSize_,int frameSize_); /** Initialise with hop size and set all frame sizes accordingly */ - void setHopSize(int hopSize); + void setHopSize(int hopSize_); /** Convert detection function from N samples to 512 */ - void dfconvert(); + void resampleOnsetDetectionFunction(); /** update the cumulative score */ - void updatecumscore(double df_sample); + void updateCumulativeScore(double df_sample); /** predicts the next beat */ - void predictbeat(); + void predictBeat(); /** Calculates the current tempo expressed as the beat period in detection function samples */ - void calcTempo(); + void calculateTempo(); /** calculates an adaptive threshold which is used to remove low level energy from detection * function and emphasise peaks */ - void adapt_thresh(double *x,int N); + void adaptiveThreshold(double *x,int N); /** calculates the mean of values in an array from index locations [start,end] */ - double mean_array(double *array,int start,int end); + double calculateMeanOfArray(double *array,int start,int end); /** normalises a given array */ - void normalise(double *array,int N); + void normaliseArray(double *array,int N); /** calculates the balanced autocorrelation of the smoothed detection function */ - void acf_bal(double *df_thresh); + void calculateBalancedACF(double *df_thresh); - /** returns the output of the comb filter */ - void getrcfoutput(); + /** calculates the output of the comb filter bank */ + void calculateOutputOfCombFilterBank(); // buffers double *dfbuffer; /**< to hold detection function */ double df512[512]; /**< to hold resampled detection function */ double *cumscore; /**< to hold cumulative score */ - double acf[512]; /**< to hold autocorrelation function */ + double acf[512]; /**< to hold autocorrelation function */ double wv[128]; /**< to hold weighting vector */ - double rcf[128]; /**< to hold comb filter output */ + double rcf[128]; /**< to hold comb filter output */ double t_obs[41]; /**< to hold tempo version of comb filter output */ double delta[41]; /**< to hold final tempo candidate array */ @@ -124,7 +129,7 @@ // parameters double tightness; double alpha; - double bperiod; + double beatPeriod; double tempo; @@ -138,11 +143,13 @@ int dfbuffer_size; - int framesize; + int hopSize; int tempofix; + + bool beatDueInFrame; }; diff -r b6d440942ff6 -r 296af6af6c3d src/OnsetDetectionFunction.cpp --- a/src/OnsetDetectionFunction.cpp Thu Jan 23 12:17:06 2014 +0000 +++ b/src/OnsetDetectionFunction.cpp Thu Jan 23 15:31:11 2014 +0000 @@ -120,19 +120,19 @@ // set the window to the specified type switch (arg_win_type){ - case 0: + case RectangularWindow: set_win_rectangular(); // Rectangular window break; - case 1: + case HanningWindow: set_win_hanning(); // Hanning Window break; - case 2: + case HammingWindow: set_win_hamming(); // Hamming Window break; - case 3: + case BlackmanWindow: set_win_blackman(); // Blackman Window break; - case 4: + case TukeyWindow: set_win_tukey(); // Tukey Window break; default: @@ -187,38 +187,70 @@ } 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 + case EnergyEnvelope: + { + // calculate energy envelope detection function sample + df_sample = energy_envelope(); break; - case 2: - df_sample = spectral_difference(); // calculate spectral difference detection function sample + } + case EnergyDifference: + { + // calculate half-wave rectified energy difference detection function sample + df_sample = energy_difference(); break; - case 3: - df_sample = spectral_difference_hwr(); // calculate spectral difference detection function sample (half wave rectified) + } + case SpectralDifference: + { + // calculate spectral difference detection function sample + df_sample = spectral_difference(); break; - case 4: - df_sample = phase_deviation(); // calculate phase deviation detection function sample (half wave rectified) + } + case SpectralDifferenceHWR: + { + // calculate spectral difference detection function sample (half wave rectified) + df_sample = spectral_difference_hwr(); break; - case 5: - df_sample = complex_spectral_difference(); // calcualte complex spectral difference detection function sample + } + case PhaseDeviation: + { + // calculate phase deviation detection function sample (half wave rectified) + df_sample = phase_deviation(); break; - case 6: - df_sample = complex_spectral_difference_hwr(); // calcualte complex spectral difference detection function sample (half-wave rectified) + } + case ComplexSpectralDifference: + { + // calcualte complex spectral difference detection function sample + df_sample = complex_spectral_difference(); break; - case 7: - df_sample = high_frequency_content(); // calculate high frequency content detection function sample + } + case ComplexSpectralDifferenceHWR: + { + // calcualte complex spectral difference detection function sample (half-wave rectified) + df_sample = complex_spectral_difference_hwr(); break; - case 8: - df_sample = high_frequency_spectral_difference(); // calculate high frequency spectral difference detection function sample + } + case HighFrequencyContent: + { + // calculate high frequency content detection function sample + df_sample = high_frequency_content(); break; - case 9: - df_sample = high_frequency_spectral_difference_hwr(); // calculate high frequency spectral difference detection function (half-wave rectified) + } + case HighFrequencySpectralDifference: + { + // calculate high frequency spectral difference detection function sample + df_sample = high_frequency_spectral_difference(); break; + } + case HighFrequencySpectralDifferenceHWR: + { + // calculate high frequency spectral difference detection function (half-wave rectified) + df_sample = high_frequency_spectral_difference_hwr(); + break; + } default: + { df_sample = 1.0; + } } return df_sample; diff -r b6d440942ff6 -r 296af6af6c3d src/OnsetDetectionFunction.h --- a/src/OnsetDetectionFunction.h Thu Jan 23 12:17:06 2014 +0000 +++ b/src/OnsetDetectionFunction.h Thu Jan 23 15:31:11 2014 +0000 @@ -24,6 +24,31 @@ #include "fftw3.h" +//======================================================================= +enum OnsetDetectionFunctionType +{ + EnergyEnvelope, + EnergyDifference, + SpectralDifference, + SpectralDifferenceHWR, + PhaseDeviation, + ComplexSpectralDifference, + ComplexSpectralDifferenceHWR, + HighFrequencyContent, + HighFrequencySpectralDifference, + HighFrequencySpectralDifferenceHWR +}; + +//======================================================================= +enum WindowType +{ + RectangularWindow, + HanningWindow, + HammingWindow, + BlackmanWindow, + TukeyWindow +}; + class OnsetDetectionFunction { public: diff -r b6d440942ff6 -r 296af6af6c3d unit-tests/BTrack Tests/tests/Test_BTrack.cpp --- a/unit-tests/BTrack Tests/tests/Test_BTrack.cpp Thu Jan 23 12:17:06 2014 +0000 +++ b/unit-tests/BTrack Tests/tests/Test_BTrack.cpp Thu Jan 23 15:31:11 2014 +0000 @@ -8,6 +8,40 @@ #include "../../../src/BTrack.h" //====================================================================== +//==================== CHECKING INITIALISATION ========================= +//====================================================================== +BOOST_AUTO_TEST_SUITE(checkingInitialisation) + +//====================================================================== +BOOST_AUTO_TEST_CASE(constructorWithNoArguments) +{ + BTrack b; + + BOOST_CHECK_EQUAL(b.getHopSize(), 512); +} + +//====================================================================== +BOOST_AUTO_TEST_CASE(constructorWithHopSize) +{ + BTrack b(1024); + + BOOST_CHECK_EQUAL(b.getHopSize(), 1024); +} + +//====================================================================== +BOOST_AUTO_TEST_CASE(constructorWithHopSizeAndFrameSize) +{ + BTrack b(256,512); + + BOOST_CHECK_EQUAL(b.getHopSize(), 256); +} + +BOOST_AUTO_TEST_SUITE_END() +//====================================================================== +//====================================================================== + + +//====================================================================== //=================== PROCESSING SIMPLE VALUES ========================= //====================================================================== BOOST_AUTO_TEST_SUITE(processingSimpleValues) @@ -31,7 +65,7 @@ currentInterval++; - if (b.playbeat == 1) + if (b.beatDueInCurrentFrame()) { numBeats++; @@ -77,7 +111,7 @@ currentInterval++; - if (b.playbeat == 1) + if (b.beatDueInCurrentFrame()) { numBeats++; @@ -123,7 +157,7 @@ currentInterval++; - if (b.playbeat == 1) + if (b.beatDueInCurrentFrame()) { numBeats++; @@ -178,7 +212,7 @@ currentInterval++; - if (b.playbeat == 1) + if (b.beatDueInCurrentFrame()) { numBeats++; @@ -210,7 +244,8 @@ BOOST_AUTO_TEST_SUITE_END() - +//====================================================================== +//======================================================================