annotate dsp/chromagram/ConstantQ.cpp @ 478:c92718cc6ef1

Untabify, indent, tidy
author Chris Cannam <cannam@all-day-breakfast.com>
date Thu, 30 May 2019 18:40:16 +0100
parents 73fc1de3254a
children fdaa63607c15
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@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 }