| 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@225 | 20 //--------------------------------------------------------------------------- | 
| c@225 | 21 // nextpow2 returns the smallest integer n such that 2^n >= x. | 
| c@225 | 22 static double nextpow2(double x) { | 
| c@225 | 23     double y = ceil(log(x)/log(2.0)); | 
| c@225 | 24     return(y); | 
| c@225 | 25 } | 
| c@225 | 26 | 
| c@225 | 27 static double squaredModule(const double & xx, const double & yy) { | 
| c@225 | 28     return xx*xx + yy*yy; | 
| c@225 | 29 } | 
| c@225 | 30 | 
| c@225 | 31 //---------------------------------------------------------------------------- | 
| c@225 | 32 | 
| c@276 | 33 ConstantQ::ConstantQ( CQConfig Config ) : | 
| c@276 | 34     m_sparseKernel(0) | 
| c@225 | 35 { | 
| c@225 | 36     initialise( Config ); | 
| c@225 | 37 } | 
| c@225 | 38 | 
| c@225 | 39 ConstantQ::~ConstantQ() | 
| c@225 | 40 { | 
| c@225 | 41     deInitialise(); | 
| c@225 | 42 } | 
| c@225 | 43 | 
| c@225 | 44 //---------------------------------------------------------------------------- | 
| c@225 | 45 void ConstantQ::sparsekernel() | 
| c@225 | 46 { | 
| c@276 | 47 //    std::cerr << "ConstantQ: initialising sparse kernel, uK = " << m_uK << ", FFTLength = " << m_FFTLength << "..."; | 
| c@276 | 48 | 
| c@276 | 49     SparseKernel *sk = new SparseKernel(); | 
| c@276 | 50 | 
| c@225 | 51     //generates spectral kernel matrix (upside down?) | 
| c@225 | 52     // initialise temporal kernel with zeros, twice length to deal w. complex numbers | 
| c@225 | 53 | 
| c@225 | 54     double* hammingWindowRe = new double [ m_FFTLength ]; | 
| c@225 | 55     double* hammingWindowIm = new double [ m_FFTLength ]; | 
| c@225 | 56     double* transfHammingWindowRe = new double [ m_FFTLength ]; | 
| c@225 | 57     double* transfHammingWindowIm = new double [ m_FFTLength ]; | 
| c@225 | 58 | 
| c@225 | 59     for (unsigned u=0; u < m_FFTLength; u++) | 
| c@225 | 60     { | 
| c@225 | 61 	hammingWindowRe[u] = 0; | 
| c@225 | 62 	hammingWindowIm[u] = 0; | 
| c@225 | 63     } | 
| c@225 | 64 | 
| c@225 | 65     // Here, fftleng*2 is a guess of the number of sparse cells in the matrix | 
| c@225 | 66     // The matrix K x fftlength but the non-zero cells are an antialiased | 
| c@225 | 67     // square root function. So mostly is a line, with some grey point. | 
| c@276 | 68     sk->is.reserve( m_FFTLength*2 ); | 
| c@276 | 69     sk->js.reserve( m_FFTLength*2 ); | 
| c@276 | 70     sk->real.reserve( m_FFTLength*2 ); | 
| c@276 | 71     sk->imag.reserve( m_FFTLength*2 ); | 
| c@225 | 72 | 
| c@225 | 73     // for each bin value K, calculate temporal kernel, take its fft to | 
| c@225 | 74     //calculate the spectral kernel then threshold it to make it sparse and | 
| c@225 | 75     //add it to the sparse kernels matrix | 
| c@225 | 76     double squareThreshold = m_CQThresh * m_CQThresh; | 
| c@225 | 77 | 
| c@289 | 78     FFT m_FFT(m_FFTLength); | 
| c@225 | 79 | 
| c@225 | 80     for (unsigned k = m_uK; k--; ) | 
| c@225 | 81     { | 
| c@228 | 82         for (unsigned u=0; u < m_FFTLength; u++) | 
| c@228 | 83         { | 
| c@228 | 84             hammingWindowRe[u] = 0; | 
| c@228 | 85             hammingWindowIm[u] = 0; | 
| c@228 | 86         } | 
| c@228 | 87 | 
| c@225 | 88 	// Computing a hamming window | 
| c@225 | 89 	const unsigned hammingLength = (int) ceil( m_dQ * m_FS / ( m_FMin * pow(2,((double)(k))/(double)m_BPO))); | 
| c@228 | 90 | 
| cannam@469 | 91 //        cerr << "k = " << k << ", q = " << m_dQ << ", m_FMin = " << m_FMin << ", hammingLength = " << hammingLength << " (rounded up from " << (m_dQ * m_FS / ( m_FMin * pow(2,((double)(k))/(double)m_BPO))) << ")" << endl; | 
| cannam@469 | 92 | 
| cannam@469 | 93 | 
| c@228 | 94         unsigned origin = m_FFTLength/2 - hammingLength/2; | 
| c@228 | 95 | 
| c@225 | 96 	for (unsigned i=0; i<hammingLength; i++) | 
| c@225 | 97 	{ | 
| c@225 | 98 	    const double angle = 2*PI*m_dQ*i/hammingLength; | 
| c@225 | 99 	    const double real = cos(angle); | 
| c@225 | 100 	    const double imag = sin(angle); | 
| c@225 | 101 	    const double absol = hamming(hammingLength, i)/hammingLength; | 
| c@228 | 102 	    hammingWindowRe[ origin + i ] = absol*real; | 
| c@228 | 103 	    hammingWindowIm[ origin + i ] = absol*imag; | 
| c@225 | 104 	} | 
| c@225 | 105 | 
| c@228 | 106         for (unsigned i = 0; i < m_FFTLength/2; ++i) { | 
| c@228 | 107             double temp = hammingWindowRe[i]; | 
| c@228 | 108             hammingWindowRe[i] = hammingWindowRe[i + m_FFTLength/2]; | 
| c@228 | 109             hammingWindowRe[i + m_FFTLength/2] = temp; | 
| c@228 | 110             temp = hammingWindowIm[i]; | 
| c@228 | 111             hammingWindowIm[i] = hammingWindowIm[i + m_FFTLength/2]; | 
| c@228 | 112             hammingWindowIm[i + m_FFTLength/2] = temp; | 
| c@228 | 113         } | 
| c@228 | 114 | 
| c@225 | 115 	//do fft of hammingWindow | 
| c@289 | 116 	m_FFT.process( 0, hammingWindowRe, hammingWindowIm, transfHammingWindowRe, transfHammingWindowIm ); | 
| c@225 | 117 | 
| c@225 | 118 | 
| c@225 | 119 	for (unsigned j=0; j<( m_FFTLength ); j++) | 
| c@225 | 120 	{ | 
| c@225 | 121 	    // perform thresholding | 
| c@225 | 122 	    const double squaredBin = squaredModule( transfHammingWindowRe[ j ], transfHammingWindowIm[ j ]); | 
| c@225 | 123 	    if (squaredBin <= squareThreshold) continue; | 
| c@225 | 124 | 
| cannam@469 | 125 	    // Insert non-zero position indexes | 
| c@276 | 126 	    sk->is.push_back(j); | 
| c@276 | 127 	    sk->js.push_back(k); | 
| c@225 | 128 | 
| c@225 | 129 	    // take conjugate, normalise and add to array sparkernel | 
| c@276 | 130 	    sk->real.push_back( transfHammingWindowRe[ j ]/m_FFTLength); | 
| c@276 | 131 	    sk->imag.push_back(-transfHammingWindowIm[ j ]/m_FFTLength); | 
| c@225 | 132 	} | 
| c@225 | 133 | 
| c@225 | 134     } | 
| c@225 | 135 | 
| c@225 | 136     delete [] hammingWindowRe; | 
| c@225 | 137     delete [] hammingWindowIm; | 
| c@225 | 138     delete [] transfHammingWindowRe; | 
| c@225 | 139     delete [] transfHammingWindowIm; | 
| c@225 | 140 | 
| c@276 | 141 //    std::cerr << "done\n -> is: " << sk->is.size() << ", js: " << sk->js.size() << ", reals: " << sk->real.size() << ", imags: " << sk->imag.size() << std::endl; | 
| c@276 | 142 | 
| c@276 | 143     m_sparseKernel = sk; | 
| c@276 | 144     return; | 
| c@225 | 145 } | 
| c@225 | 146 | 
| c@225 | 147 //----------------------------------------------------------------------------- | 
| c@257 | 148 double* ConstantQ::process( const double* fftdata ) | 
| c@225 | 149 { | 
| c@276 | 150     if (!m_sparseKernel) { | 
| c@276 | 151         std::cerr << "ERROR: ConstantQ::process: Sparse kernel has not been initialised" << std::endl; | 
| c@276 | 152         return m_CQdata; | 
| c@276 | 153     } | 
| c@276 | 154 | 
| c@276 | 155     SparseKernel *sk = m_sparseKernel; | 
| c@276 | 156 | 
| c@225 | 157     for (unsigned row=0; row<2*m_uK; row++) | 
| c@225 | 158     { | 
| c@225 | 159 	m_CQdata[ row ] = 0; | 
| c@225 | 160 	m_CQdata[ row+1 ] = 0; | 
| c@225 | 161     } | 
| c@276 | 162     const unsigned *fftbin = &(sk->is[0]); | 
| c@276 | 163     const unsigned *cqbin  = &(sk->js[0]); | 
| c@276 | 164     const double   *real   = &(sk->real[0]); | 
| c@276 | 165     const double   *imag   = &(sk->imag[0]); | 
| c@276 | 166     const unsigned int sparseCells = sk->real.size(); | 
| c@225 | 167 | 
| c@225 | 168     for (unsigned i = 0; i<sparseCells; i++) | 
| c@225 | 169     { | 
| c@225 | 170 	const unsigned row = cqbin[i]; | 
| c@225 | 171 	const unsigned col = fftbin[i]; | 
| cannam@469 | 172         if (col == 0) continue; | 
| c@225 | 173 	const double & r1  = real[i]; | 
| c@225 | 174 	const double & i1  = imag[i]; | 
| c@263 | 175 	const double & r2  = fftdata[ (2*m_FFTLength) - 2*col - 2 ]; | 
| c@263 | 176 	const double & i2  = fftdata[ (2*m_FFTLength) - 2*col - 2 + 1 ]; | 
| c@225 | 177 	// add the multiplication | 
| c@225 | 178 	m_CQdata[ 2*row  ] += (r1*r2 - i1*i2); | 
| c@225 | 179 	m_CQdata[ 2*row+1] += (r1*i2 + i1*r2); | 
| c@225 | 180     } | 
| c@225 | 181 | 
| c@225 | 182     return m_CQdata; | 
| c@225 | 183 } | 
| c@225 | 184 | 
| c@225 | 185 | 
| c@225 | 186 void ConstantQ::initialise( CQConfig Config ) | 
| c@225 | 187 { | 
| c@225 | 188     m_FS = Config.FS; | 
| c@225 | 189     m_FMin = Config.min;		// min freq | 
| c@225 | 190     m_FMax = Config.max;		// max freq | 
| c@225 | 191     m_BPO = Config.BPO;		// bins per octave | 
| c@225 | 192     m_CQThresh = Config.CQThresh;// ConstantQ threshold for kernel generation | 
| c@225 | 193 | 
| c@225 | 194     m_dQ = 1/(pow(2,(1/(double)m_BPO))-1);	// Work out Q value for Filter bank | 
| cannam@468 | 195     m_uK = (unsigned int) ceil(m_BPO * log(m_FMax/m_FMin)/log(2.0));	// No. of constant Q bins | 
| c@225 | 196 | 
| c@249 | 197 //    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 | 198 | 
| c@225 | 199     // work out length of fft required for this constant Q Filter bank | 
| c@225 | 200     m_FFTLength = (int) pow(2, nextpow2(ceil( m_dQ*m_FS/m_FMin ))); | 
| c@225 | 201 | 
| cannam@468 | 202     m_hop = m_FFTLength/8; | 
| c@225 | 203 | 
| c@249 | 204 //    std::cerr << "ConstantQ::initialise: -> fft length = " << m_FFTLength << ", hop = " << m_hop << std::endl; | 
| c@245 | 205 | 
| c@225 | 206     // allocate memory for cqdata | 
| c@225 | 207     m_CQdata = new double [2*m_uK]; | 
| c@225 | 208 } | 
| c@225 | 209 | 
| c@225 | 210 void ConstantQ::deInitialise() | 
| c@225 | 211 { | 
| c@225 | 212     delete [] m_CQdata; | 
| c@276 | 213     delete m_sparseKernel; | 
| c@225 | 214 } | 
| c@225 | 215 | 
| c@257 | 216 void ConstantQ::process(const double *FFTRe, const double* FFTIm, | 
| c@257 | 217                         double *CQRe, double *CQIm) | 
| c@225 | 218 { | 
| c@276 | 219     if (!m_sparseKernel) { | 
| c@276 | 220         std::cerr << "ERROR: ConstantQ::process: Sparse kernel has not been initialised" << std::endl; | 
| c@276 | 221         return; | 
| c@276 | 222     } | 
| c@276 | 223 | 
| c@276 | 224     SparseKernel *sk = m_sparseKernel; | 
| c@276 | 225 | 
| c@225 | 226     for (unsigned row=0; row<m_uK; row++) | 
| c@225 | 227     { | 
| c@225 | 228 	CQRe[ row ] = 0; | 
| c@225 | 229 	CQIm[ row ] = 0; | 
| c@225 | 230     } | 
| c@225 | 231 | 
| c@276 | 232     const unsigned *fftbin = &(sk->is[0]); | 
| c@276 | 233     const unsigned *cqbin  = &(sk->js[0]); | 
| c@276 | 234     const double   *real   = &(sk->real[0]); | 
| c@276 | 235     const double   *imag   = &(sk->imag[0]); | 
| c@276 | 236     const unsigned int sparseCells = sk->real.size(); | 
| c@225 | 237 | 
| c@225 | 238     for (unsigned i = 0; i<sparseCells; i++) | 
| c@225 | 239     { | 
| c@225 | 240 	const unsigned row = cqbin[i]; | 
| c@225 | 241 	const unsigned col = fftbin[i]; | 
| cannam@469 | 242         if (col == 0) continue; | 
| c@225 | 243 	const double & r1  = real[i]; | 
| c@225 | 244 	const double & i1  = imag[i]; | 
| cannam@469 | 245 	const double & r2  = FFTRe[ m_FFTLength - col ]; | 
| cannam@469 | 246 	const double & i2  = FFTIm[ m_FFTLength - col ]; | 
| c@225 | 247 	// add the multiplication | 
| c@225 | 248 	CQRe[ row ] += (r1*r2 - i1*i2); | 
| c@225 | 249 	CQIm[ row ] += (r1*i2 + i1*r2); | 
| c@225 | 250     } | 
| c@225 | 251 } |