comparison dsp/keydetection/GetKeyMode.cpp @ 34:ad645e404d0c

* Add range of normalise options to chromagram * bit of tidying
author cannam
date Tue, 22 Jan 2008 17:27:07 +0000
parents eea2a08a75a9
children 163f6e03e9e7
comparison
equal deleted inserted replaced
33:499d438b52ba 34:ad645e404d0c
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
1 // GetKeyMode.cpp: implementation of the CGetKeyMode class. 3 // GetKeyMode.cpp: implementation of the CGetKeyMode class.
2 // 4 //
3 ////////////////////////////////////////////////////////////////////// 5 //////////////////////////////////////////////////////////////////////
4 6
5 #include "GetKeyMode.h" 7 #include "GetKeyMode.h"
25 ////////////////////////////////////////////////////////////////////// 27 //////////////////////////////////////////////////////////////////////
26 // Construction/Destruction 28 // Construction/Destruction
27 ////////////////////////////////////////////////////////////////////// 29 //////////////////////////////////////////////////////////////////////
28 30
29 GetKeyMode::GetKeyMode( int sampleRate, float tuningFrequency, 31 GetKeyMode::GetKeyMode( int sampleRate, float tuningFrequency,
30 double hpcpAverage, double medianAverage ) 32 double hpcpAverage, double medianAverage ) :
31 : 33 m_hpcpAverage( hpcpAverage ),
32 m_hpcpAverage( hpcpAverage ), 34 m_medianAverage( medianAverage ),
33 m_medianAverage( medianAverage ), 35 m_ChrPointer(0),
34 m_ChrPointer(0), 36 m_DecimatedBuffer(0),
35 m_DecimatedBuffer(0), 37 m_ChromaBuffer(0),
36 m_ChromaBuffer(0), 38 m_MeanHPCP(0),
37 m_MeanHPCP(0), 39 m_MajCorr(0),
38 m_MajCorr(0), 40 m_MinCorr(0),
39 m_MinCorr(0), 41 m_Keys(0),
40 m_Keys(0), 42 m_MedianFilterBuffer(0),
41 m_MedianFilterBuffer(0), 43 m_SortedBuffer(0)
42 m_SortedBuffer(0) 44 {
43 { 45 m_DecimationFactor = 8;
44 m_DecimationFactor = 8; 46
45 47 // Chromagram configuration parameters
46 //Chromagram configuration parameters 48 m_ChromaConfig.normalise = MathUtilities::NormaliseUnitMax;
47 m_ChromaConfig.isNormalised = 1; 49 m_ChromaConfig.FS = lrint(sampleRate/(double)m_DecimationFactor);
48 m_ChromaConfig.FS = lrint(sampleRate/(double)m_DecimationFactor); 50
49 51 // Set C (= MIDI #12) as our base :
50 // m_ChromaConfig.min = 111.0641; 52 // This implies that key = 1 => Cmaj, key = 12 => Bmaj, key = 13 => Cmin, etc.
51 // m_ChromaConfig.max = 1.7770e+003; 53 m_ChromaConfig.min = Pitch::getFrequencyForPitch
52 54 (12, 0, tuningFrequency);
53 // Set C (= MIDI #12) as our base : 55 m_ChromaConfig.max = Pitch::getFrequencyForPitch
54 // This implies that key = 1 => Cmaj, key = 12 => Bmaj, key = 13 => Cmin, etc. 56 (96, 0, tuningFrequency);
55 m_ChromaConfig.min = Pitch::getFrequencyForPitch 57
56 (12, 0, tuningFrequency); 58 m_ChromaConfig.BPO = 36;
57 m_ChromaConfig.max = Pitch::getFrequencyForPitch 59 m_ChromaConfig.CQThresh = 0.0054;
58 (96, 0, tuningFrequency); 60
59 61 // Chromagram inst.
60 // The chromagram minimum pitch is 1/6 of a tone above A, two 62 m_Chroma = new Chromagram( m_ChromaConfig );
61 // octaves below middle C (for a 36-bin chromagram). The 63
62 // maximum pitch is four octaves higher. 64 // Get calculated parameters from chroma object
63 /* m_ChromaConfig.min = Pitch::getFrequencyForPitch 65 m_ChromaFrameSize = m_Chroma->getFrameSize();
64 (45, 1.f / 3.f, tuningFrequency); 66 // override hopsize for this application
65 67 m_ChromaHopSize = m_ChromaFrameSize;
66 m_ChromaConfig.max = m_ChromaConfig.min * 2; 68 m_BPO = m_ChromaConfig.BPO;
67 m_ChromaConfig.max = m_ChromaConfig.max * 2; 69
68 m_ChromaConfig.max = m_ChromaConfig.max * 2; 70 // Chromagram average and estimated key median filter lengths
69 m_ChromaConfig.max = m_ChromaConfig.max * 2; 71 m_ChromaBuffersize = (int)ceil( m_hpcpAverage * m_ChromaConfig.FS/m_ChromaFrameSize );
72 m_MedianWinsize = (int)ceil( m_medianAverage * m_ChromaConfig.FS/m_ChromaFrameSize );
73
74 // Reset counters
75 m_bufferindex = 0;
76 m_ChromaBufferFilling = 0;
77 m_MedianBufferFilling = 0;
78
79 // Spawn objectc/arrays
80 m_DecimatedBuffer = new double[m_ChromaFrameSize];
81
82 m_ChromaBuffer = new double[m_BPO * m_ChromaBuffersize];
83 memset( m_ChromaBuffer, 0, sizeof(double) * m_BPO * m_ChromaBuffersize);
84
85 m_MeanHPCP = new double[m_BPO];
86
87 m_MajCorr = new double[m_BPO];
88 m_MinCorr = new double[m_BPO];
89 m_Keys = new double[2*m_BPO];
90
91 m_MedianFilterBuffer = new int[ m_MedianWinsize ];
92 memset( m_MedianFilterBuffer, 0, sizeof(int)*m_MedianWinsize);
93
94 m_SortedBuffer = new int[ m_MedianWinsize ];
95 memset( m_SortedBuffer, 0, sizeof(int)*m_MedianWinsize);
96
97 m_Decimator = new Decimator
98 ( m_ChromaFrameSize*m_DecimationFactor, m_DecimationFactor );
99 }
100
101 GetKeyMode::~GetKeyMode()
102 {
103
104 delete m_Chroma;
105 delete m_Decimator;
106
107 delete [] m_DecimatedBuffer;
108 delete [] m_ChromaBuffer;
109 delete [] m_MeanHPCP;
110 delete [] m_MajCorr;
111 delete [] m_MinCorr;
112 delete [] m_Keys;
113 delete [] m_MedianFilterBuffer;
114 delete [] m_SortedBuffer;
115 }
116
117 double GetKeyMode::krumCorr(double *pData1, double *pData2, unsigned int length)
118 {
119 double retVal= 0.0;
120
121 double num = 0;
122 double den = 0;
123 double mX = MathUtilities::mean( pData1, length );
124 double mY = MathUtilities::mean( pData2, length );
125
126 double sum1 = 0;
127 double sum2 = 0;
128
129 for( unsigned int i = 0; i <length; i++ )
130 {
131 num += ( pData1[i] - mX ) * ( pData2[i] - mY );
132
133 sum1 += ( (pData1[i]-mX) * (pData1[i]-mX) );
134 sum2 += ( (pData2[i]-mY) * (pData2[i]-mY) );
135 }
136
137 den = sqrt(sum1 * sum2);
138
139 if( den>0 )
140 retVal = num/den;
141 else
142 retVal = 0;
143
144
145 return retVal;
146 }
147
148 int GetKeyMode::process(double *PCMData)
149 {
150 int key;
151
152 unsigned int j,k;
153
154 //////////////////////////////////////////////
155 m_Decimator->process( PCMData, m_DecimatedBuffer);
156
157 m_ChrPointer = m_Chroma->process( m_DecimatedBuffer );
158
159
160 // Move bins such that the centre of the base note is in the
161 // middle of its three bins :
162 // Added 21.11.07 by Chris Sutton based on debugging with Katy
163 // Noland + comparison with Matlab equivalent.
164 MathUtilities::circShift( m_ChrPointer, m_BPO, 1);
165
166 /*
167 std::cout << "raw chroma: ";
168 for (int ii = 0; ii < m_BPO; ++ii) {
169 std::cout << m_ChrPointer[ii] << " ";
170 }
171 std::cout << std::endl;
70 */ 172 */
71 // std::cerr << "Chromagram range: " << m_ChromaConfig.min << " -> " << m_ChromaConfig.max << std::endl; 173 // populate hpcp values;
72 174 int cbidx;
73 m_ChromaConfig.BPO = 36; 175 for( j = 0; j < m_BPO; j++ )
74 m_ChromaConfig.CQThresh = 0.0054; 176 {
75 177 cbidx = (m_bufferindex * m_BPO) + j;
76 //Chromagram inst. 178 m_ChromaBuffer[ cbidx ] = m_ChrPointer[j];
77 m_Chroma = new Chromagram( m_ChromaConfig ); 179 }
78 180
79 //Get calculated parameters from chroma object 181 //keep track of input buffers;
80 m_ChromaFrameSize = m_Chroma->getFrameSize(); 182 if( m_bufferindex++ >= m_ChromaBuffersize - 1)
81 //override hopsize for this application 183 m_bufferindex = 0;
82 m_ChromaHopSize = m_ChromaFrameSize;//m_Chroma->GetHopSize(); 184
83 m_BPO = m_ChromaConfig.BPO; 185 // track filling of chroma matrix
84 186 if( m_ChromaBufferFilling++ >= m_ChromaBuffersize)
85 //Chromagram average and estimated key median filter lengths 187 m_ChromaBufferFilling = m_ChromaBuffersize;
86 m_ChromaBuffersize = (int)ceil( m_hpcpAverage * m_ChromaConfig.FS/m_ChromaFrameSize ); 188
87 m_MedianWinsize = (int)ceil( m_medianAverage * m_ChromaConfig.FS/m_ChromaFrameSize ); 189 //calculate mean
88 190 for( k = 0; k < m_BPO; k++ )
89 //Reset counters 191 {
90 m_bufferindex = 0; 192 double mnVal = 0.0;
91 m_ChromaBufferFilling = 0; 193 for( j = 0; j < m_ChromaBufferFilling; j++ )
92 m_MedianBufferFilling = 0; 194 {
93 195 mnVal += m_ChromaBuffer[ k + (j*m_BPO) ];
94 196 }
95 //Spawn objectc/arrays 197
96 m_DecimatedBuffer = new double[m_ChromaFrameSize]; 198 m_MeanHPCP[k] = mnVal/(double)m_ChromaBufferFilling;
97 199 }
98 m_ChromaBuffer = new double[m_BPO * m_ChromaBuffersize]; 200
99 memset( m_ChromaBuffer, 0, sizeof(double) * m_BPO * m_ChromaBuffersize); 201
100 202 for( k = 0; k < m_BPO; k++ )
101 m_MeanHPCP = new double[m_BPO]; 203 {
102 204 m_MajCorr[k] = krumCorr( m_MeanHPCP, MajProfile, m_BPO );
103 m_MajCorr = new double[m_BPO]; 205 m_MinCorr[k] = krumCorr( m_MeanHPCP, MinProfile, m_BPO );
104 m_MinCorr = new double[m_BPO]; 206
105 m_Keys = new double[2*m_BPO]; 207 MathUtilities::circShift( MajProfile, m_BPO, 1 );
106 208 MathUtilities::circShift( MinProfile, m_BPO, 1 );
107 m_MedianFilterBuffer = new int[ m_MedianWinsize ]; 209 }
108 memset( m_MedianFilterBuffer, 0, sizeof(int)*m_MedianWinsize); 210
109 211 for( k = 0; k < m_BPO; k++ )
110 m_SortedBuffer = new int[ m_MedianWinsize ]; 212 {
111 memset( m_SortedBuffer, 0, sizeof(int)*m_MedianWinsize); 213 m_Keys[k] = m_MajCorr[k];
112 214 m_Keys[k+m_BPO] = m_MinCorr[k];
113 m_Decimator = new Decimator( m_ChromaFrameSize*m_DecimationFactor, m_DecimationFactor ); 215 }
114 } 216
115 217
116 GetKeyMode::~GetKeyMode() 218 /*
117 { 219 std::cout << "raw keys: ";
118 220 for (int ii = 0; ii < 2*m_BPO; ++ii) {
119 delete m_Chroma; 221 std::cout << m_Keys[ii] << " ";
120 delete m_Decimator; 222 }
121 223 std::cout << std::endl;
122 delete [] m_DecimatedBuffer; 224 */
123 delete [] m_ChromaBuffer; 225 double dummy;
124 delete [] m_MeanHPCP; 226 // '1 +' because we number keys 1-24, not 0-23.
125 delete [] m_MajCorr; 227 key = 1 + (int)ceil( (double)MathUtilities::getMax( m_Keys, 2* m_BPO, &dummy )/3 );
126 delete [] m_MinCorr; 228
127 delete [] m_Keys; 229 // std::cout << "key pre-sorting: " << key << std::endl;
128 delete [] m_MedianFilterBuffer; 230
129 delete [] m_SortedBuffer; 231
130 } 232 //Median filtering
131 233
132 double GetKeyMode::krumCorr(double *pData1, double *pData2, unsigned int length) 234 // track Median buffer initial filling
133 { 235 if( m_MedianBufferFilling++ >= m_MedianWinsize)
134 double retVal= 0.0; 236 m_MedianBufferFilling = m_MedianWinsize;
135 237
136 double num = 0; 238 //shift median buffer
137 double den = 0; 239 for( k = 1; k < m_MedianWinsize; k++ )
138 double mX = MathUtilities::mean( pData1, length ); 240 {
139 double mY = MathUtilities::mean( pData2, length ); 241 m_MedianFilterBuffer[ k - 1 ] = m_MedianFilterBuffer[ k ];
140 242 }
141 double sum1 = 0; 243
142 double sum2 = 0; 244 //write new key value into median buffer
143 245 m_MedianFilterBuffer[ m_MedianWinsize - 1 ] = key;
144 for( unsigned int i = 0; i <length; i++ ) 246
145 { 247
146 num += ( pData1[i] - mX ) * ( pData2[i] - mY ); 248 //Copy median into sorting buffer, reversed
147 249 unsigned int ijx = 0;
148 sum1 += ( (pData1[i]-mX) * (pData1[i]-mX) ); 250 for( k = 0; k < m_MedianWinsize; k++ )
149 sum2 += ( (pData2[i]-mY) * (pData2[i]-mY) ); 251 {
150 } 252 m_SortedBuffer[k] = m_MedianFilterBuffer[m_MedianWinsize-1-ijx];
151 253 ijx++;
152 den = sqrt(sum1 * sum2); 254 }
153 255
154 if( den>0 ) 256 qsort(m_SortedBuffer, m_MedianBufferFilling, sizeof(unsigned int),
155 retVal = num/den; 257 MathUtilities::compareInt);
156 else
157 retVal = 0;
158
159
160 return retVal;
161 }
162
163 int GetKeyMode::process(double *PCMData)
164 {
165 int key;
166
167 unsigned int j,k;
168
169 //////////////////////////////////////////////
170 m_Decimator->process( PCMData, m_DecimatedBuffer);
171
172 m_ChrPointer = m_Chroma->process( m_DecimatedBuffer );
173
174
175 // Move bins such that the centre of the base note is in the middle of its three bins :
176 // Added 21.11.07 by Chris Sutton based on debugging with Katy Noland + comparison with Matlab equivalent.
177 MathUtilities::circShift( m_ChrPointer, m_BPO, 1);
178
179 /* 258 /*
180 std::cout << "raw chroma: "; 259 std::cout << "sorted: ";
181 for (int ii = 0; ii < m_BPO; ++ii) { 260 for (int ii = 0; ii < m_MedianBufferFilling; ++ii) {
182 std::cout << m_ChrPointer[ii] << " "; 261 std::cout << m_SortedBuffer[ii] << " ";
183 } 262 }
184 std::cout << std::endl; 263 std::cout << std::endl;
185 */ 264 */
186 // populate hpcp values; 265 int sortlength = m_MedianBufferFilling;
187 int cbidx; 266 int midpoint = (int)ceil((double)sortlength/2);
188 for( j = 0; j < m_BPO; j++ ) 267
189 { 268 if( midpoint <= 0 )
190 cbidx = (m_bufferindex * m_BPO) + j; 269 midpoint = 1;
191 m_ChromaBuffer[ cbidx ] = m_ChrPointer[j]; 270
192 } 271 key = m_SortedBuffer[midpoint-1];
193 272
194 //keep track of input buffers; 273 return key;
195 if( m_bufferindex++ >= m_ChromaBuffersize - 1)
196 m_bufferindex = 0;
197
198 // track filling of chroma matrix
199 if( m_ChromaBufferFilling++ >= m_ChromaBuffersize)
200 m_ChromaBufferFilling = m_ChromaBuffersize;
201
202 //calculate mean
203 for( k = 0; k < m_BPO; k++ )
204 {
205 double mnVal = 0.0;
206 for( j = 0; j < m_ChromaBufferFilling; j++ )
207 {
208 mnVal += m_ChromaBuffer[ k + (j*m_BPO) ];
209 }
210
211 m_MeanHPCP[k] = mnVal/(double)m_ChromaBufferFilling;
212 }
213
214
215 for( k = 0; k < m_BPO; k++ )
216 {
217 m_MajCorr[k] = krumCorr( m_MeanHPCP, MajProfile, m_BPO );
218 m_MinCorr[k] = krumCorr( m_MeanHPCP, MinProfile, m_BPO );
219
220 MathUtilities::circShift( MajProfile, m_BPO, 1 );
221 MathUtilities::circShift( MinProfile, m_BPO, 1 );
222 }
223
224 for( k = 0; k < m_BPO; k++ )
225 {
226 m_Keys[k] = m_MajCorr[k];
227 m_Keys[k+m_BPO] = m_MinCorr[k];
228 }
229
230
231 /*
232 std::cout << "raw keys: ";
233 for (int ii = 0; ii < 2*m_BPO; ++ii) {
234 std::cout << m_Keys[ii] << " ";
235 }
236 std::cout << std::endl;
237 */
238 double dummy;
239 // '1 +' because we number keys 1-24, not 0-23.
240 key = 1 + (int)ceil( (double)MathUtilities::getMax( m_Keys, 2* m_BPO, &dummy )/3 );
241
242 // std::cout << "key pre-sorting: " << key << std::endl;
243
244
245 //Median filtering
246
247 // track Median buffer initial filling
248 if( m_MedianBufferFilling++ >= m_MedianWinsize)
249 m_MedianBufferFilling = m_MedianWinsize;
250
251 //shift median buffer
252 for( k = 1; k < m_MedianWinsize; k++ )
253 {
254 m_MedianFilterBuffer[ k - 1 ] = m_MedianFilterBuffer[ k ];
255 }
256
257 //write new key value into median buffer
258 m_MedianFilterBuffer[ m_MedianWinsize - 1 ] = key;
259
260
261 //Copy median into sorting buffer, reversed
262 unsigned int ijx = 0;
263 for( k = 0; k < m_MedianWinsize; k++ )
264 {
265 m_SortedBuffer[k] = m_MedianFilterBuffer[m_MedianWinsize-1-ijx];
266 ijx++;
267 }
268
269
270 //quicksort
271 qsort(m_SortedBuffer, m_MedianBufferFilling, sizeof(unsigned int), MathUtilities::compareInt);
272 /*
273 std::cout << "sorted: ";
274 for (int ii = 0; ii < m_MedianBufferFilling; ++ii) {
275 std::cout << m_SortedBuffer[ii] << " ";
276 }
277 std::cout << std::endl;
278 */
279 int sortlength = m_MedianBufferFilling;
280 int midpoint = (int)ceil((double)sortlength/2);
281
282 if( midpoint <= 0 )
283 midpoint = 1;
284
285 key = m_SortedBuffer[midpoint-1];
286
287 return key;
288 } 274 }
289 275
290 276
291 int GetKeyMode::isModeMinor( int key ) 277 int GetKeyMode::isModeMinor( int key )
292 { 278 {
293 // return ((key-1 - (int)MathUtilities::mod((double)(key-1),(double)12))/12); 279 return (key > 12);
294 return (key > 12); 280 }
295 }