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