Chris@43
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@43
|
2
|
Chris@43
|
3 /*
|
Chris@43
|
4 Vamp Tempogram Plugin
|
Chris@43
|
5 Carl Bussey, Centre for Digital Music, Queen Mary University of London
|
Chris@43
|
6 Copyright 2014 Queen Mary University of London.
|
Chris@43
|
7
|
Chris@43
|
8 This program is free software; you can redistribute it and/or
|
Chris@43
|
9 modify it under the terms of the GNU General Public License as
|
Chris@43
|
10 published by the Free Software Foundation; either version 2 of the
|
Chris@43
|
11 License, or (at your option) any later version. See the file
|
Chris@43
|
12 COPYING included with this distribution for more information.
|
Chris@43
|
13 */
|
c@5
|
14
|
c@11
|
15 //Spectrogram dimensions should be flipped?
|
c@11
|
16
|
c@14
|
17 #include "NoveltyCurveProcessor.h"
|
c@5
|
18 using namespace std;
|
c@5
|
19
|
c@22
|
20 NoveltyCurveProcessor::NoveltyCurveProcessor(const float &samplingFrequency, const size_t &fftLength, const size_t &compressionConstant) :
|
c@5
|
21 m_samplingFrequency(samplingFrequency),
|
c@7
|
22 m_fftLength(fftLength),
|
c@7
|
23 m_blockSize(fftLength/2 + 1),
|
c@5
|
24 m_compressionConstant(compressionConstant),
|
c@5
|
25 m_numberOfBands(5),
|
c@13
|
26 m_pBandBoundaries(0),
|
c@13
|
27 m_pBandSum(0)
|
c@5
|
28 {
|
c@5
|
29 initialise();
|
c@5
|
30 }
|
c@5
|
31
|
c@14
|
32 NoveltyCurveProcessor::~NoveltyCurveProcessor(){
|
c@5
|
33 cleanup();
|
c@5
|
34 }
|
c@5
|
35
|
c@9
|
36 //allocate all space and set variable
|
c@5
|
37 void
|
c@14
|
38 NoveltyCurveProcessor::initialise(){
|
c@5
|
39
|
c@13
|
40 // for bandwise processing, the band is split into 5 bands. m_pBandBoundaries contains the upper and lower bin boundaries for each band.
|
c@13
|
41 m_pBandBoundaries = new int[m_numberOfBands+1];
|
c@13
|
42 m_pBandBoundaries[0] = 0;
|
c@13
|
43 for (unsigned int band = 1; band < m_numberOfBands; band++){
|
c@13
|
44 float lowFreq = 500*pow(2.5, (int)band-1);
|
c@13
|
45 m_pBandBoundaries[band] = m_fftLength*lowFreq/m_samplingFrequency;
|
Chris@33
|
46 if (m_pBandBoundaries[band] > (int)m_blockSize) {
|
Chris@33
|
47 m_pBandBoundaries[band] = m_blockSize;
|
Chris@33
|
48 }
|
c@5
|
49 }
|
c@13
|
50 m_pBandBoundaries[m_numberOfBands] = m_blockSize;
|
c@13
|
51 m_pBandSum = new float [m_numberOfBands];
|
c@5
|
52 }
|
c@5
|
53
|
c@9
|
54 //delete space allocated in initialise()
|
c@5
|
55 void
|
c@14
|
56 NoveltyCurveProcessor::cleanup(){
|
c@13
|
57 delete []m_pBandBoundaries;
|
c@13
|
58 m_pBandBoundaries = 0;
|
c@13
|
59 delete []m_pBandSum;
|
c@13
|
60 m_pBandSum = 0;
|
c@5
|
61 }
|
c@5
|
62
|
c@9
|
63 //subtract local average of novelty curve
|
c@9
|
64 //uses m_hannWindow as filter
|
c@14
|
65 void NoveltyCurveProcessor::subtractLocalAverage(vector<float> &noveltyCurve, const size_t &smoothLength) const
|
c@13
|
66 {
|
c@22
|
67 int numberOfBlocks = noveltyCurve.size();
|
c@22
|
68 vector<float> localAverage(numberOfBlocks);
|
c@5
|
69
|
c@13
|
70 float * m_hannWindow = new float[smoothLength];
|
c@13
|
71 WindowFunction::hanning(m_hannWindow, smoothLength, true);
|
c@9
|
72
|
c@22
|
73 FIRFilter filter(numberOfBlocks, smoothLength);
|
c@15
|
74 filter.process(&noveltyCurve[0], m_hannWindow, &localAverage[0], FIRFilter::middle);
|
c@5
|
75
|
c@22
|
76 for (int i = 0; i < numberOfBlocks; i++){
|
c@5
|
77 noveltyCurve[i] -= localAverage[i];
|
c@5
|
78 noveltyCurve[i] = noveltyCurve[i] >= 0 ? noveltyCurve[i] : 0;
|
c@5
|
79 }
|
c@9
|
80
|
c@11
|
81 delete []m_hannWindow;
|
c@13
|
82 m_hannWindow = 0;
|
c@5
|
83 }
|
c@5
|
84
|
c@9
|
85 //smoothed differentiator filter. Flips upper half of hanning window about y-axis to create coefficients.
|
c@22
|
86 void NoveltyCurveProcessor::smoothedDifferentiator(SpectrogramTransposed &spectrogramTransposed, const size_t &smoothLength) const
|
c@13
|
87 {
|
c@22
|
88 int numberOfBlocks = spectrogramTransposed[0].size();
|
c@22
|
89
|
c@7
|
90 float * diffHannWindow = new float [smoothLength];
|
c@7
|
91 WindowFunction::hanning(diffHannWindow, smoothLength, true);
|
c@7
|
92
|
c@7
|
93 if(smoothLength%2) diffHannWindow[(smoothLength+1)/2 - 1] = 0;
|
c@20
|
94 for(int i = (smoothLength+1)/2; i < (int)smoothLength; i++){
|
c@7
|
95 diffHannWindow[i] = -diffHannWindow[i];
|
c@7
|
96 }
|
c@7
|
97
|
c@22
|
98 FIRFilter smoothFilter(numberOfBlocks, smoothLength);
|
c@7
|
99
|
c@20
|
100 for (int i = 0; i < (int)m_blockSize; i++){
|
c@22
|
101 smoothFilter.process(&spectrogramTransposed[i][0], diffHannWindow, &spectrogramTransposed[i][0], FIRFilter::middle);
|
c@7
|
102 }
|
Chris@41
|
103
|
Chris@41
|
104 delete[] diffHannWindow;
|
c@7
|
105 }
|
c@7
|
106
|
c@9
|
107 //half rectification (set negative to zero)
|
c@24
|
108 void NoveltyCurveProcessor::halfWaveRectify(Spectrogram &spectrogram) const
|
c@13
|
109 {
|
c@24
|
110 int length = spectrogram.size();
|
c@25
|
111 int height = length > 0 ? spectrogram[0].size() : 0;
|
c@22
|
112
|
c@24
|
113 for (int i = 0; i < length; i++){
|
c@24
|
114 for (int j = 0; j < height; j++){
|
c@24
|
115 if (spectrogram[i][j] < 0.0) spectrogram[i][j] = 0.0;
|
c@7
|
116 }
|
c@7
|
117 }
|
c@7
|
118 }
|
c@7
|
119
|
c@9
|
120 //process method
|
c@5
|
121 vector<float>
|
c@22
|
122 NoveltyCurveProcessor::spectrogramToNoveltyCurve(const Spectrogram &spectrogram) const //make argument const &
|
c@13
|
123 {
|
c@22
|
124 int numberOfBlocks = spectrogram.size();
|
c@22
|
125 std::vector<float> noveltyCurve(numberOfBlocks);
|
c@25
|
126 SpectrogramTransposed spectrogramTransposed(m_blockSize, vector<float>(spectrogram.size()));
|
c@5
|
127
|
c@9
|
128 //normalise and log spectrogram
|
c@25
|
129 float normaliseScale = SpectrogramProcessor::calculateMax(spectrogram);
|
c@22
|
130 for (int block = 0; block < (int)numberOfBlocks; block++){
|
c@20
|
131 for (int k = 0; k < (int)m_blockSize; k++){
|
c@25
|
132 float magnitude = spectrogram[block][k];
|
c@25
|
133 if(normaliseScale != 0.0) magnitude /= normaliseScale; //normalise
|
c@25
|
134 spectrogramTransposed[k][block] = log(1+m_compressionConstant*magnitude);
|
c@7
|
135 }
|
c@7
|
136 }
|
c@24
|
137
|
c@9
|
138 //smooted differentiator
|
c@22
|
139 smoothedDifferentiator(spectrogramTransposed, 5); //make smoothLength a parameter!
|
c@9
|
140 //halfwave rectification
|
c@22
|
141 halfWaveRectify(spectrogramTransposed);
|
c@7
|
142
|
c@9
|
143 //bandwise processing
|
c@22
|
144 for (int block = 0; block < (int)numberOfBlocks; block++){
|
c@20
|
145 for (int band = 0; band < (int)m_numberOfBands; band++){
|
c@13
|
146 int k = m_pBandBoundaries[band];
|
c@13
|
147 int bandEnd = m_pBandBoundaries[band+1];
|
c@13
|
148 m_pBandSum[band] = 0;
|
c@5
|
149
|
c@7
|
150 while(k < bandEnd){
|
c@22
|
151 m_pBandSum[band] += spectrogramTransposed[k][block];
|
c@7
|
152 k++;
|
c@5
|
153 }
|
c@5
|
154 }
|
c@5
|
155 float total = 0;
|
c@20
|
156 for(int band = 0; band < (int)m_numberOfBands; band++){
|
c@13
|
157 total += m_pBandSum[band];
|
c@5
|
158 }
|
c@13
|
159 noveltyCurve[block] = total/m_numberOfBands;
|
c@5
|
160 }
|
c@5
|
161
|
c@9
|
162 //subtract local averages
|
c@29
|
163 subtractLocalAverage(noveltyCurve, 65); //maybe smaller?
|
c@5
|
164
|
c@13
|
165 return noveltyCurve;
|
c@7
|
166 }
|