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