changeset 55:5e520f59127f

Changed the interface of the algorithm so that onset detection function samples are calculated internally. This makes the call to the algorithm for most cases much simpler. Also added a static function for calculating beat times in seconds based upon sampling frequency, hop size and the current frame number.
author Adam Stark <adamstark@users.noreply.github.com>
date Wed, 22 Jan 2014 18:47:16 +0000
parents 9699024bb3d0
children b6d440942ff6
files modules-and-plug-ins/python-module/btrack_python_module.cpp src/BTrack.cpp src/BTrack.h
diffstat 3 files changed, 135 insertions(+), 108 deletions(-) [+]
line wrap: on
line diff
--- a/modules-and-plug-ins/python-module/btrack_python_module.cpp	Wed Jan 22 02:49:29 2014 +0000
+++ b/modules-and-plug-ins/python-module/btrack_python_module.cpp	Wed Jan 22 18:47:16 2014 +0000
@@ -4,6 +4,7 @@
 #include "../../src/BTrack.h"
 #include <numpy/arrayobject.h>
 
+//=======================================================================
 static PyObject * btrack_onsetdf(PyObject *dummy, PyObject *args) 
 {
     PyObject *arg1=NULL;
@@ -29,23 +30,19 @@
     
     // get array size
     long signal_length = PyArray_Size((PyObject*)arr1);
-    //int k = (int) theSize;
-    
-    // get data type 
-    //char type = PyArray_DESCR(arr1)->type;
     
     ////////// BEGIN PROCESS ///////////////////
-    int hsize = 512;
-    int fsize = 1024;
+    int hopSize = 512;
+    int frameSize = 1024;
     int df_type = 6;
     int numframes;
-    double buffer[hsize];	// buffer to hold one hopsize worth of audio samples
+    double buffer[hopSize];	// buffer to hold one hopsize worth of audio samples
 
     
     // get number of audio frames, given the hop size and signal length
-	numframes = (int) floor(((double) signal_length) / ((double) hsize));
+	numframes = (int) floor(((double) signal_length) / ((double) hopSize));
     
-    OnsetDetectionFunction onset(hsize,fsize,df_type,1);
+    OnsetDetectionFunction onset(hopSize,frameSize,df_type,1);
 
     double df[numframes];
     
@@ -57,9 +54,9 @@
 	for (int i=0;i < numframes;i++)
 	{		
 		// add new samples to frame
-		for (int n = 0;n < hsize;n++)
+		for (int n = 0;n < hopSize;n++)
 		{
-			buffer[n] = data[(i*hsize)+n];
+			buffer[n] = data[(i*hopSize)+n];
 		}
 		
 		df[i] = onset.getDFsample(buffer);
@@ -91,19 +88,9 @@
     //return Py_None;
     
     return (PyObject *)c;
-    
-    //return Py_BuildValue("c", type);
-    //return Py_BuildValue("d", sum);
-    //return Py_BuildValue("i", k);
-/*    
-fail:
-    Py_XDECREF(arr1); 
-    Py_XDECREF(arr2); 
-    PyArray_XDECREF_ERR(oarr); 
-    return NULL;*/
 }
 
-
+//=======================================================================
 static PyObject * btrack_btrack(PyObject *dummy, PyObject *args) 
 {
     PyObject *arg1=NULL;
@@ -129,35 +116,25 @@
     
     // get array size
     long signal_length = PyArray_Size((PyObject*)arr1);
-    //int k = (int) theSize;
-    
-    // get data type 
-    //char type = PyArray_DESCR(arr1)->type;
+
     
     ////////// BEGIN PROCESS ///////////////////
-    int hsize = 512;
-    int fsize = 1024;
-    int df_type = 6;
+    int hopSize = 512;
+    int frameSize = 1024;
+
     int numframes;
-    double buffer[hsize];	// buffer to hold one hopsize worth of audio samples
+    double buffer[hopSize];	// buffer to hold one hopsize worth of audio samples
     
     
     // get number of audio frames, given the hop size and signal length
-	numframes = (int) floor(((double) signal_length) / ((double) hsize));
+	numframes = (int) floor(((double) signal_length) / ((double) hopSize));
     
-    OnsetDetectionFunction onset(hsize,fsize,df_type,1);
-    BTrack b;
+
+    BTrack b(hopSize,frameSize);
     
-    b.initialise((int) hsize);	// initialise beat tracker
-	
-	// set parameters
-    //b.setparams(0.9,5);
     
-    double df[numframes];
     double beats[5000];
     int beatnum = 0;
-
-    double df_val;
     
     ///////////////////////////////////////////
 	//////// Begin Processing Loop ////////////
@@ -165,21 +142,20 @@
 	for (int i=0;i < numframes;i++)
 	{		
 		// add new samples to frame
-		for (int n = 0;n < hsize;n++)
+		for (int n = 0;n < hopSize;n++)
 		{
-			buffer[n] = data[(i*hsize)+n];
+			buffer[n] = data[(i*hopSize)+n];
 		}
 		
-		df[i] = onset.getDFsample(buffer);
+        // process the current audio frame
+        b.processAudioFrame(buffer);
         
-        df_val = df[i] + 0.0001;
-                
-		b.process(df_val);				// process df sample in beat tracker
-		
+        // if a beat is currently scheduled
 		if (b.playbeat == 1)
 		{
-			beats[beatnum] = (((double) hsize) / 44100) * ((double) i);
-			beatnum = beatnum + 1;	
+			//beats[beatnum] = (((double) hopSize) / 44100) * ((double) i);
+			beats[beatnum] = BTrack::getBeatTimeInSeconds(i,hopSize,44100);
+            beatnum = beatnum + 1;
 		}
 		
 	}
@@ -217,18 +193,9 @@
     //return Py_None;
     
     return (PyObject *)c;
-    
-    //return Py_BuildValue("c", type);
-    //return Py_BuildValue("d", sum);
-    //return Py_BuildValue("i", k);
-    /*    
-     fail:
-     Py_XDECREF(arr1); 
-     Py_XDECREF(arr2); 
-     PyArray_XDECREF_ERR(oarr); 
-     return NULL;*/
 }
 
+//=======================================================================
 static PyObject * btrack_btrack_df(PyObject *dummy, PyObject *args) 
 {
     PyObject *arg1=NULL;
@@ -254,20 +221,12 @@
     
     // get array size
     long numframes = PyArray_Size((PyObject*)arr1);
-    //int k = (int) theSize;
-    
-    // get data type 
-    //char type = PyArray_DESCR(arr1)->type;
-    
+
     ////////// BEGIN PROCESS ///////////////////
-    int hsize = 512;
+    int hopSize = 512;
+    int frameSize = 2*hopSize;
 
-    BTrack b;
-    
-    b.initialise((int) hsize);	// initialise beat tracker
-	
-	// set parameters
-    //b.setparams(0.9,5);
+    BTrack b(hopSize,frameSize);
     
     double beats[5000];
     int beatnum = 0;
@@ -280,11 +239,12 @@
 	{		
         df_val = data[i] + 0.0001;
         
-		b.process(df_val);				// process df sample in beat tracker
+		b.processOnsetDetectionFunctionSample(df_val);				// process df sample in beat tracker
 		
 		if (b.playbeat == 1)
 		{
-			beats[beatnum] = (((double) hsize) / 44100) * ((double) i);
+			//beats[beatnum] = (((double) hopSize) / 44100) * ((double) i);
+            beats[beatnum] = BTrack::getBeatTimeInSeconds(i,hopSize,44100);
 			beatnum = beatnum + 1;	
 		}
 		
@@ -323,20 +283,10 @@
     //return Py_None;
     
     return (PyObject *)c;
-    
-    //return Py_BuildValue("c", type);
-    //return Py_BuildValue("d", sum);
-    //return Py_BuildValue("i", k);
-    /*    
-     fail:
-     Py_XDECREF(arr1); 
-     Py_XDECREF(arr2); 
-     PyArray_XDECREF_ERR(oarr); 
-     return NULL;*/
 }
 
 
-
+//=======================================================================
 static PyMethodDef btrack_methods[] = {
     { "onsetdf",btrack_onsetdf,METH_VARARGS,"onset detection function"},
     { "btrack",btrack_btrack,METH_VARARGS,"beat tracker"},
@@ -344,12 +294,14 @@
     {NULL, NULL, 0, NULL} /* Sentinel */
 };
 
+//=======================================================================
 PyMODINIT_FUNC initbtrack(void)
 {
     (void)Py_InitModule("btrack", btrack_methods);
     import_array();
 }
 
+//=======================================================================
 int main(int argc, char *argv[])
 {
     /* Pass argv[0] to the Python interpreter */
--- a/src/BTrack.cpp	Wed Jan 22 02:49:29 2014 +0000
+++ b/src/BTrack.cpp	Wed Jan 22 18:47:16 2014 +0000
@@ -24,11 +24,54 @@
 #include "BTrack.h"
 #include "samplerate.h"
 
+//=======================================================================
+BTrack::BTrack() : odf(512,1024,6,1)
+{
+    initialise(512, 1024);
+}
 
 //=======================================================================
-BTrack :: BTrack()
+BTrack::BTrack(int hopSize) : odf(hopSize,2*hopSize,6,1)
 {	
-	double rayparam = 43;
+    initialise(hopSize, 2*hopSize);
+}
+
+//=======================================================================
+BTrack::BTrack(int hopSize,int frameSize) : odf(hopSize,frameSize,6,1)
+{
+    initialise(hopSize, frameSize);
+}
+
+//=======================================================================
+BTrack::~BTrack()
+{	
+	
+}
+
+//=======================================================================
+double BTrack::getBeatTimeInSeconds(long frameNumber,int hopSize,int fs)
+{
+    double hop = (double) hopSize;
+    double samplingFrequency = (double) fs;
+    double frameNum = (double) frameNumber;
+    
+    return ((hop / samplingFrequency) * frameNum);
+}
+
+//=======================================================================
+double BTrack::getBeatTimeInSeconds(int frameNumber,int hopSize,int fs)
+{
+    long frameNum = (long) frameNumber;
+    
+    return getBeatTimeInSeconds(frameNum, hopSize, fs);
+}
+
+
+
+//=======================================================================
+void BTrack::initialise(int hopSize, int frameSize)
+{
+    double rayparam = 43;
 	double pi = 3.14159265;
 	
 	
@@ -72,27 +115,22 @@
 			t_mu = i+1;
 			t_tmat[i][j] = (1 / (m_sig * sqrt(2*pi))) * exp( (-1*pow((x-t_mu),2)) / (2*pow(m_sig,2)) );
 		}
-	}	
+	}
 	
 	// tempo is not fixed
 	tempofix = 0;
+    
+    // initialise algorithm given the hopsize
+    setHopSize(hopSize);
 }
 
 //=======================================================================
