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@225
|
6 This file copyright 2005-2006 Christian Landone.
|
c@225
|
7 All rights reserved.
|
c@225
|
8 */
|
c@225
|
9
|
c@225
|
10 #include "ConstantQ.h"
|
c@225
|
11 #include "dsp/transforms/FFT.h"
|
c@225
|
12
|
c@245
|
13 #include <iostream>
|
c@245
|
14
|
c@225
|
15 //---------------------------------------------------------------------------
|
c@225
|
16 // nextpow2 returns the smallest integer n such that 2^n >= x.
|
c@225
|
17 static double nextpow2(double x) {
|
c@225
|
18 double y = ceil(log(x)/log(2.0));
|
c@225
|
19 return(y);
|
c@225
|
20 }
|
c@225
|
21
|
c@225
|
22 static double squaredModule(const double & xx, const double & yy) {
|
c@225
|
23 return xx*xx + yy*yy;
|
c@225
|
24 }
|
c@225
|
25
|
c@225
|
26 //----------------------------------------------------------------------------
|
c@225
|
27
|
c@225
|
28 ConstantQ::ConstantQ( CQConfig Config )
|
c@225
|
29 {
|
c@225
|
30 initialise( Config );
|
c@225
|
31 }
|
c@225
|
32
|
c@225
|
33 ConstantQ::~ConstantQ()
|
c@225
|
34 {
|
c@225
|
35 deInitialise();
|
c@225
|
36 }
|
c@225
|
37
|
c@225
|
38 //----------------------------------------------------------------------------
|
c@225
|
39 void ConstantQ::sparsekernel()
|
c@225
|
40 {
|
c@225
|
41 //generates spectral kernel matrix (upside down?)
|
c@225
|
42 // initialise temporal kernel with zeros, twice length to deal w. complex numbers
|
c@225
|
43
|
c@225
|
44 double* hammingWindowRe = new double [ m_FFTLength ];
|
c@225
|
45 double* hammingWindowIm = new double [ m_FFTLength ];
|
c@225
|
46 double* transfHammingWindowRe = new double [ m_FFTLength ];
|
c@225
|
47 double* transfHammingWindowIm = new double [ m_FFTLength ];
|
c@225
|
48
|
c@225
|
49 for (unsigned u=0; u < m_FFTLength; u++)
|
c@225
|
50 {
|
c@225
|
51 hammingWindowRe[u] = 0;
|
c@225
|
52 hammingWindowIm[u] = 0;
|
c@225
|
53 }
|
c@225
|
54
|
c@225
|
55
|
c@225
|
56 // Here, fftleng*2 is a guess of the number of sparse cells in the matrix
|
c@225
|
57 // The matrix K x fftlength but the non-zero cells are an antialiased
|
c@225
|
58 // square root function. So mostly is a line, with some grey point.
|
c@225
|
59 m_sparseKernelIs.reserve( m_FFTLength*2 );
|
c@225
|
60 m_sparseKernelJs.reserve( m_FFTLength*2 );
|
c@225
|
61 m_sparseKernelRealValues.reserve( m_FFTLength*2 );
|
c@225
|
62 m_sparseKernelImagValues.reserve( m_FFTLength*2 );
|
c@225
|
63
|
c@225
|
64 // for each bin value K, calculate temporal kernel, take its fft to
|
c@225
|
65 //calculate the spectral kernel then threshold it to make it sparse and
|
c@225
|
66 //add it to the sparse kernels matrix
|
c@225
|
67 double squareThreshold = m_CQThresh * m_CQThresh;
|
c@225
|
68
|
c@225
|
69 FFT m_FFT;
|
c@225
|
70
|
c@225
|
71 for (unsigned k = m_uK; k--; )
|
c@225
|
72 {
|
c@228
|
73 for (unsigned u=0; u < m_FFTLength; u++)
|
c@228
|
74 {
|
c@228
|
75 hammingWindowRe[u] = 0;
|
c@228
|
76 hammingWindowIm[u] = 0;
|
c@228
|
77 }
|
c@228
|
78
|
c@225
|
79 // Computing a hamming window
|
c@225
|
80 const unsigned hammingLength = (int) ceil( m_dQ * m_FS / ( m_FMin * pow(2,((double)(k))/(double)m_BPO)));
|
c@228
|
81
|
c@228
|
82 unsigned origin = m_FFTLength/2 - hammingLength/2;
|
c@228
|
83
|
c@225
|
84 for (unsigned i=0; i<hammingLength; i++)
|
c@225
|
85 {
|
c@225
|
86 const double angle = 2*PI*m_dQ*i/hammingLength;
|
c@225
|
87 const double real = cos(angle);
|
c@225
|
88 const double imag = sin(angle);
|
c@225
|
89 const double absol = hamming(hammingLength, i)/hammingLength;
|
c@228
|
90 hammingWindowRe[ origin + i ] = absol*real;
|
c@228
|
91 hammingWindowIm[ origin + i ] = absol*imag;
|
c@225
|
92 }
|
c@225
|
93
|
c@228
|
94 for (unsigned i = 0; i < m_FFTLength/2; ++i) {
|
c@228
|
95 double temp = hammingWindowRe[i];
|
c@228
|
96 hammingWindowRe[i] = hammingWindowRe[i + m_FFTLength/2];
|
c@228
|
97 hammingWindowRe[i + m_FFTLength/2] = temp;
|
c@228
|
98 temp = hammingWindowIm[i];
|
c@228
|
99 hammingWindowIm[i] = hammingWindowIm[i + m_FFTLength/2];
|
c@228
|
100 hammingWindowIm[i + m_FFTLength/2] = temp;
|
c@228
|
101 }
|
c@228
|
102
|
c@225
|
103 //do fft of hammingWindow
|
c@225
|
104 m_FFT.process( m_FFTLength, 0, hammingWindowRe, hammingWindowIm, transfHammingWindowRe, transfHammingWindowIm );
|
c@225
|
105
|
c@225
|
106
|
c@225
|
107 for (unsigned j=0; j<( m_FFTLength ); j++)
|
c@225
|
108 {
|
c@225
|
109 // perform thresholding
|
c@225
|
110 const double squaredBin = squaredModule( transfHammingWindowRe[ j ], transfHammingWindowIm[ j ]);
|
c@225
|
111 if (squaredBin <= squareThreshold) continue;
|
c@225
|
112
|
c@225
|
113 // Insert non-zero position indexes, doubled because they are floats
|
c@225
|
114 m_sparseKernelIs.push_back(j);
|
c@225
|
115 m_sparseKernelJs.push_back(k);
|
c@225
|
116
|
c@225
|
117 // take conjugate, normalise and add to array sparkernel
|
c@225
|
118 m_sparseKernelRealValues.push_back( transfHammingWindowRe[ j ]/m_FFTLength);
|
c@225
|
119 m_sparseKernelImagValues.push_back(-transfHammingWindowIm[ j ]/m_FFTLength);
|
c@225
|
120 }
|
c@225
|
121
|
c@225
|
122 }
|
c@225
|
123
|
c@225
|
124 delete [] hammingWindowRe;
|
c@225
|
125 delete [] hammingWindowIm;
|
c@225
|
126 delete [] transfHammingWindowRe;
|
c@225
|
127 delete [] transfHammingWindowIm;
|
c@225
|
128
|
c@225
|
129 }
|
c@225
|
130
|
c@225
|
131 //-----------------------------------------------------------------------------
|
c@225
|
132 double* ConstantQ::process( double* fftdata )
|
c@225
|
133 {
|
c@225
|
134 for (unsigned row=0; row<2*m_uK; row++)
|
c@225
|
135 {
|
c@225
|
136 m_CQdata[ row ] = 0;
|
c@225
|
137 m_CQdata[ row+1 ] = 0;
|
c@225
|
138 }
|
c@225
|
139 const unsigned *fftbin = &(m_sparseKernelIs[0]);
|
c@225
|
140 const unsigned *cqbin = &(m_sparseKernelJs[0]);
|
c@225
|
141 const double *real = &(m_sparseKernelRealValues[0]);
|
c@225
|
142 const double *imag = &(m_sparseKernelImagValues[0]);
|
c@225
|
143 const unsigned int sparseCells = m_sparseKernelRealValues.size();
|
c@225
|
144
|
c@225
|
145 for (unsigned i = 0; i<sparseCells; i++)
|
c@225
|
146 {
|
c@225
|
147 const unsigned row = cqbin[i];
|
c@225
|
148 const unsigned col = fftbin[i];
|
c@225
|
149 const double & r1 = real[i];
|
c@225
|
150 const double & i1 = imag[i];
|
c@225
|
151 const double & r2 = fftdata[ (2*m_FFTLength) - 2*col];
|
c@225
|
152 const double & i2 = fftdata[ (2*m_FFTLength) - 2*col+1];
|
c@225
|
153 // add the multiplication
|
c@225
|
154 m_CQdata[ 2*row ] += (r1*r2 - i1*i2);
|
c@225
|
155 m_CQdata[ 2*row+1] += (r1*i2 + i1*r2);
|
c@225
|
156 }
|
c@225
|
157
|
c@225
|
158 return m_CQdata;
|
c@225
|
159 }
|
c@225
|
160
|
c@225
|
161
|
c@225
|
162 void ConstantQ::initialise( CQConfig Config )
|
c@225
|
163 {
|
c@225
|
164 m_FS = Config.FS;
|
c@225
|
165 m_FMin = Config.min; // min freq
|
c@225
|
166 m_FMax = Config.max; // max freq
|
c@225
|
167 m_BPO = Config.BPO; // bins per octave
|
c@225
|
168 m_CQThresh = Config.CQThresh;// ConstantQ threshold for kernel generation
|
c@225
|
169
|
c@225
|
170 m_dQ = 1/(pow(2,(1/(double)m_BPO))-1); // Work out Q value for Filter bank
|
c@225
|
171 m_uK = (unsigned int) ceil(m_BPO * log(m_FMax/m_FMin)/log(2.0)); // No. of constant Q bins
|
c@225
|
172
|
c@245
|
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;
|
c@245
|
174
|
c@225
|
175 // work out length of fft required for this constant Q Filter bank
|
c@225
|
176 m_FFTLength = (int) pow(2, nextpow2(ceil( m_dQ*m_FS/m_FMin )));
|
c@225
|
177
|
c@225
|
178 m_hop = m_FFTLength/8; // <------ hop size is window length divided by 32
|
c@225
|
179
|
c@245
|
180 std::cerr << "ConstantQ::initialise: -> fft length = " << m_FFTLength << ", hop = " << m_hop << std::endl;
|
c@245
|
181
|
c@225
|
182 // allocate memory for cqdata
|
c@225
|
183 m_CQdata = new double [2*m_uK];
|
c@225
|
184 }
|
c@225
|
185
|
c@225
|
186 void ConstantQ::deInitialise()
|
c@225
|
187 {
|
c@225
|
188 delete [] m_CQdata;
|
c@225
|
189 }
|
c@225
|
190
|
c@225
|
191 void ConstantQ::process(double *FFTRe, double* FFTIm, double *CQRe, double *CQIm)
|
c@225
|
192 {
|
c@225
|
193 for (unsigned row=0; row<m_uK; row++)
|
c@225
|
194 {
|
c@225
|
195 CQRe[ row ] = 0;
|
c@225
|
196 CQIm[ row ] = 0;
|
c@225
|
197 }
|
c@225
|
198
|
c@225
|
199 const unsigned *fftbin = &(m_sparseKernelIs[0]);
|
c@225
|
200 const unsigned *cqbin = &(m_sparseKernelJs[0]);
|
c@225
|
201 const double *real = &(m_sparseKernelRealValues[0]);
|
c@225
|
202 const double *imag = &(m_sparseKernelImagValues[0]);
|
c@225
|
203 const unsigned int sparseCells = m_sparseKernelRealValues.size();
|
c@225
|
204
|
c@225
|
205 for (unsigned i = 0; i<sparseCells; i++)
|
c@225
|
206 {
|
c@225
|
207 const unsigned row = cqbin[i];
|
c@225
|
208 const unsigned col = fftbin[i];
|
c@225
|
209 const double & r1 = real[i];
|
c@225
|
210 const double & i1 = imag[i];
|
c@225
|
211 const double & r2 = FFTRe[ m_FFTLength- col];
|
c@225
|
212 const double & i2 = FFTIm[ m_FFTLength - col];
|
c@225
|
213 // add the multiplication
|
c@225
|
214 CQRe[ row ] += (r1*r2 - i1*i2);
|
c@225
|
215 CQIm[ row ] += (r1*i2 + i1*r2);
|
c@225
|
216 }
|
c@225
|
217 }
|