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