Mercurial > hg > qm-dsp
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 } |