annotate dsp/chromagram/ConstantQ.cpp @ 263:715b0a6bcdc0

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