Mercurial > hg > btrack
changeset 57:296af6af6c3d
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.
author | Adam Stark <adamstark@users.noreply.github.com> |
---|---|
date | Thu, 23 Jan 2014 15:31:11 +0000 |
parents | b6d440942ff6 |
children | f84ccd07e17f |
files | modules-and-plug-ins/python-module/btrack_python_module.cpp src/BTrack.cpp src/BTrack.h src/OnsetDetectionFunction.cpp src/OnsetDetectionFunction.h unit-tests/BTrack Tests/tests/Test_BTrack.cpp |
diffstat | 6 files changed, 221 insertions(+), 114 deletions(-) [+] |
line wrap: on
line diff
--- 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; }
--- 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
--- 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; };
--- 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;
--- 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:
--- 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() - +//====================================================================== +//======================================================================