-BTrack :: ~BTrack()
+void BTrack :: setHopSize(int hopSize)
 {	
+	framesize = hopSize;
+	dfbuffer_size = (512*512)/hopSize;		// calculate df buffer size
 	
-}
-
-
-
-//=======================================================================
-void BTrack :: initialise(int fsize)
-{	
-	framesize = fsize;
-	dfbuffer_size = (512*512)/fsize;		// calculate df buffer size
-	
-	bperiod = round(60/((((double) fsize)/44100)*tempo));
+	bperiod = round(60/((((double) hopSize)/44100)*tempo));
 	
 	dfbuffer = new double[dfbuffer_size];	// create df_buffer
 	cumscore = new double[dfbuffer_size];	// create cumscore
@@ -113,7 +151,21 @@
 }
 
 //=======================================================================
-void BTrack :: process(double df_sample)
+void BTrack::processAudioFrame(double *frame)
+{
+    // 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);
+}
+
+//=======================================================================
+void BTrack::processOnsetDetectionFunctionSample(double newSample)
 {	 
 	m0--;
 	beat--;
@@ -126,10 +178,10 @@
 	}
 	
 	// add new sample at the end
-	dfbuffer[dfbuffer_size-1] = df_sample;	
+	dfbuffer[dfbuffer_size-1] = newSample;
 	
 	// update cumulative score
