cannam@475
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
cannam@475
|
2 /*
|
cannam@475
|
3 QM DSP Library
|
cannam@475
|
4
|
cannam@475
|
5 Centre for Digital Music, Queen Mary, University of London.
|
cannam@475
|
6 This file 2005-2006 Christian Landone and Katy Noland.
|
cannam@475
|
7
|
cannam@475
|
8 Fixes to correct chroma offsets and for thread safety contributed
|
cannam@475
|
9 by Daniel Schürmann.
|
cannam@475
|
10
|
cannam@475
|
11 This program is free software; you can redistribute it and/or
|
cannam@475
|
12 modify it under the terms of the GNU General Public License as
|
cannam@475
|
13 published by the Free Software Foundation; either version 2 of the
|
cannam@475
|
14 License, or (at your option) any later version. See the file
|
cannam@475
|
15 COPYING included with this distribution for more information.
|
cannam@475
|
16 */
|
cannam@475
|
17
|
cannam@475
|
18 #include "GetKeyMode.h"
|
cannam@475
|
19 #include "maths/MathUtilities.h"
|
cannam@475
|
20 #include "base/Pitch.h"
|
cannam@475
|
21
|
cannam@475
|
22 #include <iostream>
|
cannam@475
|
23
|
cannam@475
|
24 #include <cstring>
|
cannam@475
|
25 #include <cstdlib>
|
cannam@475
|
26
|
cannam@475
|
27 static const int kBinsPerOctave = 36;
|
cannam@475
|
28
|
cannam@475
|
29 // Chords profile
|
cannam@475
|
30 static double MajProfile[kBinsPerOctave] = {
|
cannam@475
|
31 0.0384, 0.0629, 0.0258, 0.0121, 0.0146, 0.0106, 0.0364, 0.0610, 0.0267,
|
cannam@475
|
32 0.0126, 0.0121, 0.0086, 0.0364, 0.0623, 0.0279, 0.0275, 0.0414, 0.0186,
|
cannam@475
|
33 0.0173, 0.0248, 0.0145, 0.0364, 0.0631, 0.0262, 0.0129, 0.0150, 0.0098,
|
cannam@499
|
34 0.0312, 0.0521, 0.0235, 0.0129, 0.0142, 0.0095, 0.0289, 0.0478, 0.0239
|
cannam@499
|
35 };
|
cannam@475
|
36
|
cannam@475
|
37 static double MinProfile[kBinsPerOctave] = {
|
cannam@475
|
38 0.0375, 0.0682, 0.0299, 0.0119, 0.0138, 0.0093, 0.0296, 0.0543, 0.0257,
|
cannam@475
|
39 0.0292, 0.0519, 0.0246, 0.0159, 0.0234, 0.0135, 0.0291, 0.0544, 0.0248,
|
cannam@475
|
40 0.0137, 0.0176, 0.0104, 0.0352, 0.0670, 0.0302, 0.0222, 0.0349, 0.0164,
|
cannam@499
|
41 0.0174, 0.0297, 0.0166, 0.0222, 0.0401, 0.0202, 0.0175, 0.0270, 0.0146
|
cannam@499
|
42 };
|
cannam@475
|
43 //
|
cannam@475
|
44
|
cannam@475
|
45
|
cannam@475
|
46 //////////////////////////////////////////////////////////////////////
|
cannam@475
|
47 // Construction/Destruction
|
cannam@475
|
48 //////////////////////////////////////////////////////////////////////
|
cannam@475
|
49
|
cannam@475
|
50 GetKeyMode::GetKeyMode( int sampleRate, float tuningFrequency,
|
cannam@475
|
51 double hpcpAverage, double medianAverage ) :
|
cannam@475
|
52 m_hpcpAverage( hpcpAverage ),
|
cannam@475
|
53 m_medianAverage( medianAverage ),
|
cannam@499
|
54 m_chrPointer(0),
|
cannam@499
|
55 m_decimatedBuffer(0),
|
cannam@499
|
56 m_chromaBuffer(0),
|
cannam@499
|
57 m_meanHPCP(0),
|
cannam@499
|
58 m_majCorr(0),
|
cannam@499
|
59 m_minCorr(0),
|
cannam@499
|
60 m_medianFilterBuffer(0),
|
cannam@499
|
61 m_sortedBuffer(0),
|
cannam@475
|
62 m_keyStrengths(0)
|
cannam@475
|
63 {
|
cannam@499
|
64 m_decimationFactor = 8;
|
cannam@475
|
65
|
cannam@475
|
66 // Chromagram configuration parameters
|
cannam@499
|
67 m_chromaConfig.normalise = MathUtilities::NormaliseUnitMax;
|
cannam@499
|
68 m_chromaConfig.FS = sampleRate / (double)m_decimationFactor;
|
cannam@499
|
69 if (m_chromaConfig.FS < 1) {
|
cannam@499
|
70 m_chromaConfig.FS = 1;
|
cannam@475
|
71 }
|
cannam@475
|
72
|
cannam@475
|
73 // Set C3 (= MIDI #48) as our base:
|
cannam@475
|
74 // This implies that key = 1 => Cmaj, key = 12 => Bmaj, key = 13 => Cmin, etc.
|
cannam@499
|
75 m_chromaConfig.min = Pitch::getFrequencyForPitch( 48, 0, tuningFrequency );
|
cannam@499
|
76 m_chromaConfig.max = Pitch::getFrequencyForPitch( 96, 0, tuningFrequency );
|
cannam@475
|
77
|
cannam@499
|
78 m_chromaConfig.BPO = kBinsPerOctave;
|
cannam@499
|
79 m_chromaConfig.CQThresh = 0.0054;
|
cannam@475
|
80
|
cannam@475
|
81 // Chromagram inst.
|
cannam@499
|
82 m_chroma = new Chromagram( m_chromaConfig );
|
cannam@475
|
83
|
cannam@475
|
84 // Get calculated parameters from chroma object
|
cannam@499
|
85 m_chromaFrameSize = m_chroma->getFrameSize();
|
cannam@475
|
86 // override hopsize for this application
|
cannam@499
|
87 m_chromaHopSize = m_chromaFrameSize;
|
cannam@475
|
88
|
cannam@475
|
89 // std::cerr << "chroma frame size = " << m_ChromaFrameSize << ", decimation factor = " << m_DecimationFactor << " therefore block size = " << getBlockSize() << std::endl;
|
cannam@475
|
90
|
cannam@475
|
91 // Chromagram average and estimated key median filter lengths
|
cannam@499
|
92 m_chromaBufferSize = (int)ceil
|
cannam@499
|
93 (m_hpcpAverage * m_chromaConfig.FS / m_chromaFrameSize);
|
cannam@499
|
94 m_medianWinSize = (int)ceil
|
cannam@499
|
95 (m_medianAverage * m_chromaConfig.FS / m_chromaFrameSize);
|
cannam@475
|
96
|
cannam@475
|
97 // Reset counters
|
cannam@499
|
98 m_bufferIndex = 0;
|
cannam@499
|
99 m_chromaBufferFilling = 0;
|
cannam@499
|
100 m_medianBufferFilling = 0;
|
cannam@475
|
101
|
cannam@475
|
102 // Spawn objectc/arrays
|
cannam@499
|
103 m_decimatedBuffer = new double[m_chromaFrameSize];
|
cannam@499
|
104 m_chromaBuffer = new double[kBinsPerOctave * m_chromaBufferSize];
|
cannam@499
|
105
|
cannam@499
|
106 memset(m_chromaBuffer, 0,
|
cannam@499
|
107 sizeof(double) * kBinsPerOctave * m_chromaBufferSize);
|
cannam@475
|
108
|
cannam@499
|
109 m_meanHPCP = new double[kBinsPerOctave];
|
cannam@475
|
110
|
cannam@499
|
111 m_majCorr = new double[kBinsPerOctave];
|
cannam@499
|
112 m_minCorr = new double[kBinsPerOctave];
|
cannam@475
|
113
|
cannam@499
|
114 m_majProfileNorm = new double[kBinsPerOctave];
|
cannam@499
|
115 m_minProfileNorm = new double[kBinsPerOctave];
|
cannam@475
|
116
|
cannam@475
|
117 double mMaj = MathUtilities::mean( MajProfile, kBinsPerOctave );
|
cannam@475
|
118 double mMin = MathUtilities::mean( MinProfile, kBinsPerOctave );
|
cannam@475
|
119
|
cannam@499
|
120 for (int i = 0; i < kBinsPerOctave; i++) {
|
cannam@499
|
121 m_majProfileNorm[i] = MajProfile[i] - mMaj;
|
cannam@499
|
122 m_minProfileNorm[i] = MinProfile[i] - mMin;
|
cannam@475
|
123 }
|
cannam@475
|
124
|
cannam@499
|
125 m_medianFilterBuffer = new int[ m_medianWinSize ];
|
cannam@499
|
126 memset( m_medianFilterBuffer, 0, sizeof(int)*m_medianWinSize);
|
cannam@475
|
127
|
cannam@499
|
128 m_sortedBuffer = new int[ m_medianWinSize ];
|
cannam@499
|
129 memset( m_sortedBuffer, 0, sizeof(int)*m_medianWinSize);
|
cannam@475
|
130
|
cannam@499
|
131 m_decimator = new Decimator( m_chromaFrameSize * m_decimationFactor,
|
cannam@499
|
132 m_decimationFactor );
|
cannam@475
|
133
|
cannam@475
|
134 m_keyStrengths = new double[24];
|
cannam@475
|
135 }
|
cannam@475
|
136
|
cannam@475
|
137 GetKeyMode::~GetKeyMode()
|
cannam@475
|
138 {
|
cannam@499
|
139 delete m_chroma;
|
cannam@499
|
140 delete m_decimator;
|
cannam@475
|
141
|
cannam@499
|
142 delete [] m_decimatedBuffer;
|
cannam@499
|
143 delete [] m_chromaBuffer;
|
cannam@499
|
144 delete [] m_meanHPCP;
|
cannam@499
|
145 delete [] m_majCorr;
|
cannam@499
|
146 delete [] m_minCorr;
|
cannam@499
|
147 delete [] m_majProfileNorm;
|
cannam@499
|
148 delete [] m_minProfileNorm;
|
cannam@499
|
149 delete [] m_medianFilterBuffer;
|
cannam@499
|
150 delete [] m_sortedBuffer;
|
cannam@475
|
151 delete [] m_keyStrengths;
|
cannam@475
|
152 }
|
cannam@475
|
153
|
cannam@475
|
154 double GetKeyMode::krumCorr( const double *pDataNorm, const double *pProfileNorm,
|
cannam@499
|
155 int shiftProfile, int length)
|
cannam@475
|
156 {
|
cannam@475
|
157 double retVal= 0.0;
|
cannam@475
|
158
|
cannam@475
|
159 double num = 0;
|
cannam@475
|
160 double den = 0;
|
cannam@475
|
161 double sum1 = 0;
|
cannam@475
|
162 double sum2 = 0;
|
cannam@475
|
163
|
cannam@499
|
164 for (int i = 0; i < length; i++) {
|
cannam@478
|
165
|
cannam@475
|
166 int k = (i - shiftProfile + length) % length;
|
cannam@475
|
167
|
cannam@475
|
168 num += pDataNorm[i] * pProfileNorm[k];
|
cannam@475
|
169
|
cannam@499
|
170 sum1 += (pDataNorm[i] * pDataNorm[i]);
|
cannam@499
|
171 sum2 += (pProfileNorm[k] * pProfileNorm[k]);
|
cannam@475
|
172 }
|
cannam@478
|
173
|
cannam@475
|
174 den = sqrt(sum1 * sum2);
|
cannam@475
|
175
|
cannam@499
|
176 if (den > 0) {
|
cannam@475
|
177 retVal = num/den;
|
cannam@475
|
178 } else {
|
cannam@475
|
179 retVal = 0;
|
cannam@475
|
180 }
|
cannam@475
|
181
|
cannam@475
|
182 return retVal;
|
cannam@475
|
183 }
|
cannam@475
|
184
|
cannam@499
|
185 int GetKeyMode::process(double *pcmData)
|
cannam@475
|
186 {
|
cannam@475
|
187 int key;
|
cannam@499
|
188 int j, k;
|
cannam@475
|
189
|
cannam@499
|
190 m_decimator->process(pcmData, m_decimatedBuffer);
|
cannam@475
|
191
|
cannam@499
|
192 m_chrPointer = m_chroma->process(m_decimatedBuffer);
|
cannam@475
|
193
|
cannam@499
|
194 // populate hpcp values
|
cannam@475
|
195 int cbidx;
|
cannam@499
|
196 for (j = 0;j < kBinsPerOctave;j++ ) {
|
cannam@499
|
197 cbidx = (m_bufferIndex * kBinsPerOctave) + j;
|
cannam@499
|
198 m_chromaBuffer[ cbidx ] = m_chrPointer[j];
|
cannam@475
|
199 }
|
cannam@475
|
200
|
cannam@499
|
201 // keep track of input buffers
|
cannam@499
|
202 if (m_bufferIndex++ >= m_chromaBufferSize - 1) {
|
cannam@499
|
203 m_bufferIndex = 0;
|
cannam@475
|
204 }
|
cannam@475
|
205
|
cannam@475
|
206 // track filling of chroma matrix
|
cannam@499
|
207 if (m_chromaBufferFilling++ >= m_chromaBufferSize) {
|
cannam@499
|
208 m_chromaBufferFilling = m_chromaBufferSize;
|
cannam@475
|
209 }
|
cannam@475
|
210
|
cannam@499
|
211 // calculate mean
|
cannam@499
|
212 for (k = 0; k < kBinsPerOctave; k++) {
|
cannam@475
|
213 double mnVal = 0.0;
|
cannam@499
|
214 for (j = 0; j < m_chromaBufferFilling; j++) {
|
cannam@499
|
215 mnVal += m_chromaBuffer[ k + (j * kBinsPerOctave) ];
|
cannam@475
|
216 }
|
cannam@475
|
217
|
cannam@499
|
218 m_meanHPCP[k] = mnVal / (double)m_chromaBufferFilling;
|
cannam@475
|
219 }
|
cannam@475
|
220
|
cannam@475
|
221 // Normalize for zero average
|
cannam@499
|
222 double mHPCP = MathUtilities::mean(m_meanHPCP, kBinsPerOctave);
|
cannam@499
|
223 for (k = 0; k < kBinsPerOctave; k++) {
|
cannam@499
|
224 m_meanHPCP[k] -= mHPCP;
|
cannam@475
|
225 }
|
cannam@475
|
226
|
cannam@499
|
227 for (k = 0; k < kBinsPerOctave; k++) {
|
cannam@478
|
228 // The Chromagram has the center of C at bin 0, while the major
|
cannam@475
|
229 // and minor profiles have the center of C at 1. We want to have
|
cannam@475
|
230 // the correlation for C result also at 1.
|
cannam@475
|
231 // To achieve this we have to shift two times:
|
cannam@499
|
232 m_majCorr[k] = krumCorr
|
cannam@499
|
233 (m_meanHPCP, m_majProfileNorm, k - 2, kBinsPerOctave);
|
cannam@499
|
234 m_minCorr[k] = krumCorr
|
cannam@499
|
235 (m_meanHPCP, m_minProfileNorm, k - 2, kBinsPerOctave);
|
cannam@475
|
236 }
|
cannam@475
|
237
|
cannam@475
|
238 // m_MajCorr[1] is C center 1 / 3 + 1 = 1
|
cannam@475
|
239 // m_MajCorr[4] is D center 4 / 3 + 1 = 2
|
cannam@475
|
240 // '+ 1' because we number keys 1-24, not 0-23.
|
cannam@475
|
241 double maxMaj;
|
cannam@499
|
242 int maxMajBin = MathUtilities::getMax(m_majCorr, kBinsPerOctave, &maxMaj);
|
cannam@475
|
243 double maxMin;
|
cannam@499
|
244 int maxMinBin = MathUtilities::getMax(m_minCorr, kBinsPerOctave, &maxMin);
|
cannam@475
|
245 int maxBin = (maxMaj > maxMin) ? maxMajBin : (maxMinBin + kBinsPerOctave);
|
cannam@475
|
246 key = maxBin / 3 + 1;
|
cannam@475
|
247
|
cannam@499
|
248 // Median filtering
|
cannam@475
|
249
|
cannam@475
|
250 // track Median buffer initial filling
|
cannam@499
|
251 if (m_medianBufferFilling++ >= m_medianWinSize) {
|
cannam@499
|
252 m_medianBufferFilling = m_medianWinSize;
|
cannam@475
|
253 }
|
cannam@475
|
254
|
cannam@499
|
255 // shift median buffer
|
cannam@499
|
256 for (k = 1; k < m_medianWinSize; k++ ) {
|
cannam@499
|
257 m_medianFilterBuffer[ k - 1 ] = m_medianFilterBuffer[ k ];
|
cannam@475
|
258 }
|
cannam@475
|
259
|
cannam@499
|
260 // write new key value into median buffer
|
cannam@499
|
261 m_medianFilterBuffer[ m_medianWinSize - 1 ] = key;
|
cannam@475
|
262
|
cannam@499
|
263 // copy median into sorting buffer, reversed
|
cannam@499
|
264 int ijx = 0;
|
cannam@499
|
265 for (k = 0; k < m_medianWinSize; k++) {
|
cannam@499
|
266 m_sortedBuffer[k] = m_medianFilterBuffer[m_medianWinSize - 1 - ijx];
|
cannam@475
|
267 ijx++;
|
cannam@475
|
268 }
|
cannam@475
|
269
|
cannam@499
|
270 qsort(m_sortedBuffer, m_medianBufferFilling, sizeof(int),
|
cannam@475
|
271 MathUtilities::compareInt);
|
cannam@478
|
272
|
cannam@499
|
273 int sortlength = m_medianBufferFilling;
|
cannam@499
|
274 int midpoint = (int)ceil((double)sortlength / 2);
|
cannam@475
|
275
|
cannam@499
|
276 if (midpoint <= 0) {
|
cannam@475
|
277 midpoint = 1;
|
cannam@475
|
278 }
|
cannam@475
|
279
|
cannam@499
|
280 key = m_sortedBuffer[midpoint-1];
|
cannam@475
|
281
|
cannam@475
|
282 return key;
|
cannam@475
|
283 }
|
cannam@475
|
284
|
cannam@475
|
285
|
cannam@475
|
286 bool GetKeyMode::isModeMinor( int key )
|
cannam@475
|
287 {
|
cannam@475
|
288 return (key > 12);
|
cannam@475
|
289 }
|
cannam@475
|
290
|
cannam@499
|
291 int GetKeyMode::getChromaSize()
|
cannam@475
|
292 {
|
cannam@475
|
293 return kBinsPerOctave;
|
cannam@475
|
294 }
|
cannam@475
|
295
|
cannam@475
|
296 double* GetKeyMode::getKeyStrengths() {
|
cannam@499
|
297 int k;
|
cannam@475
|
298
|
cannam@475
|
299 for (k = 0; k < 24; ++k) {
|
cannam@475
|
300 m_keyStrengths[k] = 0;
|
cannam@475
|
301 }
|
cannam@475
|
302
|
cannam@499
|
303 for (k = 0; k < kBinsPerOctave; k++) {
|
cannam@475
|
304 int idx = k / (kBinsPerOctave/12);
|
cannam@475
|
305 int rem = k % (kBinsPerOctave/12);
|
cannam@499
|
306 if (rem == 0 || m_majCorr[k] > m_keyStrengths[idx]) {
|
cannam@499
|
307 m_keyStrengths[idx] = m_majCorr[k];
|
cannam@475
|
308 }
|
cannam@475
|
309 }
|
cannam@475
|
310
|
cannam@499
|
311 for (k = 0; k < kBinsPerOctave; k++) {
|
cannam@475
|
312 int idx = (k + kBinsPerOctave) / (kBinsPerOctave/12);
|
cannam@475
|
313 int rem = k % (kBinsPerOctave/12);
|
cannam@499
|
314 if (rem == 0 || m_minCorr[k] > m_keyStrengths[idx]) {
|
cannam@499
|
315 m_keyStrengths[idx] = m_minCorr[k];
|
cannam@475
|
316 }
|
cannam@475
|
317 }
|
cannam@475
|
318
|
cannam@475
|
319 return m_keyStrengths;
|
cannam@475
|
320 }
|