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