changeset 14:c11367df624d

* Renamed NoveltyCurve.* and Spectrogram.* to $(Name)Processor.* * Aligned novelty curve with audio - when performing FIRFilter::process(params), take inputLength after group delay. * Removed trail of Spectrogram. * General tidying!
author Carl Bussey <c.bussey@se10.qmul.ac.uk>
date Thu, 14 Aug 2014 10:31:49 +0100
parents 7680cc4c0073
children 203551cbad47
files FIRFilter.cpp Makefile NoveltyCurve.cpp NoveltyCurve.h NoveltyCurveProcessor.cpp NoveltyCurveProcessor.h Spectrogram.cpp Spectrogram.h SpectrogramProcessor.cpp SpectrogramProcessor.h Tempogram.cpp Tempogram.h TempogramPlugin.cpp TempogramPlugin.h plugins.cpp
diffstat 15 files changed, 935 insertions(+), 868 deletions(-) [+]
line wrap: on
line diff
--- a/FIRFilter.cpp	Wed Aug 13 14:18:00 2014 +0100
+++ b/FIRFilter.cpp	Thu Aug 14 10:31:49 2014 +0100
@@ -77,8 +77,10 @@
     FFT::inverse(m_lengthFIRFFT, m_pFftFilteredReal, m_pFftFilteredImag, m_pFftOutputReal, m_pFftOutputImag);
     
     //copy to output
+    int offset = ceil(m_numberOfCoefficients/2);
+    //int offset = 0;
     for (unsigned int i = 0; i < m_lengthInput; i++){
-        pOutput[i] = m_pFftOutputReal[i];
+        pOutput[i] = m_pFftOutputReal[i + offset];
     }
 }
 
--- a/Makefile	Wed Aug 13 14:18:00 2014 +0100
+++ b/Makefile	Thu Aug 14 10:31:49 2014 +0100
@@ -21,11 +21,11 @@
 
 # Edit this to list the .cpp or .c files in your plugin project
 #
-PLUGIN_SOURCES := Tempogram.cpp FIRFilter.cpp WindowFunction.cpp plugins.cpp NoveltyCurve.cpp Spectrogram.cpp
+PLUGIN_SOURCES := TempogramPlugin.cpp FIRFilter.cpp WindowFunction.cpp plugins.cpp NoveltyCurveProcessor.cpp SpectrogramProcessor.cpp
 
 # Edit this to list the .h files in your plugin project
 #
-PLUGIN_HEADERS := Tempogram.h FIRFilter.h WindowFunction.h NoveltyCurve.h Spectrogram.h
+PLUGIN_HEADERS := TempogramPlugin.h FIRFilter.h WindowFunction.h NoveltyCurveProcessor.h SpectrogramProcessor.h
 
 # Edit this to the location of the Vamp plugin SDK, relative to your
 # project directory
