annotate dsp/chromagram/ConstantQ.cpp @ 321:f1e6be2de9a5

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