annotate dsp/chromagram/ConstantQ.cpp @ 209:ccd2019190bf msvc

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