1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
5 Centre for Digital Music, Queen Mary, University of London.
6 This file 2005-2006 Christian Landone.
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version. See the file
12 COPYING included with this distribution for more information.
15 #include "ConstantQ.h"
16 #include "dsp/transforms/FFT.h"
21 // see note in CQprecalc
23 #include "CQprecalc.cpp"
25 static bool push_precalculated(int uk
, int fftlength
,
26 std::vector
<unsigned> &is
,
27 std::vector
<unsigned> &js
,
28 std::vector
<double> &real
,
29 std::vector
<double> &imag
)
31 if (uk
== 76 && fftlength
== 16384) {
32 push_76_16384(is
, js
, real
, imag
);
35 if (uk
== 144 && fftlength
== 4096) {
36 push_144_4096(is
, js
, real
, imag
);
39 if (uk
== 65 && fftlength
== 2048) {
40 push_65_2048(is
, js
, real
, imag
);
43 if (uk
== 84 && fftlength
== 65536) {
44 push_84_65536(is
, js
, real
, imag
);
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));
58 static double squaredModule(const double & xx
, const double & yy
) {
62 //----------------------------------------------------------------------------
64 ConstantQ::ConstantQ( CQConfig Config
) :
70 ConstantQ::~ConstantQ()
75 //----------------------------------------------------------------------------
76 void ConstantQ::sparsekernel()
78 // std::cerr << "ConstantQ: initialising sparse kernel, uK = " << m_uK << ", FFTLength = " << m_FFTLength << "...";
80 SparseKernel
*sk
= new SparseKernel();
83 if (push_precalculated(m_uK
, m_FFTLength
,
84 sk
->is
, sk
->js
, sk
->real
, sk
->imag
)) {
85 // std::cerr << "using precalculated kernel" << std::endl;
91 //generates spectral kernel matrix (upside down?)
92 // initialise temporal kernel with zeros, twice length to deal w. complex numbers
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
];
99 for (unsigned u
=0; u
< m_FFTLength
; u
++)
101 hammingWindowRe
[u
] = 0;
102 hammingWindowIm
[u
] = 0;
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 );
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
;
118 FFT
m_FFT(m_FFTLength
);
120 for (unsigned k
= m_uK
; k
--; )
122 for (unsigned u
=0; u
< m_FFTLength
; u
++)
124 hammingWindowRe
[u
] = 0;
125 hammingWindowIm
[u
] = 0;
128 // Computing a hamming window
129 const unsigned hammingLength
= (int) ceil( m_dQ
* m_FS
/ ( m_FMin
* pow(2,((double)(k
))/(double)m_BPO
)));
131 unsigned origin
= m_FFTLength
/2 - hammingLength
/2;
133 for (unsigned i
=0; i
<hammingLength
; i
++)
135 const double angle
= 2*PI
*m_dQ
*i
/hammingLength
;
136 const double real
= cos(angle
);
137 const double imag
= sin(angle
);
138 const double absol
= hamming(hammingLength
, i
)/hammingLength
;
139 hammingWindowRe
[ origin
+ i
] = absol
*real
;
140 hammingWindowIm
[ origin
+ i
] = absol
*imag
;
143 for (unsigned i
= 0; i
< m_FFTLength
/2; ++i
) {
144 double temp
= hammingWindowRe
[i
];
145 hammingWindowRe
[i
] = hammingWindowRe
[i
+ m_FFTLength
/2];
146 hammingWindowRe
[i
+ m_FFTLength
/2] = temp
;
147 temp
= hammingWindowIm
[i
];
148 hammingWindowIm
[i
] = hammingWindowIm
[i
+ m_FFTLength
/2];
149 hammingWindowIm
[i
+ m_FFTLength
/2] = temp
;
152 //do fft of hammingWindow
153 m_FFT
.process( 0, hammingWindowRe
, hammingWindowIm
, transfHammingWindowRe
, transfHammingWindowIm
);
156 for (unsigned j
=0; j
<( m_FFTLength
); j
++)
158 // perform thresholding
159 const double squaredBin
= squaredModule( transfHammingWindowRe
[ j
], transfHammingWindowIm
[ j
]);
160 if (squaredBin
<= squareThreshold
) continue;
162 // Insert non-zero position indexes, doubled because they are floats
166 // take conjugate, normalise and add to array sparkernel
167 sk
->real
.push_back( transfHammingWindowRe
[ j
]/m_FFTLength
);
168 sk
->imag
.push_back(-transfHammingWindowIm
[ j
]/m_FFTLength
);
173 delete [] hammingWindowRe
;
174 delete [] hammingWindowIm
;
175 delete [] transfHammingWindowRe
;
176 delete [] transfHammingWindowIm
;
184 int n = sk->is.size();
186 cout << "static unsigned int sk_i_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl;
187 for (int i = 0; i < n; ++i) {
188 if (i % w == 0) cout << " ";
190 if (i + 1 < n) cout << ", ";
191 if (i % w == w-1) cout << endl;
193 if (n % w != 0) cout << endl;
194 cout << "};" << endl;
197 cout << "static unsigned int sk_j_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl;
198 for (int i = 0; i < n; ++i) {
199 if (i % w == 0) cout << " ";
201 if (i + 1 < n) cout << ", ";
202 if (i % w == w-1) cout << endl;
204 if (n % w != 0) cout << endl;
205 cout << "};" << endl;
209 cout << "static double sk_real_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl;
210 for (int i = 0; i < n; ++i) {
211 if (i % w == 0) cout << " ";
213 if (i + 1 < n) cout << ", ";
214 if (i % w == w-1) cout << endl;
216 if (n % w != 0) cout << endl;
217 cout << "};" << endl;
220 cout << "static double sk_imag_" << m_uK << "_" << m_FFTLength << "[" << n << "] = {" << endl;
221 for (int i = 0; i < n; ++i) {
222 if (i % w == 0) cout << " ";
224 if (i + 1 < n) cout << ", ";
225 if (i % w == w-1) cout << endl;
227 if (n % w != 0) cout << endl;
228 cout << "};" << endl;
230 cout << "static void push_" << m_uK << "_" << m_FFTLength << "(vector<unsigned int> &is, vector<unsigned int> &js, vector<double> &real, vector<double> &imag)" << endl;
231 cout << "{\n is.reserve(" << n << ");\n";
232 cout << " js.reserve(" << n << ");\n";
233 cout << " real.reserve(" << n << ");\n";
234 cout << " imag.reserve(" << n << ");\n";
235 cout << " for (int i = 0; i < " << n << "; ++i) {" << endl;
236 cout << " is.push_back(sk_i_" << m_uK << "_" << m_FFTLength << "[i]);" << endl;
237 cout << " js.push_back(sk_j_" << m_uK << "_" << m_FFTLength << "[i]);" << endl;
238 cout << " real.push_back(sk_real_" << m_uK << "_" << m_FFTLength << "[i]);" << endl;
239 cout << " imag.push_back(sk_imag_" << m_uK << "_" << m_FFTLength << "[i]);" << endl;
240 cout << " }" << endl;
243 // std::cerr << "done\n -> is: " << sk->is.size() << ", js: " << sk->js.size() << ", reals: " << sk->real.size() << ", imags: " << sk->imag.size() << std::endl;
249 //-----------------------------------------------------------------------------
250 double* ConstantQ::process( const double* fftdata
)
252 if (!m_sparseKernel
) {
253 std::cerr
<< "ERROR: ConstantQ::process: Sparse kernel has not been initialised" << std::endl
;
257 SparseKernel
*sk
= m_sparseKernel
;
259 for (unsigned row
=0; row
<2*m_uK
; row
++)
262 m_CQdata
[ row
+1 ] = 0;
264 const unsigned *fftbin
= &(sk
->is
[0]);
265 const unsigned *cqbin
= &(sk
->js
[0]);
266 const double *real
= &(sk
->real
[0]);
267 const double *imag
= &(sk
->imag
[0]);
268 const unsigned int sparseCells
= sk
->real
.size();
270 for (unsigned i
= 0; i
<sparseCells
; i
++)
272 const unsigned row
= cqbin
[i
];
273 const unsigned col
= fftbin
[i
];
274 const double & r1
= real
[i
];
275 const double & i1
= imag
[i
];
276 const double & r2
= fftdata
[ (2*m_FFTLength
) - 2*col
- 2 ];
277 const double & i2
= fftdata
[ (2*m_FFTLength
) - 2*col
- 2 + 1 ];
278 // add the multiplication
279 m_CQdata
[ 2*row
] += (r1
*r2
- i1
*i2
);
280 m_CQdata
[ 2*row
+1] += (r1
*i2
+ i1
*r2
);
287 void ConstantQ::initialise( CQConfig Config
)
290 m_FMin
= Config
.min
; // min freq
291 m_FMax
= Config
.max
; // max freq
292 m_BPO
= Config
.BPO
; // bins per octave
293 m_CQThresh
= Config
.CQThresh
;// ConstantQ threshold for kernel generation
295 m_dQ
= 1/(pow(2,(1/(double)m_BPO
))-1); // Work out Q value for Filter bank
296 m_uK
= (unsigned int) ceil(m_BPO
* log(m_FMax
/m_FMin
)/log(2.0)); // No. of constant Q bins
298 // std::cerr << "ConstantQ::initialise: rate = " << m_FS << ", fmin = " << m_FMin << ", fmax = " << m_FMax << ", bpo = " << m_BPO << ", K = " << m_uK << ", Q = " << m_dQ << std::endl;
300 // work out length of fft required for this constant Q Filter bank
301 m_FFTLength
= (int) pow(2, nextpow2(ceil( m_dQ
*m_FS
/m_FMin
)));
303 m_hop
= m_FFTLength
/8; // <------ hop size is window length divided by 32
305 // std::cerr << "ConstantQ::initialise: -> fft length = " << m_FFTLength << ", hop = " << m_hop << std::endl;
307 // allocate memory for cqdata
308 m_CQdata
= new double [2*m_uK
];
311 void ConstantQ::deInitialise()
314 delete m_sparseKernel
;
317 void ConstantQ::process(const double *FFTRe
, const double* FFTIm
,
318 double *CQRe
, double *CQIm
)
320 if (!m_sparseKernel
) {
321 std::cerr
<< "ERROR: ConstantQ::process: Sparse kernel has not been initialised" << std::endl
;
325 SparseKernel
*sk
= m_sparseKernel
;
327 for (unsigned row
=0; row
<m_uK
; row
++)
333 const unsigned *fftbin
= &(sk
->is
[0]);
334 const unsigned *cqbin
= &(sk
->js
[0]);
335 const double *real
= &(sk
->real
[0]);
336 const double *imag
= &(sk
->imag
[0]);
337 const unsigned int sparseCells
= sk
->real
.size();
339 for (unsigned i
= 0; i
<sparseCells
; i
++)
341 const unsigned row
= cqbin
[i
];
342 const unsigned col
= fftbin
[i
];
343 const double & r1
= real
[i
];
344 const double & i1
= imag
[i
];
345 const double & r2
= FFTRe
[ m_FFTLength
- col
- 1 ];
346 const double & i2
= FFTIm
[ m_FFTLength
- col
- 1 ];
347 // add the multiplication
348 CQRe
[ row
] += (r1
*r2
- i1
*i2
);
349 CQIm
[ row
] += (r1
*i2
+ i1
*r2
);