--- a/NoveltyCurve.cpp	Wed Aug 13 14:18:00 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,168 +0,0 @@
-//
-//  NoveltyCurve.cpp
-//  Tempogram
-//
-//  Created by Carl Bussey on 10/07/2014.
-//  Copyright (c) 2014 Carl Bussey. All rights reserved.
-//
-
-//Spectrogram dimensions should be flipped?
-
-#include "NoveltyCurve.h"
-#include <memory>
-using namespace std;
-
-NoveltyCurve::NoveltyCurve(const float &samplingFrequency, const size_t &fftLength, const size_t &numberOfBlocks, const size_t &compressionConstant) :
-    m_samplingFrequency(samplingFrequency),
-    m_fftLength(fftLength),
-    m_blockSize(fftLength/2 + 1),
-    m_numberOfBlocks(numberOfBlocks),
-    m_compressionConstant(compressionConstant),
-    m_numberOfBands(5),
-    m_pBandBoundaries(0),
-    m_pBandSum(0)
-{
-    initialise();
-}
-
-NoveltyCurve::~NoveltyCurve(){
-    cleanup();
-}
-
-//allocate all space and set variable
-void
-NoveltyCurve::initialise(){
-    
-    // for bandwise processing, the band is split into 5 bands. m_pBandBoundaries contains the upper and lower bin boundaries for each band.
-    m_pBandBoundaries = new int[m_numberOfBands+1];
-    m_pBandBoundaries[0] = 0;
-    for (unsigned int band = 1; band < m_numberOfBands; band++){
-        float lowFreq = 500*pow(2.5, (int)band-1);
-        m_pBandBoundaries[band] = m_fftLength*lowFreq/m_samplingFrequency;
-    }
-    m_pBandBoundaries[m_numberOfBands] = m_blockSize;
-    
-    m_pBandSum = new float [m_numberOfBands];
-}
-
-//delete space allocated in initialise()
-void
-NoveltyCurve::cleanup(){
-    delete []m_pBandBoundaries;
-    m_pBandBoundaries = 0;
-    delete []m_pBandSum;
-    m_pBandSum = 0;
-}
-
-//calculate max of spectrogram
-float NoveltyCurve::calculateMax(const vector< vector<float> > &spectrogram) const
-{
-    float max = 0;
-    
-    for (unsigned int j = 0; j < m_numberOfBlocks; j++){
-        for (unsigned int i = 0; i < m_blockSize; i++){
-            max = max > fabs(spectrogram[i][j]) ? max : fabs(spectrogram[i][j]);
-        }
-    }
-    
-    return max;
-}
-
-//subtract local average of novelty curve
-//uses m_hannWindow as filter
-void NoveltyCurve::subtractLocalAverage(vector<float> &noveltyCurve, const size_t &smoothLength) const
-{
-    vector<float> localAverage(m_numberOfBlocks);
-    
-    float * m_hannWindow = new float[smoothLength];
-    WindowFunction::hanning(m_hannWindow, smoothLength, true);
-    
-    FIRFilter filter(m_numberOfBlocks, smoothLength);
-    filter.process(&noveltyCurve[0], m_hannWindow, &localAverage[0]);
-    
-    assert(noveltyCurve.size() == m_numberOfBlocks);
-    for (unsigned int i = 0; i < m_numberOfBlocks; i++){
-        noveltyCurve[i] -= localAverage[i];
-        noveltyCurve[i] = noveltyCurve[i] >= 0 ? noveltyCurve[i] : 0;
-    }
-    
-    delete []m_hannWindow;
-    m_hannWindow = 0;
-}
-
-//smoothed differentiator filter. Flips upper half of hanning window about y-axis to create coefficients.
-void NoveltyCurve::smoothedDifferentiator(vector< vector<float> > &spectrogram, const size_t &smoothLength) const
-{
-    
-    float * diffHannWindow = new float [smoothLength];
-    WindowFunction::hanning(diffHannWindow, smoothLength, true);
-    
-    if(smoothLength%2) diffHannWindow[(smoothLength+1)/2 - 1] = 0;
-    for(unsigned int i = (smoothLength+1)/2; i < smoothLength; i++){
-        diffHannWindow[i] = -diffHannWindow[i];
-    }
-    
-    FIRFilter smoothFilter(m_numberOfBlocks, smoothLength);
-    
-    for (unsigned int i = 0; i < m_blockSize; i++){
-        smoothFilter.process(&spectrogram[i][0], diffHannWindow, &spectrogram[i][0]);
-    }
-}
-
-//half rectification (set negative to zero)
-void NoveltyCurve::halfWaveRectify(vector< vector<float> > &spectrogram) const
-{
-    for (unsigned int block = 0; block < m_numberOfBlocks; block++){
-        for (unsigned int k = 0; k < m_blockSize; k++){
-            if (spectrogram[k][block] < 0.0) spectrogram[k][block] = 0.0;
-        }
-    }
-}
-
-//process method
-vector<float>
-NoveltyCurve::spectrogramToNoveltyCurve(Spectrogram spectrogram) const
-{
-    std::vector<float> noveltyCurve(m_numberOfBlocks);
-    
-    assert(spectrogram.size() == m_blockSize);
-    assert(spectrogram[0].size() == m_numberOfBlocks);
-    
-    //normalise and log spectrogram
-    float normaliseScale = calculateMax(spectrogram);
-    for (unsigned int block = 0; block < m_numberOfBlocks; block++){
-        for (unsigned int k = 0; k < m_blockSize; k++){
-            if(normaliseScale != 0.0) spectrogram[k][block] /= normaliseScale; //normalise
-            spectrogram[k][block] = log(1+m_compressionConstant*spectrogram[k][block]);
-        }
-    }
-
-    //smooted differentiator
-    smoothedDifferentiator(spectrogram, 5); //make smoothLength a parameter!
-    //halfwave rectification
-    halfWaveRectify(spectrogram);
-    
-    //bandwise processing
-    for (unsigned int block = 0; block < m_numberOfBlocks; block++){
-        for (unsigned int band = 0; band < m_numberOfBands; band++){
-            int k = m_pBandBoundaries[band];
-            int bandEnd = m_pBandBoundaries[band+1];
-            m_pBandSum[band] = 0;
-            
-            while(k < bandEnd){
-                m_pBandSum[band] += spectrogram[k][block];
-                k++;
-            }
-        }
-        float total = 0;
-        for(unsigned int band = 0; band < m_numberOfBands; band++){
-            total += m_pBandSum[band];
-        }
-        noveltyCurve[block] = total/m_numberOfBands;
-    }
-    
-    //subtract local averages
-    subtractLocalAverage(noveltyCurve, 65);
-    
-    return noveltyCurve;
-}
--- a/NoveltyCurve.h	Wed Aug 13 14:18:00 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-//
-//  NoveltyCurve.h
-//  Tempogram
-//
-//  Created by Carl Bussey on 10/07/2014.
-//  Copyright (c) 2014 Carl Bussey. All rights reserved.
-//
-
-// Don't new delete objects
-
-#ifndef __Tempogram__NoveltyCurve__
-#define __Tempogram__NoveltyCurve__
-
-#include <iostream>
-#include <cmath>
-#include <vector>
-#include <iostream>
-#include "FIRFilter.h"
-#include "WindowFunction.h"
-#include <cassert>
-#include "Spectrogram.h"
-
-class NoveltyCurve{
-    float m_samplingFrequency;
-    int m_fftLength;
-    int m_blockSize;
-    int m_numberOfBlocks;
-    int m_compressionConstant;
-    int m_numberOfBands;
-    int * m_pBandBoundaries;
-    float * m_pBandSum;
-    
-    void initialise();
-    void cleanup();
-    float calculateMax(const std::vector< std::vector<float> > &spectrogram) const;
-    void subtractLocalAverage(std::vector<float> &noveltyCurve, const size_t &smoothLength) const;
-    void smoothedDifferentiator(std::vector< std::vector<float> > &spectrogram, const size_t &smoothLength) const;
-    void halfWaveRectify(std::vector< std::vector<float> > &spectrogram) const;
-    
-public:
-    
-    NoveltyCurve(const float &samplingFrequency, const size_t &fftLength, const size_t &numberOfBlocks, const size_t &compressionConstant);
-    ~NoveltyCurve();
-    std::vector<float> spectrogramToNoveltyCurve(Spectrogram spectrogram) const;
-};
-
-#endif /* defined(__Tempogram__NoveltyCurve__) */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NoveltyCurveProcessor.cpp	Thu Aug 14 10:31:49 2014 +0100
@@ -0,0 +1,168 @@
+//
+//  NoveltyCurve.cpp
+//  Tempogram
+//
+//  Created by Carl Bussey on 10/07/2014.
+//  Copyright (c) 2014 Carl Bussey. All rights reserved.
+//
+
+//Spectrogram dimensions should be flipped?
+
+#include "NoveltyCurveProcessor.h"
+using namespace std;
+
+NoveltyCurveProcessor::NoveltyCurveProcessor(const float &samplingFrequency, const size_t &fftLength, const size_t &numberOfBlocks, const size_t &compressionConstant) :
+    m_samplingFrequency(samplingFrequency),
+    m_fftLength(fftLength),
+    m_blockSize(fftLength/2 + 1),
+    m_numberOfBlocks(numberOfBlocks),
+    m_compressionConstant(compressionConstant),
+    m_numberOfBands(5),
+    m_pBandBoundaries(0),
+    m_pBandSum(0)
+{
+    initialise();
+}
+
+NoveltyCurveProcessor::~NoveltyCurveProcessor(){
+    cleanup();
+}
+
+//allocate all space and set variable
+void
+NoveltyCurveProcessor::initialise(){
+    
+    // for bandwise processing, the band is split into 5 bands. m_pBandBoundaries contains the upper and lower bin boundaries for each band.
+    m_pBandBoundaries = new int[m_numberOfBands+1];
+    m_pBandBoundaries[0] = 0;
+    for (unsigned int band = 1; band < m_numberOfBands; band++){
+        float lowFreq = 500*pow(2.5, (int)band-1);
+        m_pBandBoundaries[band] = m_fftLength*lowFreq/m_samplingFrequency;
+    }
+    m_pBandBoundaries[m_numberOfBands] = m_blockSize;
+    
+    m_pBandSum = new float [m_numberOfBands];
+}
+
+//delete space allocated in initialise()
+void
+NoveltyCurveProcessor::cleanup(){
+    delete []m_pBandBoundaries;
+    m_pBandBoundaries = 0;
+    delete []m_pBandSum;
+    m_pBandSum = 0;
+}
+
+//calculate max of spectrogram
+float NoveltyCurveProcessor::calculateMax(const vector< vector<float> > &spectrogram) const
+{
+    float max = 0;
+    
+    for (unsigned int j = 0; j < m_numberOfBlocks; j++){
+        for (unsigned int i = 0; i < m_blockSize; i++){
+            max = max > fabs(spectrogram[i][j]) ? max : fabs(spectrogram[i][j]);
+        }
+    }
+    
+    return max;
+}
+
+//subtract local average of novelty curve
+//uses m_hannWindow as filter
+void NoveltyCurveProcessor::subtractLocalAverage(vector<float> &noveltyCurve, const size_t &smoothLength) const
+{
+    vector<float> localAverage(m_numberOfBlocks);
+    
+    float * m_hannWindow = new float[smoothLength];
+    WindowFunction::hanning(m_hannWindow, smoothLength, true);
+    
+    FIRFilter filter(m_numberOfBlocks, smoothLength);
+    filter.process(&noveltyCurve[0], m_hannWindow, &localAverage[0]);
+    
+    assert(noveltyCurve.size() == m_numberOfBlocks);
+    for (unsigned int i = 0; i < m_numberOfBlocks; i++){
+        noveltyCurve[i] -= localAverage[i];
+        noveltyCurve[i] = noveltyCurve[i] >= 0 ? noveltyCurve[i] : 0;
+    }
+    
+    delete []m_hannWindow;
+    m_hannWindow = 0;
+}
+
+//smoothed differentiator filter. Flips upper half of hanning window about y-axis to create coefficients.
+void NoveltyCurveProcessor::smoothedDifferentiator(vector< vector<float> > &spectrogram, const size_t &smoothLength) const
+{
+    
+    float * diffHannWindow = new float [smoothLength];
+    WindowFunction::hanning(diffHannWindow, smoothLength, true);
+    
+    if(smoothLength%2) diffHannWindow[(smoothLength+1)/2 - 1] = 0;
+    for(unsigned int i = (smoothLength+1)/2; i < smoothLength; i++){
+        diffHannWindow[i] = -diffHannWindow[i];
+    }
+    
+    FIRFilter smoothFilter(m_numberOfBlocks, smoothLength);
+    
+    for (unsigned int i = 0; i < m_blockSize; i++){
+        smoothFilter.process(&spectrogram[i][0], diffHannWindow, &spectrogram[i][0]);
+    }
+}
+
+//half rectification (set negative to zero)
+void NoveltyCurveProcessor::halfWaveRectify(vector< vector<float> > &spectrogram) const
+{
+    for (unsigned int block = 0; block < m_numberOfBlocks; block++){
+        for (unsigned int k = 0; k < m_blockSize; k++){
+            if (spectrogram[k][block] < 0.0) spectrogram[k][block] = 0.0;
+        }
+    }
+}
+
+//process method
+vector<float>
+NoveltyCurveProcessor::spectrogramToNoveltyCurve(Spectrogram spectrogram) const
+{
+    std::vector<float> noveltyCurve(m_numberOfBlocks);
+    
+    //cout << spectrogram[0].size() << " : " << m_numberOfBlocks << endl;
+    assert(spectrogram.size() == m_blockSize);
+    assert(spectrogram[0].size() == m_numberOfBlocks);
+    
+    //normalise and log spectrogram
+    float normaliseScale = calculateMax(spectrogram);
+    for (unsigned int block = 0; block < m_numberOfBlocks; block++){
+        for (unsigned int k = 0; k < m_blockSize; k++){
+            if(normaliseScale != 0.0) spectrogram[k][block] /= normaliseScale; //normalise
+            spectrogram[k][block] = log(1+m_compressionConstant*spectrogram[k][block]);
+        }
+    }
+
+    //smooted differentiator
+    smoothedDifferentiator(spectrogram, 5); //make smoothLength a parameter!
+    //halfwave rectification
+    halfWaveRectify(spectrogram);
+    
+    //bandwise processing
+    for (unsigned int block = 0; block < m_numberOfBlocks; block++){
+        for (unsigned int band = 0; band < m_numberOfBands; band++){
+            int k = m_pBandBoundaries[band];
+            int bandEnd = m_pBandBoundaries[band+1];
+            m_pBandSum[band] = 0;
+            
+            while(k < bandEnd){
+                m_pBandSum[band] += spectrogram[k][block];
+                k++;
+            }
+        }
+        float total = 0;
+        for(unsigned int band = 0; band < m_numberOfBands; band++){
+            total += m_pBandSum[band];
+        }
+        noveltyCurve[block] = total/m_numberOfBands;
+    }
+    
+    //subtract local averages
+    subtractLocalAverage(noveltyCurve, 65);
+    
+    return noveltyCurve;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NoveltyCurveProcessor.h	Thu Aug 14 10:31:49 2014 +0100
@@ -0,0 +1,47 @@
+//
+//  NoveltyCurve.h
+//  Tempogram
+//
+//  Created by Carl Bussey on 10/07/2014.
+//  Copyright (c) 2014 Carl Bussey. All rights reserved.
+//
+
+// Don't new delete objects
+
+#ifndef __Tempogram__NoveltyCurve__
+#define __Tempogram__NoveltyCurve__
+
+#include <iostream>
+#include <cmath>
+#include <vector>
+#include <iostream>
+#include "FIRFilter.h"
+#include "WindowFunction.h"
+#include <cassert>
+#include "SpectrogramProcessor.h"
+
+class NoveltyCurveProcessor{
+    float m_samplingFrequency;
+    int m_fftLength;
+    int m_blockSize;
+    int m_numberOfBlocks;
+    int m_compressionConstant;
+    int m_numberOfBands;
+    int * m_pBandBoundaries;
+    float * m_pBandSum;
+    
+    void initialise();
+    void cleanup();
+    float calculateMax(const std::vector< std::vector<float> > &spectrogram) const;
+    void subtractLocalAverage(std::vector<float> &noveltyCurve, const size_t &smoothLength) const;
+    void smoothedDifferentiator(std::vector< std::vector<float> > &spectrogram, const size_t &smoothLength) const;
+    void halfWaveRectify(std::vector< std::vector<float> > &spectrogram) const;
+    
+public:
+    
+    NoveltyCurveProcessor(const float &samplingFrequency, const size_t &fftLength, const size_t &numberOfBlocks, const size_t &compressionConstant);
+    ~NoveltyCurveProcessor();
+    std::vector<float> spectrogramToNoveltyCurve(Spectrogram spectrogram) const;
+};
+
+#endif /* defined(__Tempogram__NoveltyCurve__) */
--- a/Spectrogram.cpp	Wed Aug 13 14:18:00 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-//
-//  Spectrogram.cpp
-//  Tempogram
-//
-//  Created by Carl Bussey on 07/08/2014.
-//  Copyright (c) 2014 Carl Bussey. All rights reserved.
-//
-
-#include "Spectrogram.h"
-using namespace std;
-using Vamp::FFT;
-#include <iostream>
-
-SpectrogramProcessor::SpectrogramProcessor(const size_t &inputLength, const size_t &windowLength, const size_t &fftLength, const size_t &hopSize) :
-    m_inputLength(inputLength),
-    m_windowLength(windowLength),
-    m_fftLength(fftLength),
-    m_hopSize(hopSize),
-    m_numberOfOutputBins(ceil(fftLength/2) + 1),
-    m_pFftInput(0),
-    m_pFftOutputReal(0),
-    m_pFftOutputImag(0)
-{
-    initialise();
-}
-
-SpectrogramProcessor::~SpectrogramProcessor(){
-    cleanup();
-}
-
-void SpectrogramProcessor::initialise(){
-    m_pFftInput = new double [m_fftLength];
-    m_pFftOutputReal = new double [m_fftLength];
-    m_pFftOutputImag = new double [m_fftLength];
-}
-
-void SpectrogramProcessor::cleanup(){
-    delete []m_pFftInput;
-    delete []m_pFftOutputReal;
-    delete []m_pFftOutputImag;
-    
-    m_pFftInput = m_pFftOutputReal = m_pFftOutputImag = 0;
-}
-
-//process method
-Spectrogram SpectrogramProcessor::process(const float * const pInput, const float * pWindow) const
-{
-    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...
-    Spectrogram spectrogram(m_numberOfOutputBins, vector<float>(numberOfBlocks));
-    
-    int readPointerBeginIndex = m_hopSize-m_windowLength;
-    unsigned int writeBlockPointer = 0;
-    
-    while(readPointerBeginIndex < (int)m_inputLength){
-        
-        int readPointer = readPointerBeginIndex;
-        for (unsigned int n = 0; n < m_windowLength; n++){
-            if(readPointer < 0 || readPointer >= (int)m_inputLength){
-                m_pFftInput[n] = 0.0; //pad with zeros
-            }
-            else{
-                m_pFftInput[n] = pInput[readPointer] * pWindow[n];
-            }
-            readPointer++;
-        }
-        for (unsigned int n = m_windowLength; n < m_fftLength; n++){
-            m_pFftInput[n] = 0.0;
-        }
-        
-        FFT::forward(m_fftLength, m_pFftInput, 0, m_pFftOutputReal, m_pFftOutputImag);
-        
-        //@todo: sample at logarithmic spacing? Leave for host?
-        for(unsigned int k = 0; k < m_numberOfOutputBins; k++){
-            spectrogram[k][writeBlockPointer] = (m_pFftOutputReal[k]*m_pFftOutputReal[k] + m_pFftOutputImag[k]*m_pFftOutputImag[k]); //Magnitude or power?
-            //std::cout << spectrogram[k][writeBlockPointer] << std::endl;
-        }
-        
-        readPointerBeginIndex += m_hopSize;
-        writeBlockPointer++;
-    }
-    
-    return spectrogram;
-}
--- a/Spectrogram.h	Wed Aug 13 14:18:00 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-//
-//  Spectrogram.h
-//  Tempogram
-//
-//  Created by Carl Bussey on 07/08/2014.
-//  Copyright (c) 2014 Carl Bussey. All rights reserved.
-//
-
-#ifndef __Tempogram__Spectrogram__
-#define __Tempogram__Spectrogram__
-#include <vector>
-#include <vamp-sdk/FFT.h>
-#include <cmath>
-
-typedef std::vector <std::vector<float> > Spectrogram;
-typedef std::vector <std::vector<float> > SpectrogramTransposed;
-
-class SpectrogramProcessor{
-    size_t m_inputLength;
-    size_t m_windowLength;
-    size_t m_fftLength;
-    size_t m_hopSize;
-    size_t m_numberOfOutputBins;
-    double * m_pFftInput;
-    double * m_pFftOutputReal;
-    double * m_pFftOutputImag;
-    
-    void initialise();
-    void cleanup();
-public:
-    SpectrogramProcessor(const size_t &inputLength, const size_t &windowLength, const size_t &fftLength, const size_t &hopSize);
-    ~SpectrogramProcessor();
-    
-    std::vector< std::vector<float> > process(const float * const pInput, const float * pWindow) const;
-};
-
-#endif /* defined(__Tempogram__Spectrogram__) */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SpectrogramProcessor.cpp	Thu Aug 14 10:31:49 2014 +0100
@@ -0,0 +1,100 @@
+//
+//  Spectrogram.cpp
+//  Tempogram
+//
+//  Created by Carl Bussey on 07/08/2014.
+//  Copyright (c) 2014 Carl Bussey. All rights reserved.
+//
+
+#include "SpectrogramProcessor.h"
+using namespace std;
+using Vamp::FFT;
+#include <iostream>
+
+SpectrogramProcessor::SpectrogramProcessor(const size_t &windowLength, const size_t &fftLength, const size_t &hopSize) :
+    m_windowLength(windowLength),
+    m_fftLength(fftLength),
+    m_hopSize(hopSize),
+    m_numberOfOutputBins(ceil(fftLength/2) + 1),
+    m_pFftInput(0),
+    m_pFftOutputReal(0),
+    m_pFftOutputImag(0)
+{
+    initialise();
+}
+
+SpectrogramProcessor::~SpectrogramProcessor(){
+    cleanup();
+}
+
+void SpectrogramProcessor::initialise(){
+    m_pFftInput = new double [m_fftLength];
+    m_pFftOutputReal = new double [m_fftLength];
+    m_pFftOutputImag = new double [m_fftLength];
+}
+
+void SpectrogramProcessor::cleanup(){
+    delete []m_pFftInput;
+    delete []m_pFftOutputReal;
+    delete []m_pFftOutputImag;
+    
+    m_pFftInput = m_pFftOutputReal = m_pFftOutputImag = 0;
+}
+
+SpectrogramTransposed SpectrogramProcessor::transpose(const Spectrogram &spectrogram){
+    int numberOfBlocks = spectrogram.size();
+    int numberOfBins = spectrogram[0].size();
+    
+    SpectrogramTransposed spectrogramT(numberOfBins, vector<float>(numberOfBlocks));
+    
+    for (int i = 0; i < numberOfBlocks; i++){
+        for (int j = 0; j < numberOfBins; j++){
+            spectrogramT[j][i] = spectrogram[i][j];
+        }
+    }
+    
+    return spectrogramT;
+}
+
+//process method
+Spectrogram SpectrogramProcessor::process(const float * const pInput, const size_t &inputLength, const float * pWindow, const bool &transposeOutput) const
+{
+    Spectrogram spectrogram;
+    
+    unsigned int readBlockPointerIndex = 0;
+    unsigned int writeBlockPointer = 0;
+    
+    //cout << m_hopSize << endl;
+    while(readBlockPointerIndex <= inputLength) {
+        
+        int readPointer = readBlockPointerIndex - m_windowLength/2;
+        for (unsigned int n = 0; n < m_windowLength; n++){
+            if(readPointer < 0 || readPointer >= (int)inputLength){
+                m_pFftInput[n] = 0.0; //pad with zeros
+            }
+            else{
+                m_pFftInput[n] = pInput[readPointer] * pWindow[n];
+            }
+            readPointer++;
+        }
+        for (unsigned int n = m_windowLength; n < m_fftLength; n++){
+            m_pFftInput[n] = 0.0;
+        }
+        
+        FFT::forward(m_fftLength, m_pFftInput, 0, m_pFftOutputReal, m_pFftOutputImag);
+        
+        vector<float> binValues;
+        //@todo: sample at logarithmic spacing? Leave for host?
+        for(unsigned int k = 0; k < m_numberOfOutputBins; k++){
+            binValues.push_back(m_pFftOutputReal[k]*m_pFftOutputReal[k] + m_pFftOutputImag[k]*m_pFftOutputImag[k]); //Magnitude or power?
+            //std::cout << spectrogram[k][writeBlockPointer] << std::endl;
+        }
+        spectrogram.push_back(binValues);
+        
+        readBlockPointerIndex += m_hopSize;
+        writeBlockPointer++;
+    }
+    
+    if(transposeOutput) return transpose(spectrogram);
+    else return spectrogram;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SpectrogramProcessor.h	Thu Aug 14 10:31:49 2014 +0100
@@ -0,0 +1,37 @@
+//
+//  Spectrogram.h
+//  Tempogram
+//
+//  Created by Carl Bussey on 07/08/2014.
+//  Copyright (c) 2014 Carl Bussey. All rights reserved.
+//
+
+#ifndef __Tempogram__Spectrogram__
+#define __Tempogram__Spectrogram__
+#include <vector>
+#include <vamp-sdk/FFT.h>
+#include <cmath>
+
+typedef std::vector <std::vector<float> > Spectrogram;
+typedef std::vector <std::vector<float> > SpectrogramTransposed;
+
+class SpectrogramProcessor{
+    size_t m_windowLength;
+    size_t m_fftLength;
+    size_t m_hopSize;
+    size_t m_numberOfOutputBins;
+    double * m_pFftInput;
+    double * m_pFftOutputReal;
+    double * m_pFftOutputImag;
+    
+    void initialise();
+    void cleanup();
+public:
+    SpectrogramProcessor(const size_t &windowLength, const size_t &fftLength, const size_t &hopSize);
+    ~SpectrogramProcessor();
+    
+    std::vector< std::vector<float> > process(const float * const pInput, const size_t &inputLength, const float * pWindow, const bool &transposeOutput = false) const;
+    static SpectrogramTransposed transpose(const Spectrogram &spectrogram);
+};
+
+#endif /* defined(__Tempogram__Spectrogram__) */
--- a/Tempogram.cpp	Wed Aug 13 14:18:00 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,424 +0,0 @@
-
-// This is a skeleton file for use in creating your own plugin
-// libraries.  Replace MyPlugin and myPlugin throughout with the name
-// of your first plugin class, and fill in the gaps as appropriate.
-
-
-#include "Tempogram.h"
-#include <sstream>
-#include <stdexcept>
-
-using Vamp::FFT;
-using Vamp::RealTime;
-using namespace std;
-
-Tempogram::Tempogram(float inputSampleRate) :
-    Plugin(inputSampleRate),
-    m_blockSize(0),
-    m_stepSize(0),
-    m_compressionConstant(1000), //parameter
-    m_minDB(0),
-    m_log2WindowLength(10),
-    m_windowLength(pow((float)2,(float)m_log2WindowLength)), //parameter
-    m_fftLength(4096), //parameter
-    m_hopSize(64), //parameter
-    m_minBPM(30),
-    m_maxBPM(480),
-    m_minBin(0), //set in initialise()
-    m_maxBin(0), //set in initialise()
-    m_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
-    // will not do that for you
-{
-}
-
-Tempogram::~Tempogram()
-{
-    //delete stuff
-    cleanup();
-}
-
-string
-Tempogram::getIdentifier() const
-{
-    return "tempogram";
-}
-
-string
-Tempogram::getName() const
-{
-    return "Tempogram";
-}
-
-string
-Tempogram::getDescription() const
-{
-    // Return something helpful here!
-    return "Cyclic Tempogram as described by Peter Grosche and Meinard Muller";
-}
-
-string
-Tempogram::getMaker() const
-{
-    //Your name here
-    return "Carl Bussey";
-}
-
-int
-Tempogram::getPluginVersion() const
-{
-    // Increment this each time you release a version that behaves
-    // differently from the previous one
-    return 1;
-}
-
-string
-Tempogram::getCopyright() const
-{
-    // This function is not ideally named.  It does not necessarily
-    // need to say who made the plugin -- getMaker does that -- but it
-    // should indicate the terms under which it is distributed.  For
-    // example, "Copyright (year). All Rights Reserved", or "GPL"
-    return "";
-}
-
-Tempogram::InputDomain
-Tempogram::getInputDomain() const
-{
-    return FrequencyDomain;
-}
-
-size_t
-Tempogram::getPreferredBlockSize() const
-{
-    return 2048; // 0 means "I can handle any block size"
-}
-
-size_t 
-Tempogram::getPreferredStepSize() const
-{
-    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
-}
-
-size_t
-Tempogram::getMinChannelCount() const
-{
-    return 1;
-}
-
-size_t
-Tempogram::getMaxChannelCount() const
-{
-    return 1;
-}
-
-Tempogram::ParameterList
-Tempogram::getParameterDescriptors() const
-{
-    ParameterList list;
-
-    // If the plugin has no adjustable parameters, return an empty
-    // list here (and there's no need to provide implementations of
-    // getParameter and setParameter in that case either).
-
-    // Note that it is your responsibility to make sure the parameters
-    // start off having their default values (e.g. in the constructor
-    // above).  The host needs to know the default value so it can do
-    // things like provide a "reset to default" function, but it will
-    // not explicitly set your parameters to their defaults for you if
-    // they have not changed in the mean time.
-
-    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 = "log2TN";
-    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 = 7;
-    d.maxValue = 12;
-    d.defaultValue = 10;
-    d.isQuantized = true;
-    d.quantizeStep = 1;
-    for (int i = d.minValue; i <= d.maxValue; i++){
-        d.valueNames.push_back(floatToString(pow((float)2,(float)i)));
-    }
-    list.push_back(d);
-    
-    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;
-}
-
-float
-Tempogram::getParameter(string identifier) const
-{
-    if (identifier == "C") {
-        return m_compressionConstant; // return the ACTUAL current value of your parameter here!
-    }
-    if (identifier == "log2TN"){
-        return m_log2WindowLength;
-    }
-    if (identifier == "minBPM") {
-        return m_minBPM;
-    }
-    if (identifier == "maxBPM"){
-        return m_maxBPM;
-    }
-    
-    return 0;
-}
-
-void
-Tempogram::setParameter(string identifier, float value) 
-{
-    
-    if (identifier == "C") {
-        m_compressionConstant = value; // set the actual value of your parameter
-    }
-    if (identifier == "log2TN") {
-        m_windowLength = pow(2,value);
-        m_log2WindowLength = value;
-    }
-    if (identifier == "minBPM") {
-        m_minBPM = value;
-    }
-    if (identifier == "maxBPM"){
-        m_maxBPM = value;
-    }
-    
-}
-
-void Tempogram::updateBPMParameters(){
-
-}
-
-Tempogram::ProgramList
-Tempogram::getPrograms() const
-{
-    ProgramList list;
-
-    // If you have no programs, return an empty list (or simply don't
-    // implement this function or getCurrentProgram/selectProgram)
-
-    return list;
-}
-
-string
-Tempogram::getCurrentProgram() const
-{
-    return ""; // no programs
-}
-
-void
-Tempogram::selectProgram(string name)
-{
-}
-
-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
-{
-    OutputList list;
-
-    // See OutputDescriptor documentation for the possibilities here.
-    // Every plugin must have at least one output.
-    
-    OutputDescriptor d;
-    float d_sampleRate;
-    float tempogramInputSampleRate = (float)m_inputSampleRate/m_stepSize;
-    
-    d.identifier = "tempogram";
-    d.name = "Tempogram";
-    d.description = "Tempogram";
-    d.unit = "BPM";
-    d.hasFixedBinCount = true;
-    d.binCount = m_maxBin - m_minBin + 1;
-    d.hasKnownExtents = false;
-    d.isQuantized = false;
-    d.sampleType = OutputDescriptor::FixedSampleRate;
-    d_sampleRate = tempogramInputSampleRate/m_hopSize;
-    d.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
-    for(int i = m_minBin; i <= (int)m_maxBin; i++){
-        float w = ((float)i/m_fftLength)*(tempogramInputSampleRate);
-        d.binNames.push_back(floatToString(w*60));
-    }
-    d.hasDuration = false;
-    list.push_back(d);
-    
-    d.identifier = "nc";
-    d.name = "Novelty Curve";
-    d.description = "Novelty Curve";
-    d.unit = "";
-    d.hasFixedBinCount = true;
-    d.binCount = 1;
-    d.hasKnownExtents = false;
-    d.isQuantized = false;
-    d.sampleType = OutputDescriptor::FixedSampleRate;
-    d_sampleRate = tempogramInputSampleRate;
-    d.sampleRate = d_sampleRate > 0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
-    d.hasDuration = false;
-    list.push_back(d);
-    
-    return list;
-}
-
-bool
-Tempogram::initialise(size_t channels, size_t stepSize, size_t blockSize)
-{
-    if (channels < getMinChannelCount() ||
-	channels > getMaxChannelCount()) return false;
-    
-    // Real initialisation work goes here!
-    m_blockSize = blockSize;
-    m_stepSize = stepSize;
-    m_minDB = pow(10,(float)-74/20);
-    
-    if (m_minBPM > m_maxBPM){
-        m_minBPM = 30;
-        m_maxBPM = 480;
-    }
-    float tempogramInputSampleRate = (float)m_inputSampleRate/m_stepSize;
-    m_minBin = (unsigned int)(max(floor(((m_minBPM/60)/tempogramInputSampleRate)*m_fftLength), (float)0.0));
-    m_maxBin = (unsigned int)(min(ceil(((m_maxBPM/60)/tempogramInputSampleRate)*m_fftLength), (float)m_fftLength/2));
-    
-    m_spectrogram = vector< vector<float> >(m_blockSize/2 + 1);
-    
-    return true;
-}
-
-void Tempogram::cleanup(){
-
-}
-
-void
-Tempogram::reset()
-{
-    // Clear buffers, reset stored values, etc
-    ncTimestamps.clear();
-    m_spectrogram.clear();
-    m_spectrogram = vector< vector<float> >(m_blockSize/2 + 1);
-}
-
-Tempogram::FeatureSet
-Tempogram::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
-{
-    size_t n = m_blockSize/2 + 1;
-    
-    FeatureSet featureSet;
-    Feature feature;
-    
-    const float *in = inputBuffers[0];
-
-    //calculate magnitude of FrequencyDomain input
-    for (unsigned 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 > m_minDB ? magnitude : m_minDB;
-        m_spectrogram[i].push_back(magnitude);
-    }
-    
-    m_numberOfBlocks++;
-    ncTimestamps.push_back(timestamp); //save timestamp
-
-    return featureSet;
-}
-
-Tempogram::FeatureSet
-Tempogram::getRemainingFeatures()
-{
-    
-    float * hannWindowtN = new float[m_windowLength];
-    for (unsigned int i = 0; i < m_windowLength; i++){
-        hannWindowtN[i] = 0.0;
-    }
-    
-    FeatureSet featureSet;
-    
-    //initialise m_noveltyCurve processor
-    NoveltyCurve nc(m_inputSampleRate, m_blockSize, m_numberOfBlocks, m_compressionConstant);
-    m_noveltyCurve = nc.spectrogramToNoveltyCurve(m_spectrogram); //calculate novelty curve from magnitude data
-    
-    //push novelty curve data to featureset 1 and set timestamps
-    for (unsigned int i = 0; i < m_numberOfBlocks; i++){
-        Feature feature;
-        feature.values.push_back(m_noveltyCurve[i]);
-        feature.hasTimestamp = true;
-        feature.timestamp = ncTimestamps[i];
-        featureSet[1].push_back(feature);
-    }
-    
-    //window function for spectrogram
-    WindowFunction::hanning(hannWindowtN,m_windowLength);
-    
-    //initialise spectrogram processor
-    SpectrogramProcessor spectrogramProcessor(m_numberOfBlocks, m_windowLength, m_fftLength, m_hopSize);
-    //compute spectrogram from novelty curve data (i.e., tempogram)
-    Spectrogram tempogram = spectrogramProcessor.process(&m_noveltyCurve[0], hannWindowtN);
-    
-    int timePointer = m_hopSize-m_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);
-        
-        assert(tempogram.size() == (m_fftLength/2 + 1));
-        for(int k = m_minBin; k < (int)m_maxBin; k++){
-            feature.values.push_back(tempogram[k][block]);
-            //cout << tempogram[k][block] << endl;
-        }
-        feature.hasTimestamp = true;
-        feature.timestamp = RealTime::fromMilliseconds(timeMS);
-        featureSet[0].push_back(feature);
-        
-        timePointer += m_hopSize;
-    }
-    
-    //float func = [](){ cout << "Hello"; };
-    
-    delete []hannWindowtN;
-    hannWindowtN = 0;
-    
-    return featureSet;
-}
--- a/Tempogram.h	Wed Aug 13 14:18:00 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-
-// This is a skeleton file for use in creating your own plugin
-// 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_
-#define _TEMPOGRAM_H_
-
-#include <vamp-sdk/Plugin.h>
-#include "FIRFilter.h"
-#include "WindowFunction.h"
-#include "NoveltyCurve.h"
-#include "Spectrogram.h"
-#include <vamp-sdk/FFT.h>
-
-#include <cmath>
-#include <fstream>
-#include <cassert>
-#include <string>
-
-using std::string;
-using std::vector;
-
-class Tempogram : public Vamp::Plugin
-{
-public:
-    Tempogram(float inputSampleRate);
-    virtual ~Tempogram();
-
-    string getIdentifier() const;
-    string getName() const;
-    string getDescription() const;
-    string getMaker() const;
-    int getPluginVersion() const;
-    string getCopyright() const;
-
-    InputDomain getInputDomain() const;
-    size_t getPreferredBlockSize() const;
-    size_t getPreferredStepSize() const;
-    size_t getMinChannelCount() const;
-    size_t getMaxChannelCount() const;
-
-    ParameterList getParameterDescriptors() const;
-    float getParameter(string identifier) const;
-    void setParameter(string identifier, float value);
-
-    ProgramList getPrograms() const;
-    string getCurrentProgram() const;
-    void selectProgram(string name);
-
-    OutputList getOutputDescriptors() const;
-    
-    bool initialise(size_t channels, size_t stepSize, size_t blockSize);
-    void reset();
-
-    FeatureSet process(const float *const *inputBuffers,
-                       Vamp::RealTime timestamp);
-
-    FeatureSet getRemainingFeatures();
-
-protected:
-    // plugin-specific data and methods go here
-    size_t m_blockSize;
-    size_t m_stepSize;
-    float m_compressionConstant;
-    vector< vector<float> > m_spectrogram; //spectrogram data
-    vector<float> m_noveltyCurve; //novelty curve data
-    float m_minDB;
-    
-    void cleanup(); //used to release anything allocated in initialise()
-    string floatToString(float value) const;
-    void updateBPMParameters();
-    
-    //FFT params for noveltyCurve -> tempogra
-    int m_log2WindowLength;
-    size_t m_windowLength;
-    size_t m_fftLength;
-    size_t m_hopSize;
-    
-    float m_minBPM; // tempogram output bin range min
-    float m_maxBPM; // tempogram output bin range max
-    unsigned int m_minBin;
-    unsigned int m_maxBin;
-    
-    unsigned int m_numberOfBlocks;
-    
-    vector<Vamp::RealTime> ncTimestamps;
-};
-
-
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TempogramPlugin.cpp	Thu Aug 14 10:31:49 2014 +0100
@@ -0,0 +1,472 @@
+
+// This is a skeleton file for use in creating your own plugin
+// libraries.  Replace MyPlugin and myPlugin throughout with the name
+// of your first plugin class, and fill in the gaps as appropriate.
+
+
+#include "TempogramPlugin.h"
+#include <sstream>
+#include <stdexcept>
+
+using Vamp::FFT;
+using Vamp::RealTime;
+using namespace std;
+
+TempogramPlugin::TempogramPlugin(float inputSampleRate) :
+    Plugin(inputSampleRate),
+    m_blockSize(0),
+    m_stepSize(0),
+    m_compressionConstant(1000), //parameter
+    m_minDB(0),
+    m_log2WindowLength(10), //parameter
+    m_windowLength(pow((float)2,m_log2WindowLength)),
+    m_log2FftLength(m_log2WindowLength),
+    m_fftLength(m_windowLength),
+    m_log2HopSize(6), //parameter
+    m_hopSize(pow((float)2,m_log2HopSize)),
+    m_minBPM(30),
+    m_maxBPM(480),
+    m_minBin(0), //set in initialise()
+    m_maxBin(0) //set in initialise()
+
+    // Also be sure to set your plugin parameters (presumably stored
+    // in member variables) to their default values here -- the host
+    // will not do that for you
+{
+}
+
+TempogramPlugin::~TempogramPlugin()
+{
+    //delete stuff
+    cleanup();
+}
+
+string
+TempogramPlugin::getIdentifier() const
+{
+    return "tempogram";
+}
+
+string
+TempogramPlugin::getName() const
+{
+    return "Tempogram";
+}
+
+string
+TempogramPlugin::getDescription() const
+{
+    // Return something helpful here!
+    return "Cyclic Tempogram as described by Peter Grosche and Meinard Muller";
+}
+
+string
+TempogramPlugin::getMaker() const
+{
+    //Your name here
+    return "Carl Bussey";
+}
+
+int
+TempogramPlugin::getPluginVersion() const
+{
+    // Increment this each time you release a version that behaves
+    // differently from the previous one
+    return 1;
+}
+
+string
+TempogramPlugin::getCopyright() const
+{
+    // This function is not ideally named.  It does not necessarily
+    // need to say who made the plugin -- getMaker does that -- but it
+    // should indicate the terms under which it is distributed.  For
+    // example, "Copyright (year). All Rights Reserved", or "GPL"
+    return "";
+}
+
+TempogramPlugin::InputDomain
+TempogramPlugin::getInputDomain() const
+{
+    return FrequencyDomain;
+}
+
+size_t
+TempogramPlugin::getPreferredBlockSize() const
+{
+    return 2048; // 0 means "I can handle any block size"
+}
+
+size_t 
+TempogramPlugin::getPreferredStepSize() const
+{
+    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
+}
+
+size_t
+TempogramPlugin::getMinChannelCount() const
+{
+    return 1;
+}
+
+size_t
+TempogramPlugin::getMaxChannelCount() const
+{
+    return 1;
+}
+
+TempogramPlugin::ParameterList
+TempogramPlugin::getParameterDescriptors() const
+{
+    ParameterList list;
+
+    // If the plugin has no adjustable parameters, return an empty
+    // list here (and there's no need to provide implementations of
+    // getParameter and setParameter in that case either).
+
+    // Note that it is your responsibility to make sure the parameters
+    // start off having their default values (e.g. in the constructor
+    // above).  The host needs to know the default value so it can do
+    // things like provide a "reset to default" function, but it will
+    // not explicitly set your parameters to their defaults for you if
+    // they have not changed in the mean time.
+
+    ParameterDescriptor d1;
+    d1.identifier = "C";
+    d1.name = "C";
+    d1.description = "Spectrogram compression constant, C, used when retrieving the novelty curve from the audio.";
+    d1.unit = "";
+    d1.minValue = 2;
+    d1.maxValue = 10000;
+    d1.defaultValue = 1000;
+    d1.isQuantized = false;
+    list.push_back(d1);
+
+    ParameterDescriptor d2;
+    d2.identifier = "log2TN";
+    d2.name = "Tempogram Window Length";
+    d2.description = "FFT window length when analysing the novelty curve and extracting the tempogram time-frequency function.";
+    d2.unit = "";
+    d2.minValue = 7;
+    d2.maxValue = 12;
+    d2.defaultValue = 10;
+    d2.isQuantized = true;
+    d2.quantizeStep = 1;
+    for (int i = d2.minValue; i <= d2.maxValue; i++){
+        d2.valueNames.push_back(floatToString(pow((float)2,(float)i)));
+    }
+    list.push_back(d2);
+    
+    ParameterDescriptor d3;
+    d3.identifier = "log2HopSize";
+    d3.name = "Tempogram Hopsize";
+    d3.description = "FFT hopsize when analysing the novelty curve and extracting the tempogram time-frequency function.";
+    d3.unit = "";
+    d3.minValue = 6;
+    d3.maxValue = 12;
+    d3.defaultValue = 6;
+    d3.isQuantized = true;
+    d3.quantizeStep = 1;
+    for (int i = d3.minValue; i <= d3.maxValue; i++){
+        d3.valueNames.push_back(floatToString(pow((float)2,(float)i)));
+    }
+    list.push_back(d3);
+    
+    ParameterDescriptor d4;
+    d4.identifier = "log2FftLength";
+    d4.name = "Tempogram FFT Length";
+    d4.description = "FFT length when analysing the novelty curve and extracting the tempogram time-frequency function. This parameter determines the amount of zero padding.";
+    d4.unit = "";
+    d4.minValue = 6;
+    d4.maxValue = 12;
+    d4.defaultValue = d2.defaultValue;
+    d4.isQuantized = true;
+    d4.quantizeStep = 1;
+    for (int i = d4.minValue; i <= d4.maxValue; i++){
+        d4.valueNames.push_back(floatToString(pow((float)2,(float)i)));
+    }
+    list.push_back(d4);
+    
+    ParameterDescriptor d5;
+    d5.identifier = "minBPM";
+    d5.name = "Minimum BPM";
+    d5.description = "The minimum BPM of the tempogram output bins.";
+    d5.unit = "";
+    d5.minValue = 0;
+    d5.maxValue = 2000;
+    d5.defaultValue = 30;
+    d5.isQuantized = true;
+    d5.quantizeStep = 5;
+    list.push_back(d5);
+    
+    ParameterDescriptor d6;
+    d6.identifier = "maxBPM";
+    d6.name = "Maximum BPM";
+    d6.description = "The minimum BPM of the tempogram output bins.";
+    d6.unit = "";
+    d6.minValue = 30;
+    d6.maxValue = 2000;
+    d6.defaultValue = 480;
+    d6.isQuantized = true;
+    d6.quantizeStep = 5;
+    list.push_back(d6);
+
+    return list;
+}
+
+float
+TempogramPlugin::getParameter(string identifier) const
+{
+    if (identifier == "C") {
+        return m_compressionConstant; // return the ACTUAL current value of your parameter here!
+    }
+    else if (identifier == "log2TN"){
+        return m_log2WindowLength;
+    }
+    else if (identifier == "log2HopSize"){
+        return m_log2HopSize;
+    }
+    else if (identifier == "log2FftLength"){
+        return m_log2FftLength;
+    }
+    else if (identifier == "minBPM") {
+        return m_minBPM;
+    }
+    else if (identifier == "maxBPM"){
+        return m_maxBPM;
+    }
+    
+    return 0;
+}
+
+void
+TempogramPlugin::setParameter(string identifier, float value) 
+{
+    
+    if (identifier == "C") {
+        m_compressionConstant = value; // set the actual value of your parameter
+    }
+    else if (identifier == "log2TN") {
+        m_windowLength = pow(2,value);
+        m_log2WindowLength = value;
+    }
+    else if (identifier == "log2HopSize"){
+        m_hopSize = pow(2,value);
+        m_log2HopSize = value;
+    }
+    else if (identifier == "log2HopFftLength"){
+        m_fftLength = pow(2,value);
+        m_log2FftLength = value;
+    }
+    else if (identifier == "minBPM") {
+        m_minBPM = value;
+    }
+    else if (identifier == "maxBPM"){
+        m_maxBPM = value;
+    }
+    
+}
+
+void TempogramPlugin::updateBPMParameters(){
+
+}
+
+TempogramPlugin::ProgramList
+TempogramPlugin::getPrograms() const
+{
+    ProgramList list;
+
+    // If you have no programs, return an empty list (or simply don't
+    // implement this function or getCurrentProgram/selectProgram)
+
+    return list;
+}
+
+string
+TempogramPlugin::getCurrentProgram() const
+{
+    return ""; // no programs
+}
+
+void
+TempogramPlugin::selectProgram(string name)
+{
+}
+
+string TempogramPlugin::floatToString(float value) const
+{
+    ostringstream ss;
+    
+    if(!(ss << value)) throw runtime_error("TempogramPlugin::floatToString(): invalid conversion from float to string");
+    return ss.str();
+}
+
+TempogramPlugin::OutputList
+TempogramPlugin::getOutputDescriptors() const
+{
+    OutputList list;
+
+    // See OutputDescriptor documentation for the possibilities here.
+    // Every plugin must have at least one output.
+    
+    OutputDescriptor d;
+    float d_sampleRate;
+    float tempogramInputSampleRate = (float)m_inputSampleRate/m_stepSize;
+    
+    d.identifier = "tempogram";
+    d.name = "Tempogram";
+    d.description = "Tempogram";
+    d.unit = "BPM";
+    d.hasFixedBinCount = true;
+    d.binCount = m_maxBin - m_minBin + 1;
+    d.hasKnownExtents = false;
+    d.isQuantized = false;
+    d.sampleType = OutputDescriptor::FixedSampleRate;
+    d_sampleRate = tempogramInputSampleRate/m_hopSize;
+    d.sampleRate = d_sampleRate > 0.0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
+    for(int i = m_minBin; i <= (int)m_maxBin; i++){
+        float w = ((float)i/m_fftLength)*(tempogramInputSampleRate);
+        d.binNames.push_back(floatToString(w*60));
+    }
+    d.hasDuration = false;
+    list.push_back(d);
+    
+    d.identifier = "nc";
+    d.name = "Novelty Curve";
+    d.description = "Novelty Curve";
+    d.unit = "";
+    d.hasFixedBinCount = true;
+    d.binCount = 1;
+    d.hasKnownExtents = false;
+    d.isQuantized = false;
+    d.sampleType = OutputDescriptor::FixedSampleRate;
+    d_sampleRate = tempogramInputSampleRate;
+    d.sampleRate = d_sampleRate > 0 && !isnan(d_sampleRate) ? d_sampleRate : 0.0;
+    d.hasDuration = false;
+    list.push_back(d);
+    
+    return list;
+}
+
+bool
+TempogramPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
+{
+    if (channels < getMinChannelCount() ||
+	channels > getMaxChannelCount()) return false;
+    
+    // Real initialisation work goes here!
+    m_blockSize = blockSize;
+    m_stepSize = stepSize;
+    m_minDB = pow(10,(float)-74/20);
+    
+    if (m_fftLength < m_windowLength){
+        m_fftLength = m_windowLength;
+    }
+    if (m_minBPM > m_maxBPM){
+        m_minBPM = 30;
+        m_maxBPM = 480;
+    }
+    float tempogramInputSampleRate = (float)m_inputSampleRate/m_stepSize;
+    m_minBin = (unsigned int)(max(floor(((m_minBPM/60)/tempogramInputSampleRate)*m_fftLength), (float)0.0));
+    m_maxBin = (unsigned int)(min(ceil(((m_maxBPM/60)/tempogramInputSampleRate)*m_fftLength), (float)m_fftLength/2));
+    
+    m_spectrogram = SpectrogramTransposed(m_blockSize/2 + 1);
+    
+    return true;
+}
+
+void TempogramPlugin::cleanup(){
+
+}
+
+void
+TempogramPlugin::reset()
+{
+    // Clear buffers, reset stored values, etc
+    ncTimestamps.clear();
+    m_spectrogram = SpectrogramTransposed(m_blockSize/2 + 1);
+}
+
+TempogramPlugin::FeatureSet
+TempogramPlugin::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
+{
+    size_t n = m_blockSize/2 + 1;
+    
+    FeatureSet featureSet;
+    Feature feature;
+    
+    const float *in = inputBuffers[0];
+
+    //calculate magnitude of FrequencyDomain input
+    for (unsigned 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 > m_minDB ? magnitude : m_minDB;
+        m_spectrogram[i].push_back(magnitude);
+    }
+    
+    ncTimestamps.push_back(timestamp); //save timestamp
+
+    return featureSet;
+}
+
+TempogramPlugin::FeatureSet
+TempogramPlugin::getRemainingFeatures()
+{
+    
+    float * hannWindow = new float[m_windowLength];
+    for (unsigned int i = 0; i < m_windowLength; i++){
+        hannWindow[i] = 0.0;
+    }
+    
+    FeatureSet featureSet;
+    
+    //initialise m_noveltyCurve processor
+    size_t numberOfBlocks = m_spectrogram[0].size();
+    NoveltyCurveProcessor nc(m_inputSampleRate, m_blockSize, numberOfBlocks, m_compressionConstant);
+    m_noveltyCurve = nc.spectrogramToNoveltyCurve(m_spectrogram); //calculate novelty curve from magnitude data
+    
+    //push novelty curve data to featureset 1 and set timestamps
+    for (unsigned int i = 0; i < numberOfBlocks; i++){
+        Feature feature;
+        feature.values.push_back(m_noveltyCurve[i]);
+        feature.hasTimestamp = false;
+        //feature.timestamp = ncTimestamps[i];
+        featureSet[1].push_back(feature);
+    }
+    
+    //window function for spectrogram
+    WindowFunction::hanning(hannWindow, m_windowLength);
+    
+    //initialise spectrogram processor
+    SpectrogramProcessor spectrogramProcessor(m_windowLength, m_fftLength, m_hopSize);
+    //compute spectrogram from novelty curve data (i.e., tempogram)
+    Spectrogram tempogram = spectrogramProcessor.process(&m_noveltyCurve[0], numberOfBlocks, hannWindow);
+    
+    int timePointer = m_hopSize-m_windowLength/2;
+    int tempogramLength = tempogram.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);
+        
+        assert(tempogram[block].size() == (m_fftLength/2 + 1));
+        for(int k = m_minBin; k < (int)m_maxBin; k++){
+            feature.values.push_back(tempogram[block][k]);
+            //cout << tempogram[k][block] << endl;
+        }
+        feature.hasTimestamp = false;
+        //feature.timestamp = RealTime::fromMilliseconds(timeMS);
+        featureSet[0].push_back(feature);
+        
+        timePointer += m_hopSize;
+    }
+    
+    delete []hannWindow;
+    hannWindow = 0;
+    
+    return featureSet;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TempogramPlugin.h	Thu Aug 14 10:31:49 2014 +0100
@@ -0,0 +1,104 @@
+
+// This is a skeleton file for use in creating your own plugin
+// 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_
+#define _TEMPOGRAM_H_
+
+#include <vamp-sdk/Plugin.h>
+#include "FIRFilter.h"
+#include "WindowFunction.h"
+#include "NoveltyCurveProcessor.h"
+#include "SpectrogramProcessor.h"
+#include <vamp-sdk/FFT.h>
+
+#include <cmath>
+#include <fstream>
+#include <cassert>
+#include <string>
+
+using std::string;
+using std::vector;
+
+class TempogramPlugin : public Vamp::Plugin
+{
+public:
+    TempogramPlugin(float inputSampleRate);
+    virtual ~TempogramPlugin();
+
+    string getIdentifier() const;
+    string getName() const;
+    string getDescription() const;
+    string getMaker() const;
+    int getPluginVersion() const;
+    string getCopyright() const;
+
+    InputDomain getInputDomain() const;
+    size_t getPreferredBlockSize() const;
+    size_t getPreferredStepSize() const;
+    size_t getMinChannelCount() const;
+    size_t getMaxChannelCount() const;
+
+    ParameterList getParameterDescriptors() const;
+    float getParameter(string identifier) const;
+    void setParameter(string identifier, float value);
+
+    ProgramList getPrograms() const;
+    string getCurrentProgram() const;
+    void selectProgram(string name);
+
+    OutputList getOutputDescriptors() const;
+    
+    bool initialise(size_t channels, size_t stepSize, size_t blockSize);
+    void reset();
+
+    FeatureSet process(const float *const *inputBuffers,
+                       Vamp::RealTime timestamp);
+
+    FeatureSet getRemainingFeatures();
+
+protected:
+    // plugin-specific data and methods go here
+    size_t m_blockSize;
+    size_t m_stepSize;
+    float m_compressionConstant;
+    SpectrogramTransposed m_spectrogram; //spectrogram data
+    vector<float> m_noveltyCurve; //novelty curve data
+    float m_minDB;
+    
+    void cleanup(); //used to release anything allocated in initialise()
+    string floatToString(float value) const;
+    void updateBPMParameters();
+    
+    //FFT params for noveltyCurve -> tempogra
+    float m_log2WindowLength;
+    size_t m_windowLength;
+    float m_log2FftLength;
+    size_t m_fftLength;
+    float m_log2HopSize;
+    size_t m_hopSize;
+    
+    float m_minBPM; // tempogram output bin range min
+    float m_maxBPM; // tempogram output bin range max
+    unsigned int m_minBin;
+    unsigned int m_maxBin;
+    
+    vector<Vamp::RealTime> ncTimestamps;
+};
+
+
+#endif
--- a/plugins.cpp	Wed Aug 13 14:18:00 2014 +0100
+++ b/plugins.cpp	Thu Aug 14 10:31:49 2014 +0100
@@ -7,12 +7,12 @@
 #include <vamp/vamp.h>
 #include <vamp-sdk/PluginAdapter.h>
 
-#include "Tempogram.h"
+#include "TempogramPlugin.h"
 
 
 // Declare one static adapter here for each plugin class in this library.
 
-static Vamp::PluginAdapter<Tempogram> myPluginAdapter;
+static Vamp::PluginAdapter<TempogramPlugin> myPluginAdapter;
 
 
 // This is the entry-point for the library, and the only function that