annotate NoveltyCurve.cpp @ 9:be59b4a73f49

* Added Spectrogram zero padding functionality * Made output bins correspond to BPM * User can now specify a range of output bins to view * Comments added
author Carl Bussey <c.bussey@se10.qmul.ac.uk>
date Tue, 12 Aug 2014 14:40:37 +0100
parents 21147df9cb2d
children 09fb76606b2b
rev   line source
c@5 1 //
c@5 2 // NoveltyCurve.cpp
c@5 3 // Tempogram
c@5 4 //
c@5 5 // Created by Carl Bussey on 10/07/2014.
c@5 6 // Copyright (c) 2014 Carl Bussey. All rights reserved.
c@5 7 //
c@5 8
c@7 9 #include "NoveltyCurve.h"
c@5 10 using namespace std;
c@5 11
c@7 12 NoveltyCurve::NoveltyCurve(float samplingFrequency, int fftLength, int numberOfBlocks, int compressionConstant) :
c@5 13 m_samplingFrequency(samplingFrequency),
c@7 14 m_fftLength(fftLength),
c@7 15 m_blockSize(fftLength/2 + 1),
c@5 16 m_numberOfBlocks(numberOfBlocks),
c@5 17 m_compressionConstant(compressionConstant),
c@5 18 m_numberOfBands(5),
c@5 19 m_bandBoundaries(NULL),
c@5 20 m_hannLength(65),
c@5 21 m_bandSum(NULL)
c@5 22 {
c@5 23 initialise();
c@5 24 }
c@5 25
c@5 26 NoveltyCurve::~NoveltyCurve(){
c@5 27 cleanup();
c@5 28 }
c@5 29
c@9 30 //allocate all space and set variable
c@5 31 void
c@5 32 NoveltyCurve::initialise(){
c@5 33 data = vector<float>(m_numberOfBlocks);
c@5 34
c@9 35 // for bandwise processing, the band is split into 5 bands. m_bandBoundaries contains the upper and lower bin boundaries for each band.
c@9 36 m_bandBoundaries = new int[m_numberOfBands+1];
c@5 37 m_bandBoundaries[0] = 0;
c@5 38 for (int band = 1; band < m_numberOfBands; band++){
c@5 39 float lowFreq = 500*pow(2.5, band-1);
c@7 40 m_bandBoundaries[band] = m_fftLength*lowFreq/m_samplingFrequency;
c@5 41 }
c@7 42 m_bandBoundaries[m_numberOfBands] = m_blockSize;
c@5 43
c@7 44 m_bandSum = new float [m_numberOfBands];
c@5 45 }
c@5 46
c@9 47 //delete space allocated in initialise()
c@5 48 void
c@5 49 NoveltyCurve::cleanup(){
c@5 50 delete []m_bandBoundaries;
c@5 51 m_bandBoundaries = NULL;
c@5 52 delete []m_bandSum;
c@5 53 m_bandSum = NULL;
c@5 54 }
c@5 55
c@9 56 //calculate max of spectrogram
c@7 57 float NoveltyCurve::calculateMax(vector< vector<float> > &spectrogram){
c@5 58 float max = 0;
c@5 59
c@5 60 for (int j = 0; j < m_numberOfBlocks; j++){
c@7 61 for (int i = 0; i < m_blockSize; i++){
c@7 62 max = max > fabs(spectrogram[i][j]) ? max : fabs(spectrogram[i][j]);
c@5 63 }
c@5 64 }
c@5 65
c@5 66 return max;
c@5 67 }
c@5 68
c@9 69 //subtract local average of novelty curve
c@9 70 //uses m_hannWindow as filter
c@7 71 void NoveltyCurve::subtractLocalAverage(vector<float> &noveltyCurve){
c@5 72 vector<float> localAverage(m_numberOfBlocks);
c@5 73
c@9 74 float * m_hannWindow = new float[m_hannLength];
c@9 75 WindowFunction::hanning(m_hannWindow, m_hannLength, true);
c@9 76
c@5 77 FIRFilter *filter = new FIRFilter(m_numberOfBlocks, m_hannLength);
c@5 78 filter->process(&noveltyCurve[0], m_hannWindow, &localAverage[0]);
c@5 79 delete filter;
c@7 80 filter = NULL;
c@5 81
c@7 82 assert(noveltyCurve.size() == m_numberOfBlocks);
c@5 83 for (int i = 0; i < m_numberOfBlocks; i++){
c@5 84 noveltyCurve[i] -= localAverage[i];
c@5 85 noveltyCurve[i] = noveltyCurve[i] >= 0 ? noveltyCurve[i] : 0;
c@5 86 }
c@9 87
c@9 88 delete m_hannWindow;
c@9 89 m_hannWindow = NULL;
c@5 90 }
c@5 91
c@9 92 //smoothed differentiator filter. Flips upper half of hanning window about y-axis to create coefficients.
c@7 93 void NoveltyCurve::smoothedDifferentiator(vector< vector<float> > &spectrogram, int smoothLength){
c@7 94
c@7 95 float * diffHannWindow = new float [smoothLength];
c@7 96 WindowFunction::hanning(diffHannWindow, smoothLength, true);
c@7 97
c@7 98 if(smoothLength%2) diffHannWindow[(smoothLength+1)/2 - 1] = 0;
c@7 99 for(int i = (smoothLength+1)/2; i < smoothLength; i++){
c@7 100 diffHannWindow[i] = -diffHannWindow[i];
c@7 101 }
c@7 102
c@7 103 FIRFilter *smoothFilter = new FIRFilter(m_numberOfBlocks, smoothLength);
c@7 104
c@7 105 for (int i = 0; i < m_blockSize; i++){
c@7 106 smoothFilter->process(&spectrogram[i][0], diffHannWindow, &spectrogram[i][0]);
c@7 107 }
c@7 108
c@7 109 delete smoothFilter;
c@7 110 smoothFilter = NULL;
c@7 111 }
c@7 112
c@9 113 //half rectification (set negative to zero)
c@9 114 void NoveltyCurve::halfWaveRectify(vector< vector<float> > &spectrogram){
c@7 115
c@7 116 for (int block = 0; block < m_numberOfBlocks; block++){
c@7 117 for (int k = 0; k < m_blockSize; k++){
c@7 118 if (spectrogram[k][block] < 0.0) spectrogram[k][block] = 0.0;
c@7 119 }
c@7 120 }
c@7 121 }
c@7 122
c@9 123 //process method
c@5 124 vector<float>
c@7 125 NoveltyCurve::spectrogramToNoveltyCurve(vector< vector<float> > &spectrogram){
c@7 126
c@7 127 assert(spectrogram.size() == m_blockSize);
c@7 128 assert(spectrogram[0].size() == m_numberOfBlocks);
c@5 129
c@9 130 //normalise and log spectrogram
c@5 131 float normaliseScale = calculateMax(spectrogram);
c@5 132 for (int block = 0; block < m_numberOfBlocks; block++){
c@7 133 for (int k = 0; k < m_blockSize; k++){
c@7 134 if(normaliseScale != 0.0) spectrogram[k][block] /= normaliseScale; //normalise
c@7 135 spectrogram[k][block] = log(1+m_compressionConstant*spectrogram[k][block]);
c@7 136 }
c@7 137 }
c@7 138
c@9 139 //smooted differentiator
c@7 140 smoothedDifferentiator(spectrogram, 5); //make smoothLength a parameter!
c@9 141 //halfwave rectification
c@7 142 halfWaveRectify(spectrogram);
c@7 143
c@9 144 //bandwise processing
c@7 145 for (int block = 0; block < m_numberOfBlocks; block++){
c@5 146 for (int band = 0; band < m_numberOfBands; band++){
c@7 147 int k = m_bandBoundaries[band];
c@7 148 int bandEnd = m_bandBoundaries[band+1];
c@7 149 m_bandSum[band] = 0;
c@5 150
c@7 151 while(k < bandEnd){
c@7 152 m_bandSum[band] += spectrogram[k][block];
c@7 153 k++;
c@5 154 }
c@5 155 }
c@5 156 float total = 0;
c@5 157 for(int band = 0; band < m_numberOfBands; band++){
c@7 158 total += m_bandSum[band];
c@5 159 }
c@7 160 data[block] = total/m_numberOfBands;
c@5 161 }
c@5 162
c@9 163 //subtract local averages
c@7 164 subtractLocalAverage(data);
c@5 165
c@5 166 return data;
c@7 167 }