ConstantQTransform.cxx

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2001-2006 MUSIC TECHNOLOGY GROUP (MTG)
00003  *                         UNIVERSITAT POMPEU FABRA
00004  *
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  */
00021 
00022 #include <iostream>
00023 #include "ConstantQTransform.hxx"
00024 #include "FourierTransform.hxx"
00025 #include <cmath>
00026 
00027 namespace Simac
00028 {
00029 //---------------------------------------------------------------------------
00030 // nextpow2 returns the smallest integer n such that 2^n >= x.
00031 static double nextpow2(double x)
00032 {
00033         return ceil(std::log(x)/std::log(2.0));
00034 }
00035 //----------------------------------------------------------------------------
00036 static double squaredModule(const double & xx, const double & yy)
00037 {
00038         return xx*xx + yy*yy;
00039 }
00040 //----------------------------------------------------------------------------
00041 
00042 ConstantQTransform::ConstantQTransform(unsigned iFS, double ifmin, double ifmax, unsigned binsPerOctave) 
00043         : _binsPerOctave(binsPerOctave)
00044 {
00045         FS = iFS;
00046         fmin = ifmin;           // min freq
00047         fmax = ifmax;           // max freq
00048 
00049         Q = 1/(std::pow(2.,(1/(double)_binsPerOctave))-1);      // Work out Q value for filter bank
00050         K = (int) ceil(_binsPerOctave * std::log(fmax/fmin)/std::log(2.0));     // No. of constant Q bins
00051 
00052         // work out length of fft required for this constant Q filter bank
00053         mSpectrumSize = (int) std::pow(2., nextpow2(ceil(Q*FS/fmin)));
00054 
00055         // allocate memory for cqdata
00056         cqdata.resize(2*K);
00057 }
00058 
00059 ConstantQTransform::~ConstantQTransform()
00060 {
00061 }
00062 
00063 void ConstantQTransform::sparsekernel(double thresh)
00064 {
00065         //generates spectral kernel matrix (upside down?)
00066         // initialise temporal kernel with zeros, twice length to deal w. complex numbers
00067         double* hammingWindow = new double [2*mSpectrumSize];
00068         for (unsigned mm=0; mm<2*mSpectrumSize; mm++) {
00069                 hammingWindow[mm] = 0;
00070         }
00071         // Here, fftleng*2 is a guess of the number of sparse cells in the matrix
00072         // The matrix K x mSpectrumSize but the non-zero cells are an antialiased
00073         // square root function. So mostly is a line, with some grey point.
00074         mSparseKernelIs.reserve(mSpectrumSize*2);
00075         mSparseKernelJs.reserve(mSpectrumSize*2);
00076         mSparseKernelRealValues.reserve(mSpectrumSize*2);
00077         mSparseKernelImagValues.reserve(mSpectrumSize*2);
00078         
00079         // for each bin value K, calculate temporal kernel, take its fft to
00080         //calculate the spectral kernel then threshold it to make it sparse and 
00081         //add it to the sparse kernels matrix
00082         double squareThreshold = thresh * thresh;
00083         FourierTransform fft(mSpectrumSize, 1, true);
00084         for (unsigned k=K; k--; ) {
00085                 // Computing a hamming window
00086                 const unsigned hammingLength = (int) ceil(Q * FS / (fmin * std::pow(2.,((double)(k))/(double)_binsPerOctave)));
00087                 for (unsigned i=0; i<hammingLength; i++) {
00088                         const double angle = 2*M_PI*Q*i/hammingLength;
00089                         const double real = cos(angle);
00090                         const double imag = sin(angle);
00091                         const double absol = Hamming(hammingLength, i)/hammingLength;
00092                         hammingWindow[2*i  ] = absol*real;
00093                         hammingWindow[2*i+1] = absol*imag;
00094                 }
00095 
00096                 //do fft of hammingWindow
00097                 fft.doIt(hammingWindow);
00098                 const double * transformedHamming = &fft.spectrum()[0];
00099                 // 2 steps because they are complex
00100                 for (unsigned j=0; j<(2*mSpectrumSize); j+=2) {
00101                         // perform thresholding
00102                         const double squaredBin = squaredModule(transformedHamming[j], transformedHamming[j+1]);
00103                         if (squaredBin <= squareThreshold) continue;
00104                         // Insert non-zero position indexes, doubled because they are floats
00105                         mSparseKernelIs.push_back(j);
00106                         mSparseKernelJs.push_back(k*2);
00107                         // take conjugate, normalise and add to array sparkernel
00108                         mSparseKernelRealValues.push_back( transformedHamming[j  ]/mSpectrumSize);
00109                         mSparseKernelImagValues.push_back(-transformedHamming[j+1]/mSpectrumSize);
00110                 }
00111         }
00112         delete [] hammingWindow;
00113 }
00114 
00115 //-----------------------------------------------------------------------------
00116 void ConstantQTransform::doIt(const std::vector<double> & fftdata)
00117 {
00118         // Multiply by sparse kernels matrix and store in cqdata
00119         // N.B. complex data
00120         // rows = mSpectrumSize
00121         // columns = K
00122         double * constantQSpectrum = &cqdata[0];
00123         const double * fftSpectrum = &fftdata[0];
00124         for (unsigned row=0; row<2*K; row+=2) {
00125                 constantQSpectrum[row  ] = 0;
00126                 constantQSpectrum[row+1] = 0;
00127         }
00128         const unsigned *fftbin = &(mSparseKernelIs[0]);
00129         const unsigned *cqbin = &(mSparseKernelJs[0]);
00130         const double   *real = &(mSparseKernelRealValues[0]);
00131         const double   *imag = &(mSparseKernelImagValues[0]);
00132         const unsigned int nSparseCells = mSparseKernelRealValues.size();
00133         for (unsigned i = 0; i<nSparseCells; i++)
00134         {
00135                 const unsigned row = cqbin[i];
00136                 const unsigned col = fftbin[i];
00137                 const double & r1 = real[i];
00138                 const double & i1 = imag[i];
00139                 const double & r2 = fftSpectrum[col];
00140                 const double & i2 = fftSpectrum[col+1];
00141                 // add the multiplication
00142                 constantQSpectrum[row  ] += r1*r2 - i1*i2;
00143                 constantQSpectrum[row+1] += r1*i2 + i1*r2;
00144         }
00145 }
00146 
00147 }
00148 
00149 
Generated by  doxygen 1.6.3