annotate dsp/chromagram/ConstantQ.cpp @ 308:25af9a1e4ec3

* Remove some unused code; minor tidy
author Chris Cannam <c.cannam@qmul.ac.uk>
date Wed, 01 Dec 2010 14:05:25 +0000
parents 255e431ae3d4
children e5907ae6de17
rev   line source
c@225 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
c@225 2 /*
c@225 3 QM DSP Library
c@225 4
c@225 5 Centre for Digital Music, Queen Mary, University of London.
c@225 6 This file copyright 2005-2006 Christian Landone.
c@225 7 All rights reserved.
c@225 8 */
c@225 9
c@225 10 #include "ConstantQ.h"
c@225 11 #include "dsp/transforms/FFT.h"
c@225 12
c@245 13 #include <iostream>
c@245 14
c@298 15 #ifdef NOT_DEFINED
c@298 16 // see note in CQprecalc
c@298 17
c@276 18 #include "CQprecalc.cpp"
c@276 19
c@276 20 static bool push_precalculated(int uk, int fftlength,
c@276 21 std::vector<unsigned> &is,
c@276 22 std::vector<unsigned> &js,
c@276 23 std::vector<double> &real,
c@276 24 std::vector<double> &imag)
c@276 25 {
c@276 26 if (uk == 76 && fftlength == 16384) {
c@276 27 push_76_16384(is, js, real, imag);
c@276 28 return true;
c@276 29 }
c@276 30 if (uk == 144 && fftlength == 4096) {
c@276 31 push_144_4096(is, js, real, imag);
c@276 32 return true;
c@276 33 }
c@276 34 if (uk == 65 && fftlength == 2048) {
c@276 35 push_65_2048(is, js, real, imag);
c@276 36 return true;
c@276 37 }
c@276 38 if (uk == 84 && fftlength == 65536) {
c@276 39 push_84_65536(is, js, real, imag);
c@276 40 return true;
c@276 41 }
c@276 42 return false;
c@276 43 }
c@298 44 #endif
c@276 45
c@225 46 //---------------------------------------------------------------------------
c@225 47 // nextpow2 returns the smallest integer n such that 2^n >= x.
c@225 48 static double nextpow2(double x) {
c@225 49 double y = ceil(log(x)/log(2.0));
c@225 50 return(y);
c@225 51 }
c@225 52
c@225 53 static double squaredModule(const double & xx, const double & yy) {
c@225 54 return xx*xx + yy*yy;
c@225 55 }
c@225 56
c@225 57 //----------------------------------------------------------------------------
c@225 58
c@276 59 ConstantQ::ConstantQ( CQConfig Config ) :
c@276 60 m_sparseKernel(0)
c@225 61 {
c@225 62 initialise( Config );
c@225 63 }
c@225 64
c@225 65 ConstantQ::~ConstantQ()
c@225 66 {
c@225 67 deInitialise();
c@225 68 }
c@225 69
c@225 70 //----------------------------------------------------------------------------
c@225 71 void ConstantQ::sparsekernel()
c@225 72 {
c@276 73 // std::cerr << "ConstantQ: initialising sparse kernel, uK = " << m_uK << ", FFTLength = " << m_FFTLength << "...";
c@276 74
c@276 75 SparseKernel *sk = new SparseKernel();
c@276 76
c@298 77 #ifdef NOT_DEFINED
c@276 78 if (push_precalculated(m_uK, m_FFTLength,
c@276 79 sk->is, sk->js, sk->real, sk->imag)) {
c@298 80 // std::cerr << "using precalculated kernel" << std::endl;
c@276 81 m_sparseKernel = sk;
c@276 82 return;
c@276 83 }
c@298 84 #endif
c@298 85
c@225 86 //generates spectral kernel matrix (upside down?)
c@225 87 // initialise temporal kernel with zeros, twice length to deal w. complex numbers
c@225 88
c@225 89 double* hammingWindowRe = new double [ m_FFTLength ];
c@225 90 double* hammingWindowIm = new double [ m_FFTLength ];
c@225 91 double* transfHammingWindowRe = new double [ m_FFTLength ];
c@225 92 double* transfHammingWindowIm = new double [ m_FFTLength ];
c@225 93
c@225 94 for (unsigned u=0; u < m_FFTLength; u++)
c@225 95 {
c@225 96 hammingWindowRe[u] = 0;
c@225 97 hammingWindowIm[u] = 0;
c@225 98 }
c@225 99
c@225 100 // Here, fftleng*2 is a guess of the number of sparse cells in the matrix
c@225 101 // The matrix K x fftlength but the non-zero cells are an antialiased
c@225 102 // square root function. So mostly is a line, with some grey point.
c@276 103 sk->is.reserve( m_FFTLength*2 );
c@276 104 sk->js.reserve( m_FFTLength*2 );
c@276 105 sk->real.reserve( m_FFTLength*2 );
c@276 106 sk->imag.reserve( m_FFTLength*2 );
c@225 107
c@225 108 // for each bin value K, calculate temporal kernel, take its fft to
c@225 109 //calculate the spectral kernel then threshold it to make it sparse and
c@225 110 //add it to the sparse kernels matrix
c@225 111 double squareThreshold = m_CQThresh * m_CQThresh;
c@225 112
c@289 113 FFT m_FFT(m_FFTLength);
c@225 114
c@225 115 for (unsigned k = m_uK; k--; )
c@225 116 {
c@228 117 for (unsigned u=0; u < m_FFTLength; u++)
c@228 118 {
c@228 119 hammingWindowRe[u] = 0;
c@228 120 hammingWindowIm[u] = 0;
c@228 121 }
c@228 122
c@225 123 // Computing a hamming window
c@225 124 const unsigned hammingLength = (int) ceil( m_dQ * m_FS / ( m_FMin * pow(2,((double)(k))/(double)m_BPO)));
c@228 125
c@228 126 unsigned origin = m_FFTLength/2 - hammingLength/2;
c@228 127
c@225 128 for (unsigned i=0; i<hammingLength; i++)
c@225 129 {
c@225 130 const double angle = 2*PI*m_dQ*i/hammingLength;
c@225 131 const double real = cos(angle);
c@225 132 const double imag = sin(angle);
c@225 133 const double absol = hamming(hammingLength, i)/hammingLength;
c@228 134 hammingWindowRe[ origin + i ] = absol*real;
c@228 135 hammingWindowIm[ origin + i ] = absol*imag;
c@225 136 }
c@225 137
c@228 138 for (unsigned i = 0; i < m_FFTLength/2; ++i) {
c@228 139 double temp = hammingWindowRe[i];
c@228 140 hammingWindowRe[i] = hammingWindowRe[i + m_FFTLength/2];
c@228 141 hammingWindowRe[i + m_FFTLength/2] = temp;
c@228 142 temp = hammingWindowIm[i];
c@228 143 hammingWindowIm[i] = hammingWindowIm[i + m_FFTLength/2];
c@228 144 hammingWindowIm[i + m_FFTLength/2] = temp;
c@228 145 }
c@228 146
c@225 147 //do fft of hammingWindow
c@289 148 m_FFT.process( 0, hammingWindowRe, hammingWindowIm, transfHammingWindowRe, transfHammingWindowIm );
c@225 149
c@225 150
c@225 151 for (unsigned j=0; j<( m_FFTLength ); j++)
c@225 152 {
c@225 153 // perform thresholding
c@225 154 const double squaredBin = squaredModule( transfHammingWindowRe[ j ], transfHammingWindowIm[ j ]);
c@225 155 if (squaredBin <= squareThreshold) continue;
c@225 156
c@225 157 // Insert non-zero position indexes, doubled because they are floats
c@276 158 sk->is.push_back(j);
c@276 159 sk->js.push_back(k);
c@225 160
c@225 161 // take conjugate, normalise and add to array sparkernel
c@276 162 sk->real.push_back( transfHammingWindowRe[ j ]/m_FFTLength);
c@276 163 sk->imag.push_back(-transfHammingWindowIm[ j ]/m_FFTLength);
c@225 164 }
c@225 165
c@225 166 }
c@225 167
c@225 168 delete [] hammingWindowRe;
c@225 169 delete [] hammingWindowIm;
c@225 170 delete [] transfHammingWindowRe;
c@225 171 delete [] transfHammingWindowIm;
c@225 172
c@276 173 /*
c@276 174 using std::cout;
c@276 175 using std::endl;
c@276 176
c@276 177 cout.precision(28);
c@276 178
c@276 179 int n = sk->is.size();
c@276 180 int w = 8;
c@276 181 cout << "static unsigned int sk_i_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl;
c@276 182 for (int i = 0; i < n; ++i) {
c@276 183 if (i % w == 0) cout << " ";
c@276 184 cout << sk->is[i];
c@276 185 if (i + 1 < n) cout << ", ";
c@276 186 if (i % w == w-1) cout << endl;
c@276 187 };
c@276 188 if (n % w != 0) cout << endl;
c@276 189 cout << "};" << endl;
c@276 190
c@276 191 n = sk->js.size();
c@276 192 cout << "static unsigned int sk_j_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl;
c@276 193 for (int i = 0; i < n; ++i) {
c@276 194 if (i % w == 0) cout << " ";
c@276 195 cout << sk->js[i];
c@276 196 if (i + 1 < n) cout << ", ";
c@276 197 if (i % w == w-1) cout << endl;
c@276 198 };
c@276 199 if (n % w != 0) cout << endl;
c@276 200 cout << "};" << endl;
c@276 201
c@276 202 w = 2;
c@276 203 n = sk->real.size();
c@276 204 cout << "static double sk_real_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl;
c@276 205 for (int i = 0; i < n; ++i) {
c@276 206 if (i % w == 0) cout << " ";
c@276 207 cout << sk->real[i];
c@276 208 if (i + 1 < n) cout << ", ";
c@276 209 if (i % w == w-1) cout << endl;
c@276 210 };
c@276 211 if (n % w != 0) cout << endl;
c@276 212 cout << "};" << endl;
c@276 213
c@276 214 n = sk->imag.size();
c@276 215 cout << "static double sk_imag_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl;
c@276 216 for (int i = 0; i < n; ++i) {
c@276 217 if (i % w == 0) cout << " ";
c@276 218 cout << sk->imag[i];
c@276 219 if (i + 1 < n) cout << ", ";
c@276 220 if (i % w == w-1) cout << endl;
c@276 221 };
c@276 222 if (n % w != 0) cout << endl;
c@276 223 cout << "};" << endl;
c@276 224
c@276 225 cout << "static void push_" << m_uK << "_" << m_FFTLength << "(vector<unsigned int> &is, vector<unsigned int> &js, vector<double> &real, vector<double> &imag)" << endl;
c@276 226 cout << "{\n is.reserve(" << n << ");\n";
c@276 227 cout << " js.reserve(" << n << ");\n";
c@276 228 cout << " real.reserve(" << n << ");\n";
c@276 229 cout << " imag.reserve(" << n << ");\n";
c@276 230 cout << " for (int i = 0; i < " << n << "; ++i) {" << endl;
c@276 231 cout << " is.push_back(sk_i_" << m_uK << "_" << m_FFTLength << "[i]);" << endl;
c@276 232 cout << " js.push_back(sk_j_" << m_uK << "_" << m_FFTLength << "[i]);" << endl;
c@276 233 cout << " real.push_back(sk_real_" << m_uK << "_" << m_FFTLength << "[i]);" << endl;
c@276 234 cout << " imag.push_back(sk_imag_" << m_uK << "_" << m_FFTLength << "[i]);" << endl;
c@276 235 cout << " }" << endl;
c@276 236 cout << "}" << endl;
c@276 237 */
c@276 238 // std::cerr << "done\n -> is: " << sk->is.size() << ", js: " << sk->js.size() << ", reals: " << sk->real.size() << ", imags: " << sk->imag.size() << std::endl;
c@276 239
c@276 240 m_sparseKernel = sk;
c@276 241 return;
c@225 242 }
c@225 243
c@225 244 //-----------------------------------------------------------------------------
c@257 245 double* ConstantQ::process( const double* fftdata )
c@225 246 {
c@276 247 if (!m_sparseKernel) {
c@276 248 std::cerr << "ERROR: ConstantQ::process: Sparse kernel has not been initialised" << std::endl;
c@276 249 return m_CQdata;
c@276 250 }
c@276 251
c@276 252 SparseKernel *sk = m_sparseKernel;
c@276 253
c@225 254 for (unsigned row=0; row<2*m_uK; row++)
c@225 255 {
c@225 256 m_CQdata[ row ] = 0;
c@225 257 m_CQdata[ row+1 ] = 0;
c@225 258 }
c@276 259 const unsigned *fftbin = &(sk->is[0]);
c@276 260 const unsigned *cqbin = &(sk->js[0]);
c@276 261 const double *real = &(sk->real[0]);
c@276 262 const double *imag = &(sk->imag[0]);
c@276 263 const unsigned int sparseCells = sk->real.size();
c@225 264
c@225 265 for (unsigned i = 0; i<sparseCells; i++)
c@225 266 {
c@225 267 const unsigned row = cqbin[i];
c@225 268 const unsigned col = fftbin[i];
c@225 269 const double & r1 = real[i];
c@225 270 const double & i1 = imag[i];
c@263 271 const double & r2 = fftdata[ (2*m_FFTLength) - 2*col - 2 ];
c@263 272 const double & i2 = fftdata[ (2*m_FFTLength) - 2*col - 2 + 1 ];
c@225 273 // add the multiplication
c@225 274 m_CQdata[ 2*row ] += (r1*r2 - i1*i2);
c@225 275 m_CQdata[ 2*row+1] += (r1*i2 + i1*r2);
c@225 276 }
c@225 277
c@225 278 return m_CQdata;
c@225 279 }
c@225 280
c@225 281
c@225 282 void ConstantQ::initialise( CQConfig Config )
c@225 283 {
c@225 284 m_FS = Config.FS;
c@225 285 m_FMin = Config.min; // min freq
c@225 286 m_FMax = Config.max; // max freq
c@225 287 m_BPO = Config.BPO; // bins per octave
c@225 288 m_CQThresh = Config.CQThresh;// ConstantQ threshold for kernel generation
c@225 289
c@225 290 m_dQ = 1/(pow(2,(1/(double)m_BPO))-1); // Work out Q value for Filter bank
c@225 291 m_uK = (unsigned int) ceil(m_BPO * log(m_FMax/m_FMin)/log(2.0)); // No. of constant Q bins
c@225 292
c@249 293 // std::cerr << "ConstantQ::initialise: rate = " << m_FS << ", fmin = " << m_FMin << ", fmax = " << m_FMax << ", bpo = " << m_BPO << ", K = " << m_uK << ", Q = " << m_dQ << std::endl;
c@245 294
c@225 295 // work out length of fft required for this constant Q Filter bank
c@225 296 m_FFTLength = (int) pow(2, nextpow2(ceil( m_dQ*m_FS/m_FMin )));
c@225 297
c@225 298 m_hop = m_FFTLength/8; // <------ hop size is window length divided by 32
c@225 299
c@249 300 // std::cerr << "ConstantQ::initialise: -> fft length = " << m_FFTLength << ", hop = " << m_hop << std::endl;
c@245 301
c@225 302 // allocate memory for cqdata
c@225 303 m_CQdata = new double [2*m_uK];
c@225 304 }
c@225 305
c@225 306 void ConstantQ::deInitialise()
c@225 307 {
c@225 308 delete [] m_CQdata;
c@276 309 delete m_sparseKernel;
c@225 310 }
c@225 311
c@257 312 void ConstantQ::process(const double *FFTRe, const double* FFTIm,
c@257 313 double *CQRe, double *CQIm)
c@225 314 {
c@276 315 if (!m_sparseKernel) {
c@276 316 std::cerr << "ERROR: ConstantQ::process: Sparse kernel has not been initialised" << std::endl;
c@276 317 return;
c@276 318 }
c@276 319
c@276 320 SparseKernel *sk = m_sparseKernel;
c@276 321
c@225 322 for (unsigned row=0; row<m_uK; row++)
c@225 323 {
c@225 324 CQRe[ row ] = 0;
c@225 325 CQIm[ row ] = 0;
c@225 326 }
c@225 327
c@276 328 const unsigned *fftbin = &(sk->is[0]);
c@276 329 const unsigned *cqbin = &(sk->js[0]);
c@276 330 const double *real = &(sk->real[0]);
c@276 331 const double *imag = &(sk->imag[0]);
c@276 332 const unsigned int sparseCells = sk->real.size();
c@225 333
c@225 334 for (unsigned i = 0; i<sparseCells; i++)
c@225 335 {
c@225 336 const unsigned row = cqbin[i];
c@225 337 const unsigned col = fftbin[i];
c@225 338 const double & r1 = real[i];
c@225 339 const double & i1 = imag[i];
c@263 340 const double & r2 = FFTRe[ m_FFTLength - col - 1 ];
c@263 341 const double & i2 = FFTIm[ m_FFTLength - col - 1 ];
c@225 342 // add the multiplication
c@225 343 CQRe[ row ] += (r1*r2 - i1*i2);
c@225 344 CQIm[ row ] += (r1*i2 + i1*r2);
c@225 345 }
c@225 346 }