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);