changeset 18:450c53430540 develop

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 <adamstark.uk@gmail.com>
date Wed, 22 Jan 2014 18:47:16 +0000
parents a31841af2bbc
children 88c8d3862eee
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;