![]() |
Mixxx
|
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