Mercurial > hg > btrack
changeset 56:b6d440942ff6
Added some simple unit tests. Removed the destructor from the BTrack class as it was unnecessary.
author | Adam Stark <adamstark@users.noreply.github.com> |
---|---|
date | Thu, 23 Jan 2014 12:17:06 +0000 |
parents | 5e520f59127f |
children | 296af6af6c3d |
files | src/BTrack.cpp src/BTrack.h unit-tests/BTrack Tests.xcodeproj/project.pbxproj unit-tests/BTrack Tests/main.cpp unit-tests/BTrack Tests/tests/Test_BTrack.cpp |
diffstat | 5 files changed, 312 insertions(+), 76 deletions(-) [+] |
line wrap: on
line diff
--- a/src/BTrack.cpp Wed Jan 22 18:47:16 2014 +0000 +++ b/src/BTrack.cpp Thu Jan 23 12:17:06 2014 +0000 @@ -43,12 +43,6 @@ } //======================================================================= -BTrack::~BTrack() -{ - -} - -//======================================================================= double BTrack::getBeatTimeInSeconds(long frameNumber,int hopSize,int fs) { double hop = (double) hopSize; @@ -156,9 +150,7 @@ // calculate the onset detection function sample for the frame double sample = odf.getDFsample(frame); - // add a tiny constant to the sample to stop it from ever going - // to zero. this is to avoid problems further down the line - sample = sample + 0.0001; + // process the new onset detection function sample in the beat tracking algorithm processOnsetDetectionFunctionSample(sample); @@ -166,7 +158,15 @@ //======================================================================= void BTrack::processOnsetDetectionFunctionSample(double newSample) -{ +{ + // we need to ensure that the onset + // detection function sample is positive + newSample = fabs(newSample); + + // add a tiny constant to the sample to stop it from ever going + // to zero. this is to avoid problems further down the line + newSample = newSample + 0.0001; + m0--; beat--; playbeat = 0;
--- a/src/BTrack.h Wed Jan 22 18:47:16 2014 +0000 +++ b/src/BTrack.h Thu Jan 23 12:17:06 2014 +0000 @@ -29,7 +29,7 @@ public: /** constructor assuming hop size of 512 and frame size of 1024 */ - BTrack(); + BTrack(); /** constructor assuming frame size will be double hopSize * @param hopSize the step size in audio samples by which we will receive audio frames @@ -40,46 +40,43 @@ * @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); - - /** destructor */ - ~BTrack(); - - void initialise(int hopSize,int frameSize); - - /** Initialise with hop size and set all frame sizes accordingly */ - void setHopSize(int hopSize); + BTrack(int hopSize,int frameSize); /** Process a single audio frame */ - void processAudioFrame(double *frame); + void processAudioFrame(double *frame); /** Add new onset detection function sample to buffer and apply beat tracking */ - void processOnsetDetectionFunctionSample(double sample); + void processOnsetDetectionFunctionSample(double sample); /** 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 unfixtempo(); static double getBeatTimeInSeconds(long frameNumber,int hopSize,int fs); static double getBeatTimeInSeconds(int frameNumber,int hopSize,int fs); - int playbeat; - double cscoreval; - double est_tempo; + int playbeat; + double cscoreval; + double est_tempo; private: + void initialise(int hopSize,int frameSize); + + /** Initialise with hop size and set all frame sizes accordingly */ + void setHopSize(int hopSize); + /** Convert detection function from N samples to 512 */ - void dfconvert(); + void dfconvert(); /** update the cumulative score */ - void updatecumscore(double df_sample); + void updatecumscore(double df_sample); /** predicts the next beat */ void predictbeat(); @@ -90,61 +87,61 @@ /** 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 adapt_thresh(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 mean_array(double *array,int start,int end); /** normalises a given array */ - void normalise(double *array,int N); + void normalise(double *array,int N); /** calculates the balanced autocorrelation of the smoothed detection function */ - void acf_bal(double *df_thresh); + void acf_bal(double *df_thresh); /** returns the output of the comb filter */ - void getrcfoutput(); + void getrcfoutput(); // buffers - double *dfbuffer; /**< to hold detection function */ - double df512[512]; /**< to hold resampled detection function */ - double *cumscore; /**< to hold cumulative score */ + 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 wv[128]; /**< to hold weighting vector */ - double rcf[128]; /**< to hold comb filter output */ - double t_obs[41]; /**< to hold tempo version of 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 */ - double prev_delta[41]; /**< previous delta */ - double prev_delta_fix[41]; /**< fixed tempo version of previous delta */ + double delta[41]; /**< to hold final tempo candidate array */ + double prev_delta[41]; /**< previous delta */ + double prev_delta_fix[41]; /**< fixed tempo version of previous delta */ - double t_tmat[41][41]; /**< transition matrix */ + double t_tmat[41][41]; /**< transition matrix */ OnsetDetectionFunction odf; - // parameters - double tightness; - double alpha; - double bperiod; - double tempo; + // parameters + double tightness; + double alpha; + double bperiod; + double tempo; - double p_fact; + double p_fact; - // - int m0; // indicates when the next point to predict the next beat is - int beat; + // + int m0; // indicates when the next point to predict the next beat is + int beat; - int dfbuffer_size; + int dfbuffer_size; - int framesize; + int framesize; - int tempofix; + int tempofix; };
--- a/unit-tests/BTrack Tests.xcodeproj/project.pbxproj Wed Jan 22 18:47:16 2014 +0000 +++ b/unit-tests/BTrack Tests.xcodeproj/project.pbxproj Thu Jan 23 12:17:06 2014 +0000 @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + E31C50041891302D006530ED /* Test_BTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E31C50031891302D006530ED /* Test_BTrack.cpp */; }; E38214F0188E7AED00DDD7C8 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38214EF188E7AED00DDD7C8 /* main.cpp */; }; E38214F2188E7AED00DDD7C8 /* BTrack_Tests.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = E38214F1188E7AED00DDD7C8 /* BTrack_Tests.1 */; }; E3A45DB9188E7BCD00B48CE4 /* BTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3A45DB5188E7BCD00B48CE4 /* BTrack.cpp */; }; @@ -27,6 +28,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + E31C50031891302D006530ED /* Test_BTrack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Test_BTrack.cpp; sourceTree = "<group>"; }; E38214EC188E7AED00DDD7C8 /* BTrack Tests */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "BTrack Tests"; sourceTree = BUILT_PRODUCTS_DIR; }; E38214EF188E7AED00DDD7C8 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; }; E38214F1188E7AED00DDD7C8 /* BTrack_Tests.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = BTrack_Tests.1; sourceTree = "<group>"; }; @@ -47,6 +49,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + E31C500218913007006530ED /* tests */ = { + isa = PBXGroup; + children = ( + E31C50031891302D006530ED /* Test_BTrack.cpp */, + ); + name = tests; + path = "BTrack Tests/tests"; + sourceTree = SOURCE_ROOT; + }; E38214E3188E7AED00DDD7C8 = { isa = PBXGroup; children = ( @@ -67,6 +78,7 @@ isa = PBXGroup; children = ( E38214EF188E7AED00DDD7C8 /* main.cpp */, + E31C500218913007006530ED /* tests */, E3A45DB4188E7BCD00B48CE4 /* src */, E38214F1188E7AED00DDD7C8 /* BTrack_Tests.1 */, ); @@ -136,6 +148,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E31C50041891302D006530ED /* Test_BTrack.cpp in Sources */, E3A45DBA188E7BCD00B48CE4 /* OnsetDetectionFunction.cpp in Sources */, E3A45DB9188E7BCD00B48CE4 /* BTrack.cpp in Sources */, E38214F0188E7AED00DDD7C8 /* main.cpp in Sources */, @@ -176,8 +189,19 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + /usr/local/include, + ); + LIBRARY_SEARCH_PATHS = /usr/local/lib; MACOSX_DEPLOYMENT_TARGET = 10.9; ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = ( + "-lboost_unit_test_framework\n-lboost_unit_test_framework\n-lboost_unit_test_framework\n-lboost_unit_test_framework\n-lboost_unit_test_framework\n-lboost_unit_test_framework\n-lboost_unit_test_framework\n-lboost_unit_test_framework\n-lboost_unit_test_framework", + "-lsamplerate", + "-lfftw3", + ); SDKROOT = macosx; }; name = Debug; @@ -208,7 +232,18 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + /usr/local/include, + ); + LIBRARY_SEARCH_PATHS = /usr/local/lib; MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_LDFLAGS = ( + "-lboost_unit_test_framework\n-lboost_unit_test_framework\n-lboost_unit_test_framework\n-lboost_unit_test_framework\n-lboost_unit_test_framework\n-lboost_unit_test_framework\n-lboost_unit_test_framework\n-lboost_unit_test_framework\n-lboost_unit_test_framework", + "-lsamplerate", + "-lfftw3", + ); SDKROOT = macosx; }; name = Release;
--- a/unit-tests/BTrack Tests/main.cpp Wed Jan 22 18:47:16 2014 +0000 +++ b/unit-tests/BTrack Tests/main.cpp Thu Jan 23 12:17:06 2014 +0000 @@ -1,18 +1,3 @@ -// -// main.cpp -// BTrack Tests -// -// Created by Adam Stark on 21/01/2014. -// Copyright (c) 2014 Adam Stark. All rights reserved. -// - -#include <iostream> - -int main(int argc, const char * argv[]) -{ - - // insert code here... - std::cout << "Hello, World!\n"; - return 0; -} - +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE BTrackTests +#include <boost/test/unit_test.hpp> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/unit-tests/BTrack Tests/tests/Test_BTrack.cpp Thu Jan 23 12:17:06 2014 +0000 @@ -0,0 +1,219 @@ +#ifndef BTRACK_TESTS +#define BTRACK_TESTS + +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> + +#include <iostream> +#include "../../../src/BTrack.h" + +//====================================================================== +//=================== PROCESSING SIMPLE VALUES ========================= +//====================================================================== +BOOST_AUTO_TEST_SUITE(processingSimpleValues) + +//====================================================================== +BOOST_AUTO_TEST_CASE(processZeroValuedOnsetDetectionFunctionSamples) +{ + BTrack b(512); + + long numSamples = 20000; + + std::vector<double> odfSamples; + + int maxInterval = 0; + int currentInterval = 0; + int numBeats = 0; + + for (int i = 0;i < numSamples;i++) + { + b.processOnsetDetectionFunctionSample(0.0); + + currentInterval++; + + if (b.playbeat == 1) + { + numBeats++; + + if (currentInterval > maxInterval) + { + maxInterval = currentInterval; + } + + currentInterval = 0; + } + } + + // check that the maximum interval between beats does not + // exceed 100 onset detection function samples (~ 1.3 seconds) + BOOST_CHECK(maxInterval < 100); + + // check that we have at least a beat for every 100 samples + BOOST_CHECK(numBeats > (numSamples/100)); + +} + +//====================================================================== +BOOST_AUTO_TEST_CASE(processRandomOnsetDetectionFunctionSamples) +{ + BTrack b(512); + + long numSamples = 20000; + + std::vector<double> odfSamples; + + int maxInterval = 0; + int currentInterval = 0; + int numBeats = 0; + + for (int i = 0;i < numSamples;i++) + { + odfSamples.push_back(random() % 1000); + } + + for (int i = 0;i < numSamples;i++) + { + b.processOnsetDetectionFunctionSample(odfSamples[i]); + + currentInterval++; + + if (b.playbeat == 1) + { + numBeats++; + + if (currentInterval > maxInterval) + { + maxInterval = currentInterval; + } + + currentInterval = 0; + } + } + + // check that the maximum interval between beats does not + // exceed 100 onset detection function samples (~ 1.3 seconds) + BOOST_CHECK(maxInterval < 100); + + // check that we have at least a beat for every 100 samples + BOOST_CHECK(numBeats > (numSamples/100)); + +} + +//====================================================================== +BOOST_AUTO_TEST_CASE(processNegativeOnsetDetectionFunctionSamples) +{ + BTrack b(512); + + long numSamples = 20000; + + std::vector<double> odfSamples; + + int maxInterval = 0; + int currentInterval = 0; + int numBeats = 0; + + for (int i = 0;i < numSamples;i++) + { + odfSamples.push_back(-1.0*(random() % 1000)); + } + + for (int i = 0;i < numSamples;i++) + { + b.processOnsetDetectionFunctionSample(odfSamples[i]); + + currentInterval++; + + if (b.playbeat == 1) + { + numBeats++; + + if (currentInterval > maxInterval) + { + maxInterval = currentInterval; + } + + currentInterval = 0; + } + } + + // check that the maximum interval between beats does not + // exceed 100 onset detection function samples (~ 1.3 seconds) + BOOST_CHECK(maxInterval < 100); + + // check that we have at least a beat for every 100 samples + BOOST_CHECK(numBeats > (numSamples/100)); + +} + +//====================================================================== +BOOST_AUTO_TEST_CASE(processSeriesOfDeltaFunctions) +{ + BTrack b(512); + + long numSamples = 20000; + int beatPeriod = 43; + + std::vector<double> odfSamples; + + int maxInterval = 0; + int currentInterval = 0; + int numBeats = 0; + int correct = 0; + + for (int i = 0;i < numSamples;i++) + { + if (i % beatPeriod == 0) + { + odfSamples.push_back(1000); + } + else + { + odfSamples.push_back(0.0); + } + } + + for (int i = 0;i < numSamples;i++) + { + b.processOnsetDetectionFunctionSample(odfSamples[i]); + + currentInterval++; + + if (b.playbeat == 1) + { + numBeats++; + + if (currentInterval > maxInterval) + { + maxInterval = currentInterval; + } + + if (currentInterval == beatPeriod) + { + correct++; + } + + currentInterval = 0; + } + } + + // check that the maximum interval between beats does not + // exceed 100 onset detection function samples (~ 1.3 seconds) + BOOST_CHECK(maxInterval < 100); + + // check that we have at least a beat for every 100 samples + BOOST_CHECK(numBeats > (numSamples/100)); + + // check that the number of correct beats is larger than 99% + // of the total number of beats + BOOST_CHECK(((double)correct) > (((double)numBeats)*0.99)); +} + + +BOOST_AUTO_TEST_SUITE_END() + + + + + + +#endif