Mercurial > hg > qm-dsp
comparison dsp/keydetection/GetKeyMode.cpp @ 259:c96785becf96
* Add range of normalise options to chromagram
* bit of tidying
author | Chris Cannam <c.cannam@qmul.ac.uk> |
---|---|
date | Tue, 22 Jan 2008 17:27:07 +0000 |
parents | fd68f25b9949 |
children | 163f6e03e9e7 |
comparison
equal
deleted
inserted
replaced
258:f49be56d3c4e | 259:c96785becf96 |
---|---|
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 } |