changeset 9:be59b4a73f49

* Added Spectrogram zero padding functionality * Made output bins correspond to BPM * User can now specify a range of output bins to view * Comments added
author Carl Bussey <c.bussey@se10.qmul.ac.uk>
date Tue, 12 Aug 2014 14:40:37 +0100
parents 4e429b9f2b4d
children 17a260410116 09fb76606b2b
files FIRFilter.cpp FIRFilter.h Makefile NoveltyCurve.cpp NoveltyCurve.h Spectrogram.cpp Spectrogram.h Tempogram.cpp Tempogram.h WindowFunction.cpp
diffstat 10 files changed, 196 insertions(+), 91 deletions(-) [+]
line wrap: on
line diff
--- a/FIRFilter.cpp	Thu Aug 07 17:25:24 2014 +0100
+++ b/FIRFilter.cpp	Tue Aug 12 14:40:37 2014 +0100
@@ -33,9 +33,11 @@
     cleanup();
 }
 
+//allocate memory
 void
 FIRFilter::initialise()
 {
+    //next power of 2
     m_lengthFIRFFT = pow(2,(ceil(log2(m_lengthInput+m_numberOfCoefficients-1))));
     
     fftInput = new double[m_lengthFIRFFT];
@@ -57,6 +59,7 @@
 void
 FIRFilter::process(const float* input, const float* coefficients, float* output)
 {
+    //Copy to same length FFT buffers
     for(int i = 0; i < m_lengthFIRFFT; i++){
         fftInput[i] = i < m_lengthInput ? input[i] : 0.0;
         fftCoefficients[i] = i < m_numberOfCoefficients ? coefficients[i] : 0.0;
@@ -65,17 +68,20 @@
     FFT::forward(m_lengthFIRFFT, fftInput, NULL, fftReal1, fftImag1);
     FFT::forward(m_lengthFIRFFT, fftCoefficients, NULL, fftReal2, fftImag2);
     
+    //Multiply FFT coefficients. Multiplication in freq domain is convolution in time domain.
     for (int i = 0; i < m_lengthFIRFFT; i++){
         fftFilteredReal[i] = (fftReal1[i] * fftReal2[i]) - (fftImag1[i] * fftImag2[i]);
         fftFilteredImag[i] = (fftReal1[i] * fftImag2[i]) + (fftReal2[i] * fftImag1[i]);
     }
     FFT::inverse(m_lengthFIRFFT, fftFilteredReal, fftFilteredImag, fftOutputReal, fftOutputImag);
     
+    //copy to output
     for (int i = 0; i < m_lengthInput; i++){
         output[i] = fftOutputReal[i];
     }
 }
 
+//remove memory allocations
 void
 FIRFilter::cleanup()
 {
--- a/FIRFilter.h	Thu Aug 07 17:25:24 2014 +0100
+++ b/FIRFilter.h	Tue Aug 12 14:40:37 2014 +0100
@@ -12,7 +12,6 @@
 #include <cmath>
 #include <vamp-sdk/FFT.h>
 #include <assert.h>
-#include <iostream>
 
 class FIRFilter{
 public:
--- a/Makefile	Thu Aug 07 17:25:24 2014 +0100
+++ b/Makefile	Tue Aug 12 14:40:37 2014 +0100
@@ -39,7 +39,7 @@
 
 CXX := g++
 #-mmacosx-version-min=10.6
-CXXFLAGS := -mmacosx-version-min=10.6 -arch x86_64 -I$(VAMP_SDK_DIR) -Wall -fPIC
+CXXFLAGS := -mmacosx-version-min=10.6 -arch x86_64 -I$(VAMP_SDK_DIR) -Wall -fPIC 
 PLUGIN_EXT := .dylib
 LDFLAGS := $(CXXFLAGS) -dynamiclib -install_name $(PLUGIN_LIBRARY_NAME)$(PLUGIN_EXT) /usr/local/lib/libvamp-sdk.a -exported_symbols_list vamp-plugin.list
 
--- a/NoveltyCurve.cpp	Thu Aug 07 17:25:24 2014 +0100
+++ b/NoveltyCurve.cpp	Tue Aug 12 14:40:37 2014 +0100
@@ -18,7 +18,6 @@
     m_numberOfBands(5),
     m_bandBoundaries(NULL),
     m_hannLength(65),
-    m_hannWindow(NULL),
     m_bandSum(NULL)
 {
     initialise();
@@ -28,15 +27,13 @@
     cleanup();
 }
 
+//allocate all space and set variable
 void
 NoveltyCurve::initialise(){
     data = vector<float>(m_numberOfBlocks);
     
-    m_hannWindow = new float[m_hannLength];
-    WindowFunction::hanning(m_hannWindow, m_hannLength, true);
-    
-    m_bandBoundaries = new int[m_numberOfBands+1]; //make index variable
-    
+    // for bandwise processing, the band is split into 5 bands. m_bandBoundaries contains the upper and lower bin boundaries for each band.
+    m_bandBoundaries = new int[m_numberOfBands+1];
     m_bandBoundaries[0] = 0;
     for (int band = 1; band < m_numberOfBands; band++){
         float lowFreq = 500*pow(2.5, band-1);
@@ -47,16 +44,16 @@
     m_bandSum = new float [m_numberOfBands];
 }
 
+//delete space allocated in initialise()
 void
 NoveltyCurve::cleanup(){
-    delete []m_hannWindow;
-    m_hannWindow = NULL;
     delete []m_bandBoundaries;
     m_bandBoundaries = NULL;
     delete []m_bandSum;
     m_bandSum = NULL;
 }
 
+//calculate max of spectrogram
 float NoveltyCurve::calculateMax(vector< vector<float> > &spectrogram){
     float max = 0;
     
@@ -69,9 +66,14 @@
     return max;
 }
 
+//subtract local average of novelty curve
+//uses m_hannWindow as filter
 void NoveltyCurve::subtractLocalAverage(vector<float> &noveltyCurve){
     vector<float> localAverage(m_numberOfBlocks);
     
+    float * m_hannWindow = new float[m_hannLength];
+    WindowFunction::hanning(m_hannWindow, m_hannLength, true);
+    
     FIRFilter *filter = new FIRFilter(m_numberOfBlocks, m_hannLength);
     filter->process(&noveltyCurve[0], m_hannWindow, &localAverage[0]);
     delete filter;
@@ -82,11 +84,14 @@
         noveltyCurve[i] -= localAverage[i];
         noveltyCurve[i] = noveltyCurve[i] >= 0 ? noveltyCurve[i] : 0;
     }
+    
+    delete m_hannWindow;
+    m_hannWindow = NULL;
 }
 
+//smoothed differentiator filter. Flips upper half of hanning window about y-axis to create coefficients.
 void NoveltyCurve::smoothedDifferentiator(vector< vector<float> > &spectrogram, int smoothLength){
     
-    //need to make new hannWindow!!
     float * diffHannWindow = new float [smoothLength];
     WindowFunction::hanning(diffHannWindow, smoothLength, true);
     
@@ -105,7 +110,8 @@
     smoothFilter = NULL;
 }
 
-void NoveltyCurve::halfWaveRectify(vector< vector<float> > &spectrogram){ //should this return spectrogram??
+//half rectification (set negative to zero)
+void NoveltyCurve::halfWaveRectify(vector< vector<float> > &spectrogram){
     
     for (int block = 0; block < m_numberOfBlocks; block++){
         for (int k = 0; k < m_blockSize; k++){
@@ -114,14 +120,15 @@
     }
 }
 
+//process method
 vector<float>
 NoveltyCurve::spectrogramToNoveltyCurve(vector< vector<float> > &spectrogram){
     
     assert(spectrogram.size() == m_blockSize);
     assert(spectrogram[0].size() == m_numberOfBlocks);
     
+    //normalise and log spectrogram
     float normaliseScale = calculateMax(spectrogram);
-    
     for (int block = 0; block < m_numberOfBlocks; block++){
         for (int k = 0; k < m_blockSize; k++){
             if(normaliseScale != 0.0) spectrogram[k][block] /= normaliseScale; //normalise
@@ -129,9 +136,12 @@
         }
     }
 
+    //smooted differentiator
     smoothedDifferentiator(spectrogram, 5); //make smoothLength a parameter!
+    //halfwave rectification
     halfWaveRectify(spectrogram);
     
+    //bandwise processing
     for (int block = 0; block < m_numberOfBlocks; block++){
         for (int band = 0; band < m_numberOfBands; band++){
             int k = m_bandBoundaries[band];
@@ -150,6 +160,7 @@
         data[block] = total/m_numberOfBands;
     }
     
+    //subtract local averages
     subtractLocalAverage(data);
     
     return data;
--- a/NoveltyCurve.h	Thu Aug 07 17:25:24 2014 +0100
+++ b/NoveltyCurve.h	Tue Aug 12 14:40:37 2014 +0100
@@ -26,7 +26,6 @@
     int m_numberOfBands;
     int * m_bandBoundaries;
     int m_hannLength;
-    float * m_hannWindow;
     float * m_bandSum;
     
     void initialise();
--- a/Spectrogram.cpp	Thu Aug 07 17:25:24 2014 +0100
+++ b/Spectrogram.cpp	Tue Aug 12 14:40:37 2014 +0100
@@ -7,12 +7,12 @@
 //
 
 #include "Spectrogram.h"
-#include <iostream>
 using namespace std;
 using Vamp::FFT;
 
-Spectrogram::Spectrogram(unsigned int inputLength, unsigned int fftLength, unsigned int hopSize) :
+Spectrogram::Spectrogram(unsigned int inputLength, unsigned int windowLength, unsigned int fftLength, unsigned int hopSize) :
     m_inputLength(inputLength),
+    m_windowLength(windowLength),
     m_fftLength(fftLength),
     m_hopSize(hopSize),
     m_numberOfOutputBins(ceil(fftLength/2) + 1),
@@ -32,7 +32,7 @@
     fftOutputReal = new double [m_fftLength];
     fftOutputImag = new double [m_fftLength];
     
-    int numberOfBlocks = ceil(m_inputLength/m_hopSize) + 2*(ceil(m_fftLength/m_hopSize)-1); //The last term corresponds to overlaps at the beginning and end with padded zeros. I.e., if m_hopSize = m_fftLength/2, there'll be 1 overlap at each end. If m_hopSize = m_fftLength/4, there'll be 3 overlaps at each end, etc...
+    int numberOfBlocks = ceil(m_inputLength/m_hopSize) + 2*(ceil(m_windowLength/m_hopSize)-1); //The last term corresponds to overlaps at the beginning and end with padded zeros. I.e., if m_hopSize = m_windowLength/2, there'll be 1 overlap at each end. If m_hopSize = m_windowLength/4, there'll be 3 overlaps at each end, etc...
     spectrogramOutput = vector< vector<float> >(m_numberOfOutputBins, vector<float>(numberOfBlocks));
 }
 
@@ -44,15 +44,16 @@
     fftInput = fftOutputReal = fftOutputImag = NULL;
 }
 
+//process method
 vector< vector<float> > Spectrogram::audioToMagnitudeSpectrogram(const float * const input, const float * window){
     
-    int readPointerBeginIndex = m_hopSize-m_fftLength;
+    int readPointerBeginIndex = m_hopSize-m_windowLength;
     int writeBlockPointer = 0;
     
     while(readPointerBeginIndex < m_inputLength){
         
         int readPointer = readPointerBeginIndex;
-        for (int n = 0; n < m_fftLength; n++){
+        for (int n = 0; n < m_windowLength; n++){
             if(readPointer < 0 || readPointer >= m_inputLength){
                 fftInput[n] = 0.0; //pad with zeros
             }
@@ -61,13 +62,15 @@
             }
             readPointer++;
         }
+        for (int n = m_windowLength; n < m_fftLength; n++){
+            fftInput[n] = 0.0;
+        }
         
         FFT::forward(m_fftLength, fftInput, NULL, fftOutputReal, fftOutputImag);
         
         //@todo: sample at logarithmic spacing? Leave for host?
         for(int k = 0; k < m_numberOfOutputBins; k++){
             spectrogramOutput[k][writeBlockPointer] = (fftOutputReal[k]*fftOutputReal[k] + fftOutputImag[k]*fftOutputImag[k]); //Magnitude or power?
-            cerr << writeBlockPointer << " : " << spectrogramOutput[k].size() << endl;
         }
         
         readPointerBeginIndex += m_hopSize;
--- a/Spectrogram.h	Thu Aug 07 17:25:24 2014 +0100
+++ b/Spectrogram.h	Tue Aug 12 14:40:37 2014 +0100
@@ -14,6 +14,7 @@
 
 class Spectrogram{
     int m_inputLength;
+    int m_windowLength;
     int m_fftLength;
     int m_hopSize;
     int m_numberOfOutputBins;
@@ -26,7 +27,7 @@
     void cleanup();
 public:
     std::vector< std::vector<float> > audioToMagnitudeSpectrogram(const float * const input, const float * window);
-    Spectrogram(unsigned int inputLength, unsigned int fftLength, unsigned int hopSize);
+    Spectrogram(unsigned int inputLength, unsigned int windowLength, unsigned int fftLength, unsigned int hopSize);
     ~Spectrogram();
 };
 
--- a/Tempogram.cpp	Thu Aug 07 17:25:24 2014 +0100
+++ b/Tempogram.cpp	Tue Aug 12 14:40:37 2014 +0100
@@ -5,6 +5,8 @@
 
 
 #include "Tempogram.h"
+#include <sstream>
+#include <stdexcept>
 
 using Vamp::FFT;
 using Vamp::RealTime;
@@ -14,15 +16,16 @@
     Plugin(inputSampleRate),
     m_blockSize(0),
     m_stepSize(0),
-    compressionConstant(1000), //make param
-    specMax(0),
+    compressionConstant(1000), //parameter
     minDB(0),
-    tN(128), //make param
-    thopSize(64), //make param
-    fftInput(NULL),
-    fftOutputReal(NULL),
-    fftOutputImag(NULL),
-    numberOfBlocks(0)
+    windowLength(128), //parameter
+    fftLength(4096), //parameter
+    thopSize(64), //parameter
+    minBPM(30),
+    maxBPM(480),
+    minBin(0), //set in initialise()
+    maxBin(0), //set in initialise()
+    numberOfBlocks(0) //incremented in process()
 
     // Also be sure to set your plugin parameters (presumably stored
     // in member variables) to their default values here -- the host
@@ -89,13 +92,13 @@
 size_t
 Tempogram::getPreferredBlockSize() const
 {
-    return 0; // 0 means "I can handle any block size"
+    return 2048; // 0 means "I can handle any block size"
 }
 
 size_t 
 Tempogram::getPreferredStepSize() const
 {
-    return 0; // 0 means "anything sensible"; in practice this
+    return 1024; // 0 means "anything sensible"; in practice this
               // means the same as the block size for TimeDomain
               // plugins, or half of it for FrequencyDomain plugins
 }
@@ -128,28 +131,49 @@
     // not explicitly set your parameters to their defaults for you if
     // they have not changed in the mean time.
 
-    ParameterDescriptor C;
-    C.identifier = "C";
-    C.name = "C";
-    C.description = "Spectrogram compression constant, C";
-    C.unit = "";
-    C.minValue = 2;
-    C.maxValue = 10000;
-    C.defaultValue = 1000;
-    C.isQuantized = false;
-    list.push_back(C);
+    ParameterDescriptor d;
+    d.identifier = "C";
+    d.name = "C";
+    d.description = "Spectrogram compression constant, C, used when retrieving the novelty curve from the audio.";
+    d.unit = "";
+    d.minValue = 2;
+    d.maxValue = 10000;
+    d.defaultValue = 1000;
+    d.isQuantized = false;
+    list.push_back(d);
+
+    d.identifier = "TN";
+    d.name = "Tempogram Window Length";
+    d.description = "FFT window length when analysing the novelty curve and extracting the tempogram time-frequency function.";
+    d.unit = "";
+    d.minValue = 1024;
+    d.maxValue = 4096;
+    d.defaultValue = 128;
+    d.isQuantized = true;
+    d.quantizeStep = 128;
+    list.push_back(d);
     
-    ParameterDescriptor tN;
-    tN.identifier = "tN";
-    tN.name = "Tempogram FFT length";
-    tN.description = "Tempogram FFT length.";
-    tN.unit = "";
-    tN.minValue = 128;
-    tN.maxValue = 4096;
-    tN.defaultValue = 128;
-    tN.isQuantized = true;
-    tN.quantizeStep = 128;
-    list.push_back(tN);
+    d.identifier = "minBPM";
+    d.name = "Minimum BPM";
+    d.description = "The minimum BPM of the tempogram output bins.";
+    d.unit = "";
+    d.minValue = 0;
+    d.maxValue = 2000;
+    d.defaultValue = 30;
+    d.isQuantized = true;
+    d.quantizeStep = 5;
+    list.push_back(d);
+    
+    d.identifier = "maxBPM";
+    d.name = "Maximum BPM";
+    d.description = "The minimum BPM of the tempogram output bins.";
+    d.unit = "";
+    d.minValue = 30;
+    d.maxValue = 2000;
+    d.defaultValue = 480;
+    d.isQuantized = true;
+    d.quantizeStep = 5;
+    list.push_back(d);
 
     return list;
 }
@@ -160,8 +184,14 @@
     if (identifier == "C") {
         return compressionConstant; // return the ACTUAL current value of your parameter here!
     }
-    if (identifier == "tN"){
-        return tN;
+    if (identifier == "TN"){
+        return windowLength;
+    }
+    if (identifier == "minBPM") {
+        return minBPM;
+    }
+    if (identifier == "maxBPM"){
+        return maxBPM;
     }
     
     return 0;
@@ -170,12 +200,24 @@
 void
 Tempogram::setParameter(string identifier, float value) 
 {
+    
     if (identifier == "C") {
         compressionConstant = value; // set the actual value of your parameter
     }
-    if (identifier == "tN") {
-        tN = value;
+    if (identifier == "TN") {
+        windowLength = value;
     }
+    if (identifier == "minBPM") {
+        minBPM = value;
+    }
+    if (identifier == "maxBPM"){
+        maxBPM = value;
+    }
+    
+}
+
+void Tempogram::updateBPMParameters(){
+
 }
 
 Tempogram::ProgramList
@@ -200,6 +242,14 @@
 {
 }
 
+string Tempogram::floatToString(float value) const
+{
+    ostringstream ss;
+    
+    if(!(ss << value)) throw runtime_error("Tempogram::floatToString(): invalid conversion from float to string");
+    return ss.str();
+}
+
 Tempogram::OutputList
 Tempogram::getOutputDescriptors() const
 {
@@ -210,18 +260,23 @@
     
     OutputDescriptor d;
     float d_sampleRate;
+    float tempogramInputSampleRate = (float)m_inputSampleRate/m_stepSize;
     
     d.identifier = "tempogram";
     d.name = "Tempogram";
     d.description = "Tempogram";
-    d.unit = "";
+    d.unit = "BPM";
     d.hasFixedBinCount = true;
-    d.binCount = tN/2 + 1;
+    d.binCount = maxBin - minBin + 1;
     d.hasKnownExtents = false;
     d.isQuantized = false;
     d.sampleType = OutputDescriptor::FixedSampleRate;
-    d_sampleRate = m_inputSampleRate/(m_stepSize * thopSize);
+    d_sampleRate = tempogramInputSampleRate/thopSize;
     d.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
+    for(int i = minBin; i <= maxBin; i++){
+        float w = ((float)i/fftLength)*(tempogramInputSampleRate);
+        d.binNames.push_back(floatToString(w*60));
+    }
     d.hasDuration = false;
     list.push_back(d);
     
@@ -234,7 +289,7 @@
     d.hasKnownExtents = false;
     d.isQuantized = false;
     d.sampleType = OutputDescriptor::FixedSampleRate;
-    d_sampleRate = m_inputSampleRate/m_stepSize;
+    d_sampleRate = tempogramInputSampleRate;
     d.sampleRate = d_sampleRate > 0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
     d.hasDuration = false;
     list.push_back(d);
@@ -247,12 +302,20 @@
 {
     if (channels < getMinChannelCount() ||
 	channels > getMaxChannelCount()) return false;
-
+    
     // Real initialisation work goes here!
     m_blockSize = blockSize;
     m_stepSize = stepSize;
     minDB = pow(10,(float)-74/20);
     
+    if (minBPM > maxBPM){
+        minBPM = 30;
+        maxBPM = 480;
+    }
+    float tempogramInputSampleRate = (float)m_inputSampleRate/m_stepSize;
+    minBin = (unsigned int)(max(floor(((minBPM/60)/tempogramInputSampleRate)*fftLength), (float)0.0));
+    maxBin = (unsigned int)(min(ceil(((maxBPM/60)/tempogramInputSampleRate)*fftLength), (float)fftLength/2));
+    
     specData = vector< vector<float> >(m_blockSize/2 + 1);
     
     return true;
@@ -282,6 +345,7 @@
     
     const float *in = inputBuffers[0];
 
+    //calculate magnitude of FrequencyDomain input
     for (int i = 0; i < n; i++){
         float magnitude = sqrt(in[2*i] * in[2*i] + in[2*i + 1] * in[2*i + 1]);
         magnitude = magnitude > minDB ? magnitude : minDB;
@@ -289,23 +353,17 @@
     }
     
     numberOfBlocks++;
-    ncTimestamps.push_back(timestamp);
+    ncTimestamps.push_back(timestamp); //save timestamp
 
     return featureSet;
 }
 
 void
 Tempogram::initialiseForGRF(){
-    hannWindowtN = new float[tN];
-    fftInput = new double[tN];
-    fftOutputReal = new double[tN];
-    fftOutputImag = new double[tN];
+    hannWindowtN = new float[windowLength];
     
-    for (int i = 0; i < tN; i ++){
+    for (int i = 0; i < windowLength; i++){
         hannWindowtN[i] = 0.0;
-        fftInput[i] = 0.0;
-        fftOutputReal[i] = 0.0;
-        fftOutputImag[i] = 0.0;
     }
 }
 
@@ -313,9 +371,10 @@
 Tempogram::cleanupForGRF(){
     delete []hannWindowtN;
     hannWindowtN = NULL;
-    fftInput = fftOutputReal = fftOutputImag = NULL;
 }
 
+
+
 Tempogram::FeatureSet
 Tempogram::getRemainingFeatures()
 {
@@ -323,9 +382,11 @@
     initialiseForGRF();
     FeatureSet featureSet;
     
+    //initialise noveltycurve processor
     NoveltyCurve nc(m_inputSampleRate, m_blockSize, numberOfBlocks, compressionConstant);
-    noveltyCurve = nc.spectrogramToNoveltyCurve(specData);
+    noveltyCurve = nc.spectrogramToNoveltyCurve(specData); //calculate novelty curve from magnitude data
     
+    //push novelty curve data to featureset 1 and set timestamps
     for (int i = 0; i < numberOfBlocks; i++){
         Feature feature;
         feature.values.push_back(noveltyCurve[i]);
@@ -334,21 +395,27 @@
         featureSet[1].push_back(feature);
     }
     
-    WindowFunction::hanning(hannWindowtN, tN);
-    Spectrogram * spectrogramProcessor = new Spectrogram(numberOfBlocks, tN, thopSize);
+    //window function for spectrogram
+    WindowFunction::hanning(hannWindowtN,windowLength);
+    
+    //initialise spectrogram processor
+    Spectrogram * spectrogramProcessor = new Spectrogram(numberOfBlocks, windowLength, fftLength, thopSize);
+    //compute spectrogram from novelty curve data (i.e., tempogram)
     vector< vector<float> > tempogram = spectrogramProcessor->audioToMagnitudeSpectrogram(&noveltyCurve[0], hannWindowtN);
     delete spectrogramProcessor;
     spectrogramProcessor = NULL;
     
-    int timePointer = thopSize-tN/2;
+    int timePointer = thopSize-windowLength/2;
     int tempogramLength = tempogram[0].size();
     
+    //push tempogram data to featureset 0 and set timestamps.
     for (int block = 0; block < tempogramLength; block++){
         Feature feature;
         
         int timeMS = floor(1000*(m_stepSize*timePointer)/m_inputSampleRate + 0.5);
         
-        for(int k = 0; k < tN/2 + 1; k++){
+        assert(tempogram.size() == (fftLength/2 + 1));
+        for(int k = minBin; k < maxBin; k++){
             feature.values.push_back(tempogram[k][block]);
         }
         feature.hasTimestamp = true;
--- a/Tempogram.h	Thu Aug 07 17:25:24 2014 +0100
+++ b/Tempogram.h	Tue Aug 12 14:40:37 2014 +0100
@@ -3,6 +3,17 @@
 // libraries.  Replace MyPlugin and myPlugin throughout with the name
 // of your first plugin class, and fill in the gaps as appropriate.
 
+//* Should I use initialiseForGRF()? I generally think it's nicer to initialise stuff before processing. It just means that for some reason if somebody needs to process quickly (and have preparation time before) it's a bit easier on the load.
+//* I've taken this approach with NoveltyCurve, Spectrogram and FIRFilter too. Is this a good approach?
+//* The names "cleanUpForGRF()" and "initialise...()" are horrible...
+//* The "m_..." variable name thing (I've been quite inconsitent with that)
+//* Using size_t and not unsigned int?
+//* In Tempogram.h, should the protected methods be private?
+//* NoveltyCurve::NoveltyCurve() calls initialise(). May be overdetermined with amount of info? i.e., constructor takes parameters fftLength, numberOfBlocks... these are dimensions of vector< vector<float> >spectrogram.
+//* When to use function() const?
+//* spectrogram continues for too long? see tempogram output
+//* should WindowFunction::hanning be static? Justification: no initialisation needed (i.e., no need for a constructor!).
+
 
 // Remember to use a different guard symbol in each header!
 #ifndef _TEMPOGRAM_H_
@@ -12,11 +23,13 @@
 #include "FIRFilter.h"
 #include "WindowFunction.h"
 #include "NoveltyCurve.h"
+#include "Spectrogram.h"
 #include <vamp-sdk/FFT.h>
+
 #include <cmath>
 #include <fstream>
-#include <assert.h>
-#include "Spectrogram.h"
+#include <cassert>
+#include <string>
 
 using std::string;
 using std::vector;
@@ -49,11 +62,8 @@
     void selectProgram(string name);
 
     OutputList getOutputDescriptors() const;
-
+    
     bool initialise(size_t channels, size_t stepSize, size_t blockSize);
-    void cleanup();
-    void initialiseForGRF();
-    void cleanupForGRF();
     void reset();
 
     FeatureSet process(const float *const *inputBuffers,
@@ -66,18 +76,27 @@
     size_t m_blockSize;
     size_t m_stepSize;
     float compressionConstant;
-    float specMax;
     float *previousY;
     float *currentY;
-    vector< vector<float> > specData;
-    vector<float> noveltyCurve;
+    vector< vector<float> > specData; //spectrogram data
+    vector<float> noveltyCurve; //novelty curve data
     float minDB;
     
-    unsigned int tN;
+    void cleanup(); //used to release anything allocated in initialise()
+    void initialiseForGRF(); //used to initialise anything for getRemainingFeatures()
+    void cleanupForGRF(); //used to clean up anything allocated in initialiseForGRF()
+    string floatToString(float value) const;
+    void updateBPMParameters();
+    
+    //FFT params for noveltyCurve -> tempogra
+    unsigned int windowLength;
+    unsigned int fftLength;
     unsigned int thopSize;
-    double * fftInput;
-    double * fftOutputReal;
-    double * fftOutputImag;
+    
+    float minBPM; // tempogram output bin range min
+    float maxBPM; // tempogram output bin range max
+    unsigned int minBin;
+    unsigned int maxBin;
     
     int numberOfBlocks;
     float *hannWindowtN;
--- a/WindowFunction.cpp	Thu Aug 07 17:25:24 2014 +0100
+++ b/WindowFunction.cpp	Tue Aug 12 14:40:37 2014 +0100
@@ -9,13 +9,13 @@
 #include "WindowFunction.h"
 using std::vector;
 
+//static function
 void
 WindowFunction::hanning(float *signal, const unsigned int N, const bool normalise){
     
     float sum = 0;
     for(int i = 0; i < N; i++){
-        signal[i] = 0.5*(1-cos((float)2*M_PI*i/N));
-        sum += signal[i];
+        sum += signal[i] = 0.5*(1-cos((float)2*M_PI*i/N));
     }
     if (normalise){
         for(int i = 0; i < N; i++){