-	updatecumscore(df_sample);
+	updatecumscore(newSample);
 	
 	// if we are halfway between beats
 	if (m0 == 0)
--- a/src/BTrack.h	Wed Jan 22 02:49:29 2014 +0000
+++ b/src/BTrack.h	Wed Jan 22 18:47:16 2014 +0000
@@ -22,21 +22,39 @@
 #ifndef __BTRACK_H
 #define __BTRACK_H
 
+#include "OnsetDetectionFunction.h"
+
 class BTrack {
 	
 public:
     
-    /** constructor */
+    /** constructor assuming hop size of 512 and frame size of 1024 */
 	BTrack();
     
+    /** 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);
+    
+    /** 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);
+    
     /** destructor */
 	~BTrack();		
 	
-    /** Initialise with frame size and set all frame sizes accordingly */
-	void initialise(int fsize);
+    void initialise(int hopSize,int frameSize);
     
-    /** Add new sample to buffer and apply beat tracking */
-	void process(double df_sample);
+    /** Initialise with hop size and set all frame sizes accordingly */
+	void setHopSize(int hopSize);
+    
+    /** Process a single audio frame */
+	void processAudioFrame(double *frame);
+    
+    /** Add new onset detection function sample to buffer and apply beat tracking */
+	void processOnsetDetectionFunctionSample(double sample);
    
     /** Set the tempo of the beat tracker */
 	void settempo(double tempo);
@@ -46,7 +64,11 @@
     
     /** do not fix the tempo anymore */
 	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;
@@ -100,6 +122,7 @@
 	
 	double t_tmat[41][41];		/**<  transition matrix */
 	
+    OnsetDetectionFunction odf;
 	
 	// parameters
 	double tightness;