Mercurial > hg > qm-dsp
diff dsp/chromagram/ConstantQ.cpp @ 505:930b5b0f707d
Merge branch 'codestyle-and-tidy'
author | Chris Cannam <cannam@all-day-breakfast.com> |
---|---|
date | Wed, 05 Jun 2019 12:55:15 +0100 |
parents | 0d3a001e63c7 |
children |
line wrap: on
line diff
--- a/dsp/chromagram/ConstantQ.cpp Thu May 30 16:18:13 2019 +0100 +++ b/dsp/chromagram/ConstantQ.cpp Wed Jun 05 12:55:15 2019 +0100 @@ -14,57 +14,16 @@ #include "ConstantQ.h" #include "dsp/transforms/FFT.h" +#include "base/Window.h" #include <iostream> -#ifdef NOT_DEFINED -// see note in CQprecalc - -#include "CQprecalc.cpp" - -static bool push_precalculated(int uk, int fftlength, - std::vector<unsigned> &is, - std::vector<unsigned> &js, - std::vector<double> &real, - std::vector<double> &imag) -{ - if (uk == 76 && fftlength == 16384) { - push_76_16384(is, js, real, imag); - return true; - } - if (uk == 144 && fftlength == 4096) { - push_144_4096(is, js, real, imag); - return true; - } - if (uk == 65 && fftlength == 2048) { - push_65_2048(is, js, real, imag); - return true; - } - if (uk == 84 && fftlength == 65536) { - push_84_65536(is, js, real, imag); - return true; - } - return false; -} -#endif - -//--------------------------------------------------------------------------- -// nextpow2 returns the smallest integer n such that 2^n >= x. -static double nextpow2(double x) { - double y = ceil(log(x)/log(2.0)); - return(y); -} - -static double squaredModule(const double & xx, const double & yy) { - return xx*xx + yy*yy; -} - //---------------------------------------------------------------------------- -ConstantQ::ConstantQ( CQConfig Config ) : +ConstantQ::ConstantQ( CQConfig config ) : m_sparseKernel(0) { - initialise( Config ); + initialise(config); } ConstantQ::~ConstantQ() @@ -72,181 +31,129 @@ deInitialise(); } -//---------------------------------------------------------------------------- +static double squaredModule(const double & xx, const double & yy) { + return xx*xx + yy*yy; +} + void ConstantQ::sparsekernel() { -// std::cerr << "ConstantQ: initialising sparse kernel, uK = " << m_uK << ", FFTLength = " << m_FFTLength << "..."; - SparseKernel *sk = new SparseKernel(); -#ifdef NOT_DEFINED - if (push_precalculated(m_uK, m_FFTLength, - sk->is, sk->js, sk->real, sk->imag)) { -// std::cerr << "using precalculated kernel" << std::endl; - m_sparseKernel = sk; - return; - } -#endif + double* windowRe = new double [ m_FFTLength ]; + double* windowIm = new double [ m_FFTLength ]; + double* transfWindowRe = new double [ m_FFTLength ]; + double* transfWindowIm = new double [ m_FFTLength ]; + + // for each bin value K, calculate temporal kernel, take its fft + // to calculate the spectral kernel then threshold it to make it + // sparse and add it to the sparse kernels matrix + + double squareThreshold = m_CQThresh * m_CQThresh; - //generates spectral kernel matrix (upside down?) - // initialise temporal kernel with zeros, twice length to deal w. complex numbers + FFT fft(m_FFTLength); + + for (int j = m_uK - 1; j >= 0; --j) { + + for (int i = 0; i < m_FFTLength; ++i) { + windowRe[i] = 0; + windowIm[i] = 0; + } - double* hammingWindowRe = new double [ m_FFTLength ]; - double* hammingWindowIm = new double [ m_FFTLength ]; - double* transfHammingWindowRe = new double [ m_FFTLength ]; - double* transfHammingWindowIm = new double [ m_FFTLength ]; + // Compute a complex sinusoid windowed with a hamming window + // of the right length + + int windowLength = (int)ceil + (m_dQ * m_FS / (m_FMin * pow(2, (double)j / (double)m_BPO))); - for (unsigned u=0; u < m_FFTLength; u++) - { - hammingWindowRe[u] = 0; - hammingWindowIm[u] = 0; + int origin = m_FFTLength/2 - windowLength/2; + + for (int i = 0; i < windowLength; ++i) { + double angle = (2.0 * M_PI * m_dQ * i) / windowLength; + windowRe[origin + i] = cos(angle); + windowIm[origin + i] = sin(angle); + } + + // Shape with hamming window + Window<double> hamming(HammingWindow, windowLength); + hamming.cut(windowRe + origin); + hamming.cut(windowIm + origin); + + // Scale + for (int i = 0; i < windowLength; ++i) { + windowRe[origin + i] /= windowLength; + } + for (int i = 0; i < windowLength; ++i) { + windowIm[origin + i] /= windowLength; + } + + // Input is expected to have been fftshifted, so do the + // same to the input to the fft that contains the kernel + for (int i = 0; i < m_FFTLength/2; ++i) { + double temp = windowRe[i]; + windowRe[i] = windowRe[i + m_FFTLength/2]; + windowRe[i + m_FFTLength/2] = temp; + } + for (int i = 0; i < m_FFTLength/2; ++i) { + double temp = windowIm[i]; + windowIm[i] = windowIm[i + m_FFTLength/2]; + windowIm[i + m_FFTLength/2] = temp; + } + + fft.process(false, windowRe, windowIm, transfWindowRe, transfWindowIm); + + // convert to sparse form + for (int i = 0; i < m_FFTLength; i++) { + + // perform thresholding + double mag = squaredModule(transfWindowRe[i], transfWindowIm[i]); + if (mag <= squareThreshold) continue; + + // Insert non-zero position indexes + sk->is.push_back(i); + sk->js.push_back(j); + + // take conjugate, normalise and add to array for sparse kernel + sk->real.push_back( transfWindowRe[i] / m_FFTLength); + sk->imag.push_back(-transfWindowIm[i] / m_FFTLength); + } } - // Here, fftleng*2 is a guess of the number of sparse cells in the matrix - // The matrix K x fftlength but the non-zero cells are an antialiased - // square root function. So mostly is a line, with some grey point. - sk->is.reserve( m_FFTLength*2 ); - sk->js.reserve( m_FFTLength*2 ); - sk->real.reserve( m_FFTLength*2 ); - sk->imag.reserve( m_FFTLength*2 ); - - // for each bin value K, calculate temporal kernel, take its fft to - //calculate the spectral kernel then threshold it to make it sparse and - //add it to the sparse kernels matrix - double squareThreshold = m_CQThresh * m_CQThresh; + delete [] windowRe; + delete [] windowIm; + delete [] transfWindowRe; + delete [] transfWindowIm; - FFT m_FFT(m_FFTLength); - - for (unsigned k = m_uK; k--; ) - { - for (unsigned u=0; u < m_FFTLength; u++) - { - hammingWindowRe[u] = 0; - hammingWindowIm[u] = 0; - } - - // Computing a hamming window - const unsigned hammingLength = (int) ceil( m_dQ * m_FS / ( m_FMin * pow(2,((double)(k))/(double)m_BPO))); + m_sparseKernel = sk; +} -// 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; - +void ConstantQ::initialise( CQConfig Config ) +{ + m_FS = Config.FS; // Sample rate + m_FMin = Config.min; // Minimum frequency + m_FMax = Config.max; // Maximum frequency + m_BPO = Config.BPO; // Bins per octave + m_CQThresh = Config.CQThresh; // Threshold for sparse kernel generation - unsigned origin = m_FFTLength/2 - hammingLength/2; + // Q value for filter bank + m_dQ = 1/(pow(2,(1/(double)m_BPO))-1); - for (unsigned i=0; i<hammingLength; i++) - { - const double angle = 2*PI*m_dQ*i/hammingLength; - const double real = cos(angle); - const double imag = sin(angle); - const double absol = hamming(hammingLength, i)/hammingLength; - hammingWindowRe[ origin + i ] = absol*real; - hammingWindowIm[ origin + i ] = absol*imag; - } + // No. of constant Q bins + m_uK = (int)ceil(m_BPO * log(m_FMax/m_FMin)/log(2.0)); - for (unsigned i = 0; i < m_FFTLength/2; ++i) { - double temp = hammingWindowRe[i]; - hammingWindowRe[i] = hammingWindowRe[i + m_FFTLength/2]; - hammingWindowRe[i + m_FFTLength/2] = temp; - temp = hammingWindowIm[i]; - hammingWindowIm[i] = hammingWindowIm[i + m_FFTLength/2]; - hammingWindowIm[i + m_FFTLength/2] = temp; - } - - //do fft of hammingWindow - m_FFT.process( 0, hammingWindowRe, hammingWindowIm, transfHammingWindowRe, transfHammingWindowIm ); + // Length of fft required for this Constant Q filter bank + m_FFTLength = MathUtilities::nextPowerOfTwo(int(ceil(m_dQ * m_FS/m_FMin))); - - for (unsigned j=0; j<( m_FFTLength ); j++) - { - // perform thresholding - const double squaredBin = squaredModule( transfHammingWindowRe[ j ], transfHammingWindowIm[ j ]); - if (squaredBin <= squareThreshold) continue; - - // Insert non-zero position indexes - sk->is.push_back(j); - sk->js.push_back(k); + // Hop from one frame to next + m_hop = m_FFTLength / 8; - // take conjugate, normalise and add to array sparkernel - sk->real.push_back( transfHammingWindowRe[ j ]/m_FFTLength); - sk->imag.push_back(-transfHammingWindowIm[ j ]/m_FFTLength); - } + // allocate memory for cqdata + m_CQdata = new double [2*m_uK]; +} - } - - delete [] hammingWindowRe; - delete [] hammingWindowIm; - delete [] transfHammingWindowRe; - delete [] transfHammingWindowIm; - -/* - using std::cout; - using std::endl; - - cout.precision(28); - - int n = sk->is.size(); - int w = 8; - cout << "static unsigned int sk_i_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl; - for (int i = 0; i < n; ++i) { - if (i % w == 0) cout << " "; - cout << sk->is[i]; - if (i + 1 < n) cout << ", "; - if (i % w == w-1) cout << endl; - }; - if (n % w != 0) cout << endl; - cout << "};" << endl; - - n = sk->js.size(); - cout << "static unsigned int sk_j_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl; - for (int i = 0; i < n; ++i) { - if (i % w == 0) cout << " "; - cout << sk->js[i]; - if (i + 1 < n) cout << ", "; - if (i % w == w-1) cout << endl; - }; - if (n % w != 0) cout << endl; - cout << "};" << endl; - - w = 2; - n = sk->real.size(); - cout << "static double sk_real_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl; - for (int i = 0; i < n; ++i) { - if (i % w == 0) cout << " "; - cout << sk->real[i]; - if (i + 1 < n) cout << ", "; - if (i % w == w-1) cout << endl; - }; - if (n % w != 0) cout << endl; - cout << "};" << endl; - - n = sk->imag.size(); - cout << "static double sk_imag_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl; - for (int i = 0; i < n; ++i) { - if (i % w == 0) cout << " "; - cout << sk->imag[i]; - if (i + 1 < n) cout << ", "; - if (i % w == w-1) cout << endl; - }; - if (n % w != 0) cout << endl; - cout << "};" << endl; - - cout << "static void push_" << m_uK << "_" << m_FFTLength << "(vector<unsigned int> &is, vector<unsigned int> &js, vector<double> &real, vector<double> &imag)" << endl; - cout << "{\n is.reserve(" << n << ");\n"; - cout << " js.reserve(" << n << ");\n"; - cout << " real.reserve(" << n << ");\n"; - cout << " imag.reserve(" << n << ");\n"; - cout << " for (int i = 0; i < " << n << "; ++i) {" << endl; - cout << " is.push_back(sk_i_" << m_uK << "_" << m_FFTLength << "[i]);" << endl; - cout << " js.push_back(sk_j_" << m_uK << "_" << m_FFTLength << "[i]);" << endl; - cout << " real.push_back(sk_real_" << m_uK << "_" << m_FFTLength << "[i]);" << endl; - cout << " imag.push_back(sk_imag_" << m_uK << "_" << m_FFTLength << "[i]);" << endl; - cout << " }" << endl; - cout << "}" << endl; -*/ -// std::cerr << "done\n -> is: " << sk->is.size() << ", js: " << sk->js.size() << ", reals: " << sk->real.size() << ", imags: " << sk->imag.size() << std::endl; - - m_sparseKernel = sk; - return; +void ConstantQ::deInitialise() +{ + delete [] m_CQdata; + delete m_sparseKernel; } //----------------------------------------------------------------------------- @@ -259,65 +166,32 @@ SparseKernel *sk = m_sparseKernel; - for (unsigned row=0; row<2*m_uK; row++) - { - m_CQdata[ row ] = 0; - m_CQdata[ row+1 ] = 0; + for (int row=0; row < 2 * m_uK; row++) { + m_CQdata[ row ] = 0; + m_CQdata[ row+1 ] = 0; } - const unsigned *fftbin = &(sk->is[0]); - const unsigned *cqbin = &(sk->js[0]); - const double *real = &(sk->real[0]); - const double *imag = &(sk->imag[0]); - const unsigned int sparseCells = sk->real.size(); - - for (unsigned i = 0; i<sparseCells; i++) - { - const unsigned row = cqbin[i]; - const unsigned col = fftbin[i]; + const int *fftbin = &(sk->is[0]); + const int *cqbin = &(sk->js[0]); + const double *real = &(sk->real[0]); + const double *imag = &(sk->imag[0]); + const int sparseCells = int(sk->real.size()); + + for (int i = 0; i < sparseCells; i++) { + const int row = cqbin[i]; + const int col = fftbin[i]; if (col == 0) continue; - const double & r1 = real[i]; - const double & i1 = imag[i]; - const double & r2 = fftdata[ (2*m_FFTLength) - 2*col - 2 ]; - const double & i2 = fftdata[ (2*m_FFTLength) - 2*col - 2 + 1 ]; - // add the multiplication - m_CQdata[ 2*row ] += (r1*r2 - i1*i2); - m_CQdata[ 2*row+1] += (r1*i2 + i1*r2); + const double & r1 = real[i]; + const double & i1 = imag[i]; + const double & r2 = fftdata[ (2*m_FFTLength) - 2*col - 2 ]; + const double & i2 = fftdata[ (2*m_FFTLength) - 2*col - 2 + 1 ]; + // add the multiplication + m_CQdata[ 2*row ] += (r1*r2 - i1*i2); + m_CQdata[ 2*row+1] += (r1*i2 + i1*r2); } return m_CQdata; } - -void ConstantQ::initialise( CQConfig Config ) -{ - m_FS = Config.FS; - m_FMin = Config.min; // min freq - m_FMax = Config.max; // max freq - m_BPO = Config.BPO; // bins per octave - m_CQThresh = Config.CQThresh;// ConstantQ threshold for kernel generation - - m_dQ = 1/(pow(2,(1/(double)m_BPO))-1); // Work out Q value for Filter bank - m_uK = (unsigned int) ceil(m_BPO * log(m_FMax/m_FMin)/log(2.0)); // No. of constant Q bins - -// std::cerr << "ConstantQ::initialise: rate = " << m_FS << ", fmin = " << m_FMin << ", fmax = " << m_FMax << ", bpo = " << m_BPO << ", K = " << m_uK << ", Q = " << m_dQ << std::endl; - - // work out length of fft required for this constant Q Filter bank - m_FFTLength = (int) pow(2, nextpow2(ceil( m_dQ*m_FS/m_FMin ))); - - m_hop = m_FFTLength/8; - -// std::cerr << "ConstantQ::initialise: -> fft length = " << m_FFTLength << ", hop = " << m_hop << std::endl; - - // allocate memory for cqdata - m_CQdata = new double [2*m_uK]; -} - -void ConstantQ::deInitialise() -{ - delete [] m_CQdata; - delete m_sparseKernel; -} - void ConstantQ::process(const double *FFTRe, const double* FFTIm, double *CQRe, double *CQIm) { @@ -328,29 +202,27 @@ SparseKernel *sk = m_sparseKernel; - for (unsigned row=0; row<m_uK; row++) - { - CQRe[ row ] = 0; - CQIm[ row ] = 0; + for (int row = 0; row < m_uK; row++) { + CQRe[ row ] = 0; + CQIm[ row ] = 0; } - const unsigned *fftbin = &(sk->is[0]); - const unsigned *cqbin = &(sk->js[0]); - const double *real = &(sk->real[0]); - const double *imag = &(sk->imag[0]); - const unsigned int sparseCells = sk->real.size(); - - for (unsigned i = 0; i<sparseCells; i++) - { - const unsigned row = cqbin[i]; - const unsigned col = fftbin[i]; + const int *fftbin = &(sk->is[0]); + const int *cqbin = &(sk->js[0]); + const double *real = &(sk->real[0]); + const double *imag = &(sk->imag[0]); + const int sparseCells = int(sk->real.size()); + + for (int i = 0; i<sparseCells; i++) { + const int row = cqbin[i]; + const int col = fftbin[i]; if (col == 0) continue; - const double & r1 = real[i]; - const double & i1 = imag[i]; - const double & r2 = FFTRe[ m_FFTLength - col ]; - const double & i2 = FFTIm[ m_FFTLength - col ]; - // add the multiplication - CQRe[ row ] += (r1*r2 - i1*i2); - CQIm[ row ] += (r1*i2 + i1*r2); + const double & r1 = real[i]; + const double & i1 = imag[i]; + const double & r2 = FFTRe[ m_FFTLength - col ]; + const double & i2 = FFTIm[ m_FFTLength - col ]; + // add the multiplication + CQRe[ row ] += (r1*r2 - i1*i2); + CQIm[ row ] += (r1*i2 + i1*r2); } }