# HG changeset patch # User Carl Bussey # Date 1408008709 -3600 # Node ID c11367df624ddbf2cd9bbc386db38d5d179a4eda # Parent 7680cc4c00737dbc4ab948e9edc84d573a812e3f * 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! diff -r 7680cc4c0073 -r c11367df624d FIRFilter.cpp --- 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]; } } diff -r 7680cc4c0073 -r c11367df624d Makefile --- 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 diff -r 7680cc4c0073 -r c11367df624d NoveltyCurve.cpp --- 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 -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 > &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 &noveltyCurve, const size_t &smoothLength) const -{ - vector 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 > &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 > &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 -NoveltyCurve::spectrogramToNoveltyCurve(Spectrogram spectrogram) const -{ - std::vector 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; -} diff -r 7680cc4c0073 -r c11367df624d NoveltyCurve.h --- 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 -#include -#include -#include -#include "FIRFilter.h" -#include "WindowFunction.h" -#include -#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 > &spectrogram) const; - void subtractLocalAverage(std::vector &noveltyCurve, const size_t &smoothLength) const; - void smoothedDifferentiator(std::vector< std::vector > &spectrogram, const size_t &smoothLength) const; - void halfWaveRectify(std::vector< std::vector > &spectrogram) const; - -public: - - NoveltyCurve(const float &samplingFrequency, const size_t &fftLength, const size_t &numberOfBlocks, const size_t &compressionConstant); - ~NoveltyCurve(); - std::vector spectrogramToNoveltyCurve(Spectrogram spectrogram) const; -}; - -#endif /* defined(__Tempogram__NoveltyCurve__) */ diff -r 7680cc4c0073 -r c11367df624d NoveltyCurveProcessor.cpp --- /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 > &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 &noveltyCurve, const size_t &smoothLength) const +{ + vector 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 > &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 > &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 +NoveltyCurveProcessor::spectrogramToNoveltyCurve(Spectrogram spectrogram) const +{ + std::vector 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; +} diff -r 7680cc4c0073 -r c11367df624d NoveltyCurveProcessor.h --- /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 +#include +#include +#include +#include "FIRFilter.h" +#include "WindowFunction.h" +#include +#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 > &spectrogram) const; + void subtractLocalAverage(std::vector &noveltyCurve, const size_t &smoothLength) const; + void smoothedDifferentiator(std::vector< std::vector > &spectrogram, const size_t &smoothLength) const; + void halfWaveRectify(std::vector< std::vector > &spectrogram) const; + +public: + + NoveltyCurveProcessor(const float &samplingFrequency, const size_t &fftLength, const size_t &numberOfBlocks, const size_t &compressionConstant); + ~NoveltyCurveProcessor(); + std::vector spectrogramToNoveltyCurve(Spectrogram spectrogram) const; +}; + +#endif /* defined(__Tempogram__NoveltyCurve__) */ diff -r 7680cc4c0073 -r c11367df624d Spectrogram.cpp --- 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 - -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(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; -} diff -r 7680cc4c0073 -r c11367df624d Spectrogram.h --- 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 -#include -#include - -typedef std::vector > Spectrogram; -typedef std::vector > 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 > process(const float * const pInput, const float * pWindow) const; -}; - -#endif /* defined(__Tempogram__Spectrogram__) */ diff -r 7680cc4c0073 -r c11367df624d SpectrogramProcessor.cpp --- /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 + +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(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 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; +} diff -r 7680cc4c0073 -r c11367df624d SpectrogramProcessor.h --- /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 +#include +#include + +typedef std::vector > Spectrogram; +typedef std::vector > 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 > 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__) */ diff -r 7680cc4c0073 -r c11367df624d Tempogram.cpp --- 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 -#include - -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 >(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 >(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; -} diff -r 7680cc4c0073 -r c11367df624d Tempogram.h --- 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 >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 -#include "FIRFilter.h" -#include "WindowFunction.h" -#include "NoveltyCurve.h" -#include "Spectrogram.h" -#include - -#include -#include -#include -#include - -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 > m_spectrogram; //spectrogram data - vector 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 ncTimestamps; -}; - - -#endif diff -r 7680cc4c0073 -r c11367df624d TempogramPlugin.cpp --- /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 +#include + +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; +} diff -r 7680cc4c0073 -r c11367df624d TempogramPlugin.h --- /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 >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 +#include "FIRFilter.h" +#include "WindowFunction.h" +#include "NoveltyCurveProcessor.h" +#include "SpectrogramProcessor.h" +#include + +#include +#include +#include +#include + +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 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 ncTimestamps; +}; + + +#endif diff -r 7680cc4c0073 -r c11367df624d plugins.cpp --- 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 #include -#include "Tempogram.h" +#include "TempogramPlugin.h" // Declare one static adapter here for each plugin class in this library. -static Vamp::PluginAdapter myPluginAdapter; +static Vamp::PluginAdapter myPluginAdapter; // This is the entry-point for the library, and the only function that