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