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@276
|
15 #include "CQprecalc.cpp"
|
c@276
|
16
|
c@276
|
17 static bool push_precalculated(int uk, int fftlength,
|
c@276
|
18 std::vector<unsigned> &is,
|
c@276
|
19 std::vector<unsigned> &js,
|
c@276
|
20 std::vector<double> &real,
|
c@276
|
21 std::vector<double> &imag)
|
c@276
|
22 {
|
c@276
|
23 if (uk == 76 && fftlength == 16384) {
|
c@276
|
24 push_76_16384(is, js, real, imag);
|
c@276
|
25 return true;
|
c@276
|
26 }
|
c@276
|
27 if (uk == 144 && fftlength == 4096) {
|
c@276
|
28 push_144_4096(is, js, real, imag);
|
c@276
|
29 return true;
|
c@276
|
30 }
|
c@276
|
31 if (uk == 65 && fftlength == 2048) {
|
c@276
|
32 push_65_2048(is, js, real, imag);
|
c@276
|
33 return true;
|
c@276
|
34 }
|
c@276
|
35 if (uk == 84 && fftlength == 65536) {
|
c@276
|
36 push_84_65536(is, js, real, imag);
|
c@276
|
37 return true;
|
c@276
|
38 }
|
c@276
|
39 return false;
|
c@276
|
40 }
|
c@276
|
41
|
c@225
|
42 //---------------------------------------------------------------------------
|
c@225
|
43 // nextpow2 returns the smallest integer n such that 2^n >= x.
|
c@225
|
44 static double nextpow2(double x) {
|
c@225
|
45 double y = ceil(log(x)/log(2.0));
|
c@225
|
46 return(y);
|
c@225
|
47 }
|
c@225
|
48
|
c@225
|
49 static double squaredModule(const double & xx, const double & yy) {
|
c@225
|
50 return xx*xx + yy*yy;
|
c@225
|
51 }
|
c@225
|
52
|
c@225
|
53 //----------------------------------------------------------------------------
|
c@225
|
54
|
c@276
|
55 ConstantQ::ConstantQ( CQConfig Config ) :
|
c@276
|
56 m_sparseKernel(0)
|
c@225
|
57 {
|
c@225
|
58 initialise( Config );
|
c@225
|
59 }
|
c@225
|
60
|
c@225
|
61 ConstantQ::~ConstantQ()
|
c@225
|
62 {
|
c@225
|
63 deInitialise();
|
c@225
|
64 }
|
c@225
|
65
|
c@225
|
66 //----------------------------------------------------------------------------
|
c@225
|
67 void ConstantQ::sparsekernel()
|
c@225
|
68 {
|
c@276
|
69 // std::cerr << "ConstantQ: initialising sparse kernel, uK = " << m_uK << ", FFTLength = " << m_FFTLength << "...";
|
c@276
|
70
|
c@276
|
71 SparseKernel *sk = new SparseKernel();
|
c@276
|
72
|
c@276
|
73 if (push_precalculated(m_uK, m_FFTLength,
|
c@276
|
74 sk->is, sk->js, sk->real, sk->imag)) {
|
c@276
|
75 m_sparseKernel = sk;
|
c@276
|
76 return;
|
c@276
|
77 }
|
c@276
|
78
|
c@225
|
79 //generates spectral kernel matrix (upside down?)
|
c@225
|
80 // initialise temporal kernel with zeros, twice length to deal w. complex numbers
|
c@225
|
81
|
c@225
|
82 double* hammingWindowRe = new double [ m_FFTLength ];
|
c@225
|
83 double* hammingWindowIm = new double [ m_FFTLength ];
|
c@225
|
84 double* transfHammingWindowRe = new double [ m_FFTLength ];
|
c@225
|
85 double* transfHammingWindowIm = new double [ m_FFTLength ];
|
c@225
|
86
|
c@225
|
87 for (unsigned u=0; u < m_FFTLength; u++)
|
c@225
|
88 {
|
c@225
|
89 hammingWindowRe[u] = 0;
|
c@225
|
90 hammingWindowIm[u] = 0;
|
c@225
|
91 }
|
c@225
|
92
|
c@225
|
93 // Here, fftleng*2 is a guess of the number of sparse cells in the matrix
|
c@225
|
94 // The matrix K x fftlength but the non-zero cells are an antialiased
|
c@225
|
95 // square root function. So mostly is a line, with some grey point.
|
c@276
|
96 sk->is.reserve( m_FFTLength*2 );
|
c@276
|
97 sk->js.reserve( m_FFTLength*2 );
|
c@276
|
98 sk->real.reserve( m_FFTLength*2 );
|
c@276
|
99 sk->imag.reserve( m_FFTLength*2 );
|
c@225
|
100
|
c@225
|
101 // for each bin value K, calculate temporal kernel, take its fft to
|
c@225
|
102 //calculate the spectral kernel then threshold it to make it sparse and
|
c@225
|
103 //add it to the sparse kernels matrix
|
c@225
|
104 double squareThreshold = m_CQThresh * m_CQThresh;
|
c@225
|
105
|
c@225
|
106 FFT m_FFT;
|
c@225
|
107
|
c@225
|
108 for (unsigned k = m_uK; k--; )
|
c@225
|
109 {
|
c@228
|
110 for (unsigned u=0; u < m_FFTLength; u++)
|
c@228
|
111 {
|
c@228
|
112 hammingWindowRe[u] = 0;
|
c@228
|
113 hammingWindowIm[u] = 0;
|
c@228
|
114 }
|
c@228
|
115
|
c@225
|
116 // Computing a hamming window
|
c@225
|
117 const unsigned hammingLength = (int) ceil( m_dQ * m_FS / ( m_FMin * pow(2,((double)(k))/(double)m_BPO)));
|
c@228
|
118
|
c@228
|
119 unsigned origin = m_FFTLength/2 - hammingLength/2;
|
c@228
|
120
|
c@225
|
121 for (unsigned i=0; i<hammingLength; i++)
|
c@225
|
122 {
|
c@225
|
123 const double angle = 2*PI*m_dQ*i/hammingLength;
|
c@225
|
124 const double real = cos(angle);
|
c@225
|
125 const double imag = sin(angle);
|
c@225
|
126 const double absol = hamming(hammingLength, i)/hammingLength;
|
c@228
|
127 hammingWindowRe[ origin + i ] = absol*real;
|
c@228
|
128 hammingWindowIm[ origin + i ] = absol*imag;
|
c@225
|
129 }
|
c@225
|
130
|
c@228
|
131 for (unsigned i = 0; i < m_FFTLength/2; ++i) {
|
c@228
|
132 double temp = hammingWindowRe[i];
|
c@228
|
133 hammingWindowRe[i] = hammingWindowRe[i + m_FFTLength/2];
|
c@228
|
134 hammingWindowRe[i + m_FFTLength/2] = temp;
|
c@228
|
135 temp = hammingWindowIm[i];
|
c@228
|
136 hammingWindowIm[i] = hammingWindowIm[i + m_FFTLength/2];
|
c@228
|
137 hammingWindowIm[i + m_FFTLength/2] = temp;
|
c@228
|
138 }
|
c@228
|
139
|
c@225
|
140 //do fft of hammingWindow
|
c@225
|
141 m_FFT.process( m_FFTLength, 0, hammingWindowRe, hammingWindowIm, transfHammingWindowRe, transfHammingWindowIm );
|
c@225
|
142
|
c@225
|
143
|
c@225
|
144 for (unsigned j=0; j<( m_FFTLength ); j++)
|
c@225
|
145 {
|
c@225
|
146 // perform thresholding
|
c@225
|
147 const double squaredBin = squaredModule( transfHammingWindowRe[ j ], transfHammingWindowIm[ j ]);
|
c@225
|
148 if (squaredBin <= squareThreshold) continue;
|
c@225
|
149
|
c@225
|
150 // Insert non-zero position indexes, doubled because they are floats
|
c@276
|
151 sk->is.push_back(j);
|
c@276
|
152 sk->js.push_back(k);
|
c@225
|
153
|
c@225
|
154 // take conjugate, normalise and add to array sparkernel
|
c@276
|
155 sk->real.push_back( transfHammingWindowRe[ j ]/m_FFTLength);
|
c@276
|
156 sk->imag.push_back(-transfHammingWindowIm[ j ]/m_FFTLength);
|
c@225
|
157 }
|
c@225
|
158
|
c@225
|
159 }
|
c@225
|
160
|
c@225
|
161 delete [] hammingWindowRe;
|
c@225
|
162 delete [] hammingWindowIm;
|
c@225
|
163 delete [] transfHammingWindowRe;
|
c@225
|
164 delete [] transfHammingWindowIm;
|
c@225
|
165
|
c@276
|
166 /*
|
c@276
|
167 using std::cout;
|
c@276
|
168 using std::endl;
|
c@276
|
169
|
c@276
|
170 cout.precision(28);
|
c@276
|
171
|
c@276
|
172 int n = sk->is.size();
|
c@276
|
173 int w = 8;
|
c@276
|
174 cout << "static unsigned int sk_i_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl;
|
c@276
|
175 for (int i = 0; i < n; ++i) {
|
c@276
|
176 if (i % w == 0) cout << " ";
|
c@276
|
177 cout << sk->is[i];
|
c@276
|
178 if (i + 1 < n) cout << ", ";
|
c@276
|
179 if (i % w == w-1) cout << endl;
|
c@276
|
180 };
|
c@276
|
181 if (n % w != 0) cout << endl;
|
c@276
|
182 cout << "};" << endl;
|
c@276
|
183
|
c@276
|
184 n = sk->js.size();
|
c@276
|
185 cout << "static unsigned int sk_j_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl;
|
c@276
|
186 for (int i = 0; i < n; ++i) {
|
c@276
|
187 if (i % w == 0) cout << " ";
|
c@276
|
188 cout << sk->js[i];
|
c@276
|
189 if (i + 1 < n) cout << ", ";
|
c@276
|
190 if (i % w == w-1) cout << endl;
|
c@276
|
191 };
|
c@276
|
192 if (n % w != 0) cout << endl;
|
c@276
|
193 cout << "};" << endl;
|
c@276
|
194
|
c@276
|
195 w = 2;
|
c@276
|
196 n = sk->real.size();
|
c@276
|
197 cout << "static double sk_real_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl;
|
c@276
|
198 for (int i = 0; i < n; ++i) {
|
c@276
|
199 if (i % w == 0) cout << " ";
|
c@276
|
200 cout << sk->real[i];
|
c@276
|
201 if (i + 1 < n) cout << ", ";
|
c@276
|
202 if (i % w == w-1) cout << endl;
|
c@276
|
203 };
|
c@276
|
204 if (n % w != 0) cout << endl;
|
c@276
|
205 cout << "};" << endl;
|
c@276
|
206
|
c@276
|
207 n = sk->imag.size();
|
c@276
|
208 cout << "static double sk_imag_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl;
|
c@276
|
209 for (int i = 0; i < n; ++i) {
|
c@276
|
210 if (i % w == 0) cout << " ";
|
c@276
|
211 cout << sk->imag[i];
|
c@276
|
212 if (i + 1 < n) cout << ", ";
|
c@276
|
213 if (i % w == w-1) cout << endl;
|
c@276
|
214 };
|
c@276
|
215 if (n % w != 0) cout << endl;
|
c@276
|
216 cout << "};" << endl;
|
c@276
|
217
|
c@276
|
218 cout << "static void push_" << m_uK << "_" << m_FFTLength << "(vector<unsigned int> &is, vector<unsigned int> &js, vector<double> &real, vector<double> &imag)" << endl;
|
c@276
|
219 cout << "{\n is.reserve(" << n << ");\n";
|
c@276
|
220 cout << " js.reserve(" << n << ");\n";
|
c@276
|
221 cout << " real.reserve(" << n << ");\n";
|
c@276
|
222 cout << " imag.reserve(" << n << ");\n";
|
c@276
|
223 cout << " for (int i = 0; i < " << n << "; ++i) {" << endl;
|
c@276
|
224 cout << " is.push_back(sk_i_" << m_uK << "_" << m_FFTLength << "[i]);" << endl;
|
c@276
|
225 cout << " js.push_back(sk_j_" << m_uK << "_" << m_FFTLength << "[i]);" << endl;
|
c@276
|
226 cout << " real.push_back(sk_real_" << m_uK << "_" << m_FFTLength << "[i]);" << endl;
|
c@276
|
227 cout << " imag.push_back(sk_imag_" << m_uK << "_" << m_FFTLength << "[i]);" << endl;
|
c@276
|
228 cout << " }" << endl;
|
c@276
|
229 cout << "}" << endl;
|
c@276
|
230 */
|
c@276
|
231 // std::cerr << "done\n -> is: " << sk->is.size() << ", js: " << sk->js.size() << ", reals: " << sk->real.size() << ", imags: " << sk->imag.size() << std::endl;
|
c@276
|
232
|
c@276
|
233 m_sparseKernel = sk;
|
c@276
|
234 return;
|
c@225
|
235 }
|
c@225
|
236
|
c@225
|
237 //-----------------------------------------------------------------------------
|
c@257
|
238 double* ConstantQ::process( const double* fftdata )
|
c@225
|
239 {
|
c@276
|
240 if (!m_sparseKernel) {
|
c@276
|
241 std::cerr << "ERROR: ConstantQ::process: Sparse kernel has not been initialised" << std::endl;
|
c@276
|
242 return m_CQdata;
|
c@276
|
243 }
|
c@276
|
244
|
c@276
|
245 SparseKernel *sk = m_sparseKernel;
|
c@276
|
246
|
c@225
|
247 for (unsigned row=0; row<2*m_uK; row++)
|
c@225
|
248 {
|
c@225
|
249 m_CQdata[ row ] = 0;
|
c@225
|
250 m_CQdata[ row+1 ] = 0;
|
c@225
|
251 }
|
c@276
|
252 const unsigned *fftbin = &(sk->is[0]);
|
c@276
|
253 const unsigned *cqbin = &(sk->js[0]);
|
c@276
|
254 const double *real = &(sk->real[0]);
|
c@276
|
255 const double *imag = &(sk->imag[0]);
|
c@276
|
256 const unsigned int sparseCells = sk->real.size();
|
c@225
|
257
|
c@225
|
258 for (unsigned i = 0; i<sparseCells; i++)
|
c@225
|
259 {
|
c@225
|
260 const unsigned row = cqbin[i];
|
c@225
|
261 const unsigned col = fftbin[i];
|
c@225
|
262 const double & r1 = real[i];
|
c@225
|
263 const double & i1 = imag[i];
|
c@263
|
264 const double & r2 = fftdata[ (2*m_FFTLength) - 2*col - 2 ];
|
c@263
|
265 const double & i2 = fftdata[ (2*m_FFTLength) - 2*col - 2 + 1 ];
|
c@225
|
266 // add the multiplication
|
c@225
|
267 m_CQdata[ 2*row ] += (r1*r2 - i1*i2);
|
c@225
|
268 m_CQdata[ 2*row+1] += (r1*i2 + i1*r2);
|
c@225
|
269 }
|
c@225
|
270
|
c@225
|
271 return m_CQdata;
|
c@225
|
272 }
|
c@225
|
273
|
c@225
|
274
|
c@225
|
275 void ConstantQ::initialise( CQConfig Config )
|
c@225
|
276 {
|
c@225
|
277 m_FS = Config.FS;
|
c@225
|
278 m_FMin = Config.min; // min freq
|
c@225
|
279 m_FMax = Config.max; // max freq
|
c@225
|
280 m_BPO = Config.BPO; // bins per octave
|
c@225
|
281 m_CQThresh = Config.CQThresh;// ConstantQ threshold for kernel generation
|
c@225
|
282
|
c@225
|
283 m_dQ = 1/(pow(2,(1/(double)m_BPO))-1); // Work out Q value for Filter bank
|
c@225
|
284 m_uK = (unsigned int) ceil(m_BPO * log(m_FMax/m_FMin)/log(2.0)); // No. of constant Q bins
|
c@225
|
285
|
c@249
|
286 // 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
|
287
|
c@225
|
288 // work out length of fft required for this constant Q Filter bank
|
c@225
|
289 m_FFTLength = (int) pow(2, nextpow2(ceil( m_dQ*m_FS/m_FMin )));
|
c@225
|
290
|
c@225
|
291 m_hop = m_FFTLength/8; // <------ hop size is window length divided by 32
|
c@225
|
292
|
c@249
|
293 // std::cerr << "ConstantQ::initialise: -> fft length = " << m_FFTLength << ", hop = " << m_hop << std::endl;
|
c@245
|
294
|
c@225
|
295 // allocate memory for cqdata
|
c@225
|
296 m_CQdata = new double [2*m_uK];
|
c@225
|
297 }
|
c@225
|
298
|
c@225
|
299 void ConstantQ::deInitialise()
|
c@225
|
300 {
|
c@225
|
301 delete [] m_CQdata;
|
c@276
|
302 delete m_sparseKernel;
|
c@225
|
303 }
|
c@225
|
304
|
c@257
|
305 void ConstantQ::process(const double *FFTRe, const double* FFTIm,
|
c@257
|
306 double *CQRe, double *CQIm)
|
c@225
|
307 {
|
c@276
|
308 if (!m_sparseKernel) {
|
c@276
|
309 std::cerr << "ERROR: ConstantQ::process: Sparse kernel has not been initialised" << std::endl;
|
c@276
|
310 return;
|
c@276
|
311 }
|
c@276
|
312
|
c@276
|
313 SparseKernel *sk = m_sparseKernel;
|
c@276
|
314
|
c@225
|
315 for (unsigned row=0; row<m_uK; row++)
|
c@225
|
316 {
|
c@225
|
317 CQRe[ row ] = 0;
|
c@225
|
318 CQIm[ row ] = 0;
|
c@225
|
319 }
|
c@225
|
320
|
c@276
|
321 const unsigned *fftbin = &(sk->is[0]);
|
c@276
|
322 const unsigned *cqbin = &(sk->js[0]);
|
c@276
|
323 const double *real = &(sk->real[0]);
|
c@276
|
324 const double *imag = &(sk->imag[0]);
|
c@276
|
325 const unsigned int sparseCells = sk->real.size();
|
c@225
|
326
|
c@225
|
327 for (unsigned i = 0; i<sparseCells; i++)
|
c@225
|
328 {
|
c@225
|
329 const unsigned row = cqbin[i];
|
c@225
|
330 const unsigned col = fftbin[i];
|
c@225
|
331 const double & r1 = real[i];
|
c@225
|
332 const double & i1 = imag[i];
|
c@263
|
333 const double & r2 = FFTRe[ m_FFTLength - col - 1 ];
|
c@263
|
334 const double & i2 = FFTIm[ m_FFTLength - col - 1 ];
|
c@225
|
335 // add the multiplication
|
c@225
|
336 CQRe[ row ] += (r1*r2 - i1*i2);
|
c@225
|
337 CQIm[ row ] += (r1*i2 + i1*r2);
|
c@225
|
338 }
|
c@225
|
339 }
|