annotate dsp/keydetection/GetKeyMode.cpp @ 73:dcb555b90924

* Key detector: when returning key strengths, use the peak value of the three underlying chromagram correlations (from 36-bin chromagram) corresponding to each key, instead of the mean. Rationale: This is the same method as used when returning the key value, and it's nice to have the same results in both returned value and plot. The peak performed better than the sum with a simple test set of triads, so it seems reasonable to change the plot to match the key output rather than the other way around. * FFT: kiss_fftr returns only the non-conjugate bins, synthesise the rest rather than leaving them (perhaps dangerously) undefined. Fixes an uninitialised data error in chromagram that could cause garbage results from key detector. * Constant Q: remove precalculated values again, I reckon they're not proving such a good tradeoff.
author cannam
date Fri, 05 Jun 2009 15:12:39 +0000
parents d72fcd34d9a7
children 715f779d0b4f
rev   line source
cannam@34 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
cannam@34 2
cannam@7 3 // GetKeyMode.cpp: implementation of the CGetKeyMode class.
cannam@7 4 //
cannam@7 5 //////////////////////////////////////////////////////////////////////
cannam@7 6
cannam@7 7 #include "GetKeyMode.h"
cannam@16 8 #include "maths/MathUtilities.h"
cannam@9 9 #include "base/Pitch.h"
cannam@9 10
cannam@9 11 #include <iostream>
cannam@7 12
cannam@47 13 #include <cstring>
cannam@47 14 #include <cstdlib>
cannam@47 15
cannam@7 16 // Chords profile
cannam@7 17 static double MajProfile[36] =
cannam@7 18 { 0.0384, 0.0629, 0.0258, 0.0121, 0.0146, 0.0106, 0.0364, 0.0610, 0.0267,
chriss@15 19 0.0126, 0.0121, 0.0086, 0.0364, 0.0623, 0.0279, 0.0275, 0.0414, 0.0186,
cannam@7 20 0.0173, 0.0248, 0.0145, 0.0364, 0.0631, 0.0262, 0.0129, 0.0150, 0.0098,
cannam@7 21 0.0312, 0.0521, 0.0235, 0.0129, 0.0142, 0.0095, 0.0289, 0.0478, 0.0239};
cannam@7 22
cannam@7 23 static double MinProfile[36] =
cannam@7 24 { 0.0375, 0.0682, 0.0299, 0.0119, 0.0138, 0.0093, 0.0296, 0.0543, 0.0257,
cannam@7 25 0.0292, 0.0519, 0.0246, 0.0159, 0.0234, 0.0135, 0.0291, 0.0544, 0.0248,
cannam@7 26 0.0137, 0.0176, 0.0104, 0.0352, 0.0670, 0.0302, 0.0222, 0.0349, 0.0164,
cannam@7 27 0.0174, 0.0297, 0.0166, 0.0222, 0.0401, 0.0202, 0.0175, 0.0270, 0.0146};
cannam@7 28 //
cannam@40 29
cannam@7 30
cannam@7 31 //////////////////////////////////////////////////////////////////////
cannam@7 32 // Construction/Destruction
cannam@7 33 //////////////////////////////////////////////////////////////////////
cannam@7 34
cannam@9 35 GetKeyMode::GetKeyMode( int sampleRate, float tuningFrequency,
cannam@34 36 double hpcpAverage, double medianAverage ) :
cannam@34 37 m_hpcpAverage( hpcpAverage ),
cannam@34 38 m_medianAverage( medianAverage ),
cannam@34 39 m_ChrPointer(0),
cannam@34 40 m_DecimatedBuffer(0),
cannam@34 41 m_ChromaBuffer(0),
cannam@34 42 m_MeanHPCP(0),
cannam@34 43 m_MajCorr(0),
cannam@34 44 m_MinCorr(0),
cannam@34 45 m_Keys(0),
cannam@34 46 m_MedianFilterBuffer(0),
cannam@40 47 m_SortedBuffer(0),
cannam@40 48 m_keyStrengths(0)
cannam@7 49 {
cannam@34 50 m_DecimationFactor = 8;
cannam@34 51
cannam@34 52 // Chromagram configuration parameters
cannam@34 53 m_ChromaConfig.normalise = MathUtilities::NormaliseUnitMax;
cannam@34 54 m_ChromaConfig.FS = lrint(sampleRate/(double)m_DecimationFactor);
cannam@58 55 if (m_ChromaConfig.FS < 1) m_ChromaConfig.FS = 1;
cannam@7 56
cannam@34 57 // Set C (= MIDI #12) as our base :
cannam@34 58 // This implies that key = 1 => Cmaj, key = 12 => Bmaj, key = 13 => Cmin, etc.
cannam@34 59 m_ChromaConfig.min = Pitch::getFrequencyForPitch
cannam@40 60 (48, 0, tuningFrequency);
cannam@34 61 m_ChromaConfig.max = Pitch::getFrequencyForPitch
cannam@34 62 (96, 0, tuningFrequency);
cannam@9 63
cannam@34 64 m_ChromaConfig.BPO = 36;
cannam@34 65 m_ChromaConfig.CQThresh = 0.0054;
cannam@9 66
cannam@34 67 // Chromagram inst.
cannam@34 68 m_Chroma = new Chromagram( m_ChromaConfig );
cannam@10 69
cannam@34 70 // Get calculated parameters from chroma object
cannam@34 71 m_ChromaFrameSize = m_Chroma->getFrameSize();
cannam@34 72 // override hopsize for this application
cannam@34 73 m_ChromaHopSize = m_ChromaFrameSize;
cannam@34 74 m_BPO = m_ChromaConfig.BPO;
cannam@10 75
cannam@40 76 // std::cerr << "chroma frame size = " << m_ChromaFrameSize << ", decimation factor = " << m_DecimationFactor << " therefore block size = " << getBlockSize() << std::endl;
cannam@40 77
cannam@34 78 // Chromagram average and estimated key median filter lengths
cannam@34 79 m_ChromaBuffersize = (int)ceil( m_hpcpAverage * m_ChromaConfig.FS/m_ChromaFrameSize );
cannam@34 80 m_MedianWinsize = (int)ceil( m_medianAverage * m_ChromaConfig.FS/m_ChromaFrameSize );
cannam@34 81
cannam@34 82 // Reset counters
cannam@34 83 m_bufferindex = 0;
cannam@34 84 m_ChromaBufferFilling = 0;
cannam@34 85 m_MedianBufferFilling = 0;
cannam@9 86
cannam@34 87 // Spawn objectc/arrays
cannam@34 88 m_DecimatedBuffer = new double[m_ChromaFrameSize];
cannam@34 89
cannam@34 90 m_ChromaBuffer = new double[m_BPO * m_ChromaBuffersize];
cannam@34 91 memset( m_ChromaBuffer, 0, sizeof(double) * m_BPO * m_ChromaBuffersize);
cannam@34 92
cannam@34 93 m_MeanHPCP = new double[m_BPO];
cannam@34 94
cannam@34 95 m_MajCorr = new double[m_BPO];
cannam@34 96 m_MinCorr = new double[m_BPO];
cannam@34 97 m_Keys = new double[2*m_BPO];
cannam@34 98
cannam@34 99 m_MedianFilterBuffer = new int[ m_MedianWinsize ];
cannam@34 100 memset( m_MedianFilterBuffer, 0, sizeof(int)*m_MedianWinsize);
cannam@34 101
cannam@34 102 m_SortedBuffer = new int[ m_MedianWinsize ];
cannam@34 103 memset( m_SortedBuffer, 0, sizeof(int)*m_MedianWinsize);
cannam@34 104
cannam@34 105 m_Decimator = new Decimator
cannam@34 106 ( m_ChromaFrameSize*m_DecimationFactor, m_DecimationFactor );
cannam@40 107
cannam@40 108 m_keyStrengths = new double[24];
cannam@7 109 }
cannam@7 110
cannam@7 111 GetKeyMode::~GetKeyMode()
cannam@7 112 {
cannam@7 113
cannam@34 114 delete m_Chroma;
cannam@34 115 delete m_Decimator;
cannam@34 116
cannam@34 117 delete [] m_DecimatedBuffer;
cannam@34 118 delete [] m_ChromaBuffer;
cannam@34 119 delete [] m_MeanHPCP;
cannam@34 120 delete [] m_MajCorr;
cannam@34 121 delete [] m_MinCorr;
cannam@34 122 delete [] m_Keys;
cannam@34 123 delete [] m_MedianFilterBuffer;
cannam@34 124 delete [] m_SortedBuffer;
cannam@40 125
cannam@40 126 delete[] m_keyStrengths;
cannam@7 127 }
cannam@7 128
cannam@7 129 double GetKeyMode::krumCorr(double *pData1, double *pData2, unsigned int length)
cannam@7 130 {
cannam@34 131 double retVal= 0.0;
cannam@34 132
cannam@34 133 double num = 0;
cannam@34 134 double den = 0;
cannam@34 135 double mX = MathUtilities::mean( pData1, length );
cannam@34 136 double mY = MathUtilities::mean( pData2, length );
cannam@34 137
cannam@34 138 double sum1 = 0;
cannam@34 139 double sum2 = 0;
cannam@34 140
cannam@34 141 for( unsigned int i = 0; i <length; i++ )
cannam@34 142 {
cannam@34 143 num += ( pData1[i] - mX ) * ( pData2[i] - mY );
cannam@7 144
cannam@34 145 sum1 += ( (pData1[i]-mX) * (pData1[i]-mX) );
cannam@34 146 sum2 += ( (pData2[i]-mY) * (pData2[i]-mY) );
cannam@34 147 }
cannam@34 148
cannam@34 149 den = sqrt(sum1 * sum2);
cannam@34 150
cannam@34 151 if( den>0 )
cannam@34 152 retVal = num/den;
cannam@34 153 else
cannam@34 154 retVal = 0;
cannam@7 155
cannam@7 156
cannam@34 157 return retVal;
cannam@7 158 }
cannam@7 159
cannam@7 160 int GetKeyMode::process(double *PCMData)
cannam@7 161 {
cannam@34 162 int key;
cannam@7 163
cannam@34 164 unsigned int j,k;
cannam@7 165
cannam@34 166 //////////////////////////////////////////////
cannam@34 167 m_Decimator->process( PCMData, m_DecimatedBuffer);
cannam@7 168
cannam@34 169 m_ChrPointer = m_Chroma->process( m_DecimatedBuffer );
cannam@7 170
chriss@15 171
cannam@34 172 // Move bins such that the centre of the base note is in the
cannam@34 173 // middle of its three bins :
cannam@34 174 // Added 21.11.07 by Chris Sutton based on debugging with Katy
cannam@34 175 // Noland + comparison with Matlab equivalent.
cannam@34 176 MathUtilities::circShift( m_ChrPointer, m_BPO, 1);
cannam@23 177 /*
cannam@73 178 std::cout << "raw chroma: ";
cannam@73 179 for (int ii = 0; ii < m_BPO; ++ii) {
cannam@73 180 if (ii % (m_BPO/12) == 0) std::cout << "\n";
cannam@73 181 std::cout << m_ChrPointer[ii] << " ";
cannam@73 182 }
cannam@73 183 std::cout << std::endl;
cannam@23 184 */
cannam@34 185 // populate hpcp values;
cannam@34 186 int cbidx;
cannam@34 187 for( j = 0; j < m_BPO; j++ )
cannam@34 188 {
cannam@34 189 cbidx = (m_bufferindex * m_BPO) + j;
cannam@34 190 m_ChromaBuffer[ cbidx ] = m_ChrPointer[j];
cannam@34 191 }
cannam@7 192
cannam@34 193 //keep track of input buffers;
cannam@34 194 if( m_bufferindex++ >= m_ChromaBuffersize - 1)
cannam@34 195 m_bufferindex = 0;
cannam@7 196
cannam@34 197 // track filling of chroma matrix
cannam@34 198 if( m_ChromaBufferFilling++ >= m_ChromaBuffersize)
cannam@34 199 m_ChromaBufferFilling = m_ChromaBuffersize;
cannam@7 200
cannam@34 201 //calculate mean
cannam@34 202 for( k = 0; k < m_BPO; k++ )
cannam@34 203 {
cannam@34 204 double mnVal = 0.0;
cannam@34 205 for( j = 0; j < m_ChromaBufferFilling; j++ )
cannam@34 206 {
cannam@34 207 mnVal += m_ChromaBuffer[ k + (j*m_BPO) ];
cannam@34 208 }
cannam@7 209
cannam@34 210 m_MeanHPCP[k] = mnVal/(double)m_ChromaBufferFilling;
cannam@34 211 }
cannam@7 212
cannam@7 213
cannam@34 214 for( k = 0; k < m_BPO; k++ )
cannam@34 215 {
cannam@34 216 m_MajCorr[k] = krumCorr( m_MeanHPCP, MajProfile, m_BPO );
cannam@34 217 m_MinCorr[k] = krumCorr( m_MeanHPCP, MinProfile, m_BPO );
cannam@7 218
cannam@34 219 MathUtilities::circShift( MajProfile, m_BPO, 1 );
cannam@34 220 MathUtilities::circShift( MinProfile, m_BPO, 1 );
cannam@34 221 }
chriss@15 222
cannam@34 223 for( k = 0; k < m_BPO; k++ )
cannam@34 224 {
cannam@34 225 m_Keys[k] = m_MajCorr[k];
cannam@34 226 m_Keys[k+m_BPO] = m_MinCorr[k];
cannam@34 227 }
chriss@15 228
cannam@40 229 for (k = 0; k < 24; ++k) {
cannam@40 230 m_keyStrengths[k] = 0;
cannam@40 231 }
cannam@40 232
cannam@40 233 for( k = 0; k < m_BPO*2; k++ )
cannam@40 234 {
cannam@73 235 int idx = k / (m_BPO/12);
cannam@73 236 int rem = k % (m_BPO/12);
cannam@73 237 if (rem == 0 || m_Keys[k] > m_keyStrengths[idx]) {
cannam@73 238 m_keyStrengths[idx] = m_Keys[k];
cannam@73 239 }
cannam@73 240
cannam@73 241 // m_keyStrengths[k/(m_BPO/12)] += m_Keys[k];
cannam@40 242 }
chriss@15 243
cannam@23 244 /*
cannam@34 245 std::cout << "raw keys: ";
cannam@34 246 for (int ii = 0; ii < 2*m_BPO; ++ii) {
cannam@73 247 if (ii % (m_BPO/12) == 0) std::cout << "\n";
cannam@73 248 std::cout << m_Keys[ii] << " ";
cannam@73 249 }
cannam@73 250 std::cout << std::endl;
cannam@73 251
cannam@73 252 std::cout << "key strengths: ";
cannam@73 253 for (int ii = 0; ii < 24; ++ii) {
cannam@73 254 if (ii % 6 == 0) std::cout << "\n";
cannam@73 255 std::cout << m_keyStrengths[ii] << " ";
cannam@34 256 }
cannam@34 257 std::cout << std::endl;
cannam@23 258 */
cannam@34 259 double dummy;
cannam@34 260 // '1 +' because we number keys 1-24, not 0-23.
cannam@34 261 key = 1 + (int)ceil( (double)MathUtilities::getMax( m_Keys, 2* m_BPO, &dummy )/3 );
cannam@9 262
cannam@73 263 // std::cout << "key pre-sorting: " << key << std::endl;
cannam@7 264
cannam@7 265
cannam@34 266 //Median filtering
cannam@7 267
cannam@34 268 // track Median buffer initial filling
cannam@34 269 if( m_MedianBufferFilling++ >= m_MedianWinsize)
cannam@34 270 m_MedianBufferFilling = m_MedianWinsize;
cannam@7 271
cannam@34 272 //shift median buffer
cannam@34 273 for( k = 1; k < m_MedianWinsize; k++ )
cannam@34 274 {
cannam@34 275 m_MedianFilterBuffer[ k - 1 ] = m_MedianFilterBuffer[ k ];
cannam@34 276 }
cannam@7 277
cannam@34 278 //write new key value into median buffer
cannam@34 279 m_MedianFilterBuffer[ m_MedianWinsize - 1 ] = key;
cannam@7 280
cannam@7 281
cannam@34 282 //Copy median into sorting buffer, reversed
cannam@34 283 unsigned int ijx = 0;
cannam@34 284 for( k = 0; k < m_MedianWinsize; k++ )
cannam@34 285 {
cannam@34 286 m_SortedBuffer[k] = m_MedianFilterBuffer[m_MedianWinsize-1-ijx];
cannam@34 287 ijx++;
cannam@34 288 }
cannam@7 289
cannam@34 290 qsort(m_SortedBuffer, m_MedianBufferFilling, sizeof(unsigned int),
cannam@34 291 MathUtilities::compareInt);
cannam@34 292 /*
cannam@34 293 std::cout << "sorted: ";
cannam@34 294 for (int ii = 0; ii < m_MedianBufferFilling; ++ii) {
cannam@34 295 std::cout << m_SortedBuffer[ii] << " ";
cannam@34 296 }
cannam@34 297 std::cout << std::endl;
cannam@34 298 */
cannam@34 299 int sortlength = m_MedianBufferFilling;
cannam@34 300 int midpoint = (int)ceil((double)sortlength/2);
cannam@7 301
cannam@73 302 // std::cout << "midpoint = " << midpoint << endl;
cannam@73 303
cannam@34 304 if( midpoint <= 0 )
cannam@34 305 midpoint = 1;
cannam@7 306
cannam@34 307 key = m_SortedBuffer[midpoint-1];
cannam@7 308
cannam@73 309 // std::cout << "returning key = " << key << endl;
cannam@73 310
cannam@34 311 return key;
cannam@7 312 }
cannam@7 313
cannam@7 314
cannam@43 315 bool GetKeyMode::isModeMinor( int key )
cannam@7 316 {
cannam@34 317 return (key > 12);
cannam@7 318 }