annotate dsp/chromagram/ConstantQ.cpp @ 34:ad645e404d0c

* Add range of normalise options to chromagram * bit of tidying
author cannam
date Tue, 22 Jan 2008 17:27:07 +0000
parents 8bb764969d50
children 3dff6e3e2121
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.
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 }