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@309
|
6 This file 2005-2006 Christian Landone.
|
c@309
|
7
|
c@309
|
8 This program is free software; you can redistribute it and/or
|
c@309
|
9 modify it under the terms of the GNU General Public License as
|
c@309
|
10 published by the Free Software Foundation; either version 2 of the
|
c@309
|
11 License, or (at your option) any later version. See the file
|
c@309
|
12 COPYING included with this distribution for more information.
|
c@225
|
13 */
|
c@225
|
14
|
c@225
|
15 #include "ConstantQ.h"
|
c@225
|
16 #include "dsp/transforms/FFT.h"
|
c@225
|
17
|
c@245
|
18 #include <iostream>
|
c@245
|
19
|
c@225
|
20 //---------------------------------------------------------------------------
|
c@225
|
21 // nextpow2 returns the smallest integer n such that 2^n >= x.
|
c@225
|
22 static double nextpow2(double x) {
|
c@225
|
23 double y = ceil(log(x)/log(2.0));
|
c@225
|
24 return(y);
|
c@225
|
25 }
|
c@225
|
26
|
c@225
|
27 static double squaredModule(const double & xx, const double & yy) {
|
c@225
|
28 return xx*xx + yy*yy;
|
c@225
|
29 }
|
c@225
|
30
|
c@225
|
31 //----------------------------------------------------------------------------
|
c@225
|
32
|
c@276
|
33 ConstantQ::ConstantQ( CQConfig Config ) :
|
c@276
|
34 m_sparseKernel(0)
|
c@225
|
35 {
|
c@225
|
36 initialise( Config );
|
c@225
|
37 }
|
c@225
|
38
|
c@225
|
39 ConstantQ::~ConstantQ()
|
c@225
|
40 {
|
c@225
|
41 deInitialise();
|
c@225
|
42 }
|
c@225
|
43
|
c@225
|
44 //----------------------------------------------------------------------------
|
c@225
|
45 void ConstantQ::sparsekernel()
|
c@225
|
46 {
|
c@276
|
47 // std::cerr << "ConstantQ: initialising sparse kernel, uK = " << m_uK << ", FFTLength = " << m_FFTLength << "...";
|
c@276
|
48
|
c@276
|
49 SparseKernel *sk = new SparseKernel();
|
c@276
|
50
|
c@225
|
51 //generates spectral kernel matrix (upside down?)
|
c@225
|
52 // initialise temporal kernel with zeros, twice length to deal w. complex numbers
|
c@225
|
53
|
c@225
|
54 double* hammingWindowRe = new double [ m_FFTLength ];
|
c@225
|
55 double* hammingWindowIm = new double [ m_FFTLength ];
|
c@225
|
56 double* transfHammingWindowRe = new double [ m_FFTLength ];
|
c@225
|
57 double* transfHammingWindowIm = new double [ m_FFTLength ];
|
c@225
|
58
|
cannam@483
|
59 for (unsigned u=0; u < m_FFTLength; u++) {
|
cannam@483
|
60 hammingWindowRe[u] = 0;
|
cannam@483
|
61 hammingWindowIm[u] = 0;
|
c@225
|
62 }
|
c@225
|
63
|
c@225
|
64 // Here, fftleng*2 is a guess of the number of sparse cells in the matrix
|
c@225
|
65 // The matrix K x fftlength but the non-zero cells are an antialiased
|
c@225
|
66 // square root function. So mostly is a line, with some grey point.
|
c@276
|
67 sk->is.reserve( m_FFTLength*2 );
|
c@276
|
68 sk->js.reserve( m_FFTLength*2 );
|
c@276
|
69 sk->real.reserve( m_FFTLength*2 );
|
c@276
|
70 sk->imag.reserve( m_FFTLength*2 );
|
cannam@483
|
71
|
c@225
|
72 // for each bin value K, calculate temporal kernel, take its fft to
|
c@225
|
73 //calculate the spectral kernel then threshold it to make it sparse and
|
c@225
|
74 //add it to the sparse kernels matrix
|
c@225
|
75 double squareThreshold = m_CQThresh * m_CQThresh;
|
c@225
|
76
|
c@289
|
77 FFT m_FFT(m_FFTLength);
|
cannam@483
|
78
|
cannam@483
|
79 for (unsigned k = m_uK; k--; ) {
|
cannam@483
|
80 for (unsigned u=0; u < m_FFTLength; u++) {
|
c@228
|
81 hammingWindowRe[u] = 0;
|
c@228
|
82 hammingWindowIm[u] = 0;
|
c@228
|
83 }
|
c@228
|
84
|
cannam@483
|
85 // Computing a hamming window
|
cannam@483
|
86 const unsigned hammingLength = (int) ceil( m_dQ * m_FS / ( m_FMin * pow(2,((double)(k))/(double)m_BPO)));
|
c@228
|
87
|
cannam@469
|
88 // 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;
|
cannam@469
|
89
|
cannam@469
|
90
|
c@228
|
91 unsigned origin = m_FFTLength/2 - hammingLength/2;
|
c@228
|
92
|
cannam@483
|
93 for (unsigned i=0; i<hammingLength; i++) {
|
cannam@483
|
94 const double angle = 2*PI*m_dQ*i/hammingLength;
|
cannam@483
|
95 const double real = cos(angle);
|
cannam@483
|
96 const double imag = sin(angle);
|
cannam@483
|
97 const double absol = hamming(hammingLength, i)/hammingLength;
|
cannam@483
|
98 hammingWindowRe[ origin + i ] = absol*real;
|
cannam@483
|
99 hammingWindowIm[ origin + i ] = absol*imag;
|
cannam@483
|
100 }
|
c@225
|
101
|
c@228
|
102 for (unsigned i = 0; i < m_FFTLength/2; ++i) {
|
c@228
|
103 double temp = hammingWindowRe[i];
|
c@228
|
104 hammingWindowRe[i] = hammingWindowRe[i + m_FFTLength/2];
|
c@228
|
105 hammingWindowRe[i + m_FFTLength/2] = temp;
|
c@228
|
106 temp = hammingWindowIm[i];
|
c@228
|
107 hammingWindowIm[i] = hammingWindowIm[i + m_FFTLength/2];
|
c@228
|
108 hammingWindowIm[i + m_FFTLength/2] = temp;
|
c@228
|
109 }
|
c@228
|
110
|
cannam@483
|
111 //do fft of hammingWindow
|
cannam@483
|
112 m_FFT.process( 0, hammingWindowRe, hammingWindowIm, transfHammingWindowRe, transfHammingWindowIm );
|
cannam@483
|
113
|
cannam@483
|
114 for (unsigned j=0; j<( m_FFTLength ); j++) {
|
cannam@483
|
115 // perform thresholding
|
cannam@483
|
116 const double squaredBin = squaredModule( transfHammingWindowRe[ j ], transfHammingWindowIm[ j ]);
|
cannam@483
|
117 if (squaredBin <= squareThreshold) continue;
|
cannam@483
|
118
|
cannam@483
|
119 // Insert non-zero position indexes
|
cannam@483
|
120 sk->is.push_back(j);
|
cannam@483
|
121 sk->js.push_back(k);
|
c@225
|
122
|
cannam@483
|
123 // take conjugate, normalise and add to array sparkernel
|
cannam@483
|
124 sk->real.push_back( transfHammingWindowRe[ j ]/m_FFTLength);
|
cannam@483
|
125 sk->imag.push_back(-transfHammingWindowIm[ j ]/m_FFTLength);
|
cannam@483
|
126 }
|
c@225
|
127 }
|
c@225
|
128
|
c@225
|
129 delete [] hammingWindowRe;
|
c@225
|
130 delete [] hammingWindowIm;
|
c@225
|
131 delete [] transfHammingWindowRe;
|
c@225
|
132 delete [] transfHammingWindowIm;
|
c@225
|
133
|
c@276
|
134 // std::cerr << "done\n -> is: " << sk->is.size() << ", js: " << sk->js.size() << ", reals: " << sk->real.size() << ", imags: " << sk->imag.size() << std::endl;
|
c@276
|
135
|
c@276
|
136 m_sparseKernel = sk;
|
c@276
|
137 return;
|
c@225
|
138 }
|
c@225
|
139
|
c@225
|
140 //-----------------------------------------------------------------------------
|
c@257
|
141 double* ConstantQ::process( const double* fftdata )
|
c@225
|
142 {
|
c@276
|
143 if (!m_sparseKernel) {
|
c@276
|
144 std::cerr << "ERROR: ConstantQ::process: Sparse kernel has not been initialised" << std::endl;
|
c@276
|
145 return m_CQdata;
|
c@276
|
146 }
|
c@276
|
147
|
c@276
|
148 SparseKernel *sk = m_sparseKernel;
|
c@276
|
149
|
cannam@483
|
150 for (unsigned row=0; row<2*m_uK; row++) {
|
cannam@483
|
151 m_CQdata[ row ] = 0;
|
cannam@483
|
152 m_CQdata[ row+1 ] = 0;
|
c@225
|
153 }
|
c@276
|
154 const unsigned *fftbin = &(sk->is[0]);
|
c@276
|
155 const unsigned *cqbin = &(sk->js[0]);
|
c@276
|
156 const double *real = &(sk->real[0]);
|
c@276
|
157 const double *imag = &(sk->imag[0]);
|
c@276
|
158 const unsigned int sparseCells = sk->real.size();
|
cannam@483
|
159
|
cannam@483
|
160 for (unsigned i = 0; i<sparseCells; i++) {
|
cannam@483
|
161 const unsigned row = cqbin[i];
|
cannam@483
|
162 const unsigned col = fftbin[i];
|
cannam@469
|
163 if (col == 0) continue;
|
cannam@483
|
164 const double & r1 = real[i];
|
cannam@483
|
165 const double & i1 = imag[i];
|
cannam@483
|
166 const double & r2 = fftdata[ (2*m_FFTLength) - 2*col - 2 ];
|
cannam@483
|
167 const double & i2 = fftdata[ (2*m_FFTLength) - 2*col - 2 + 1 ];
|
cannam@483
|
168 // add the multiplication
|
cannam@483
|
169 m_CQdata[ 2*row ] += (r1*r2 - i1*i2);
|
cannam@483
|
170 m_CQdata[ 2*row+1] += (r1*i2 + i1*r2);
|
c@225
|
171 }
|
c@225
|
172
|
c@225
|
173 return m_CQdata;
|
c@225
|
174 }
|
c@225
|
175
|
c@225
|
176
|
c@225
|
177 void ConstantQ::initialise( CQConfig Config )
|
c@225
|
178 {
|
c@225
|
179 m_FS = Config.FS;
|
cannam@483
|
180 m_FMin = Config.min; // min freq
|
cannam@483
|
181 m_FMax = Config.max; // max freq
|
cannam@483
|
182 m_BPO = Config.BPO; // bins per octave
|
c@225
|
183 m_CQThresh = Config.CQThresh;// ConstantQ threshold for kernel generation
|
c@225
|
184
|
cannam@483
|
185 m_dQ = 1/(pow(2,(1/(double)m_BPO))-1); // Work out Q value for Filter bank
|
cannam@483
|
186 m_uK = (unsigned int) ceil(m_BPO * log(m_FMax/m_FMin)/log(2.0)); // No. of constant Q bins
|
c@225
|
187
|
c@249
|
188 // 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
|
189
|
c@225
|
190 // work out length of fft required for this constant Q Filter bank
|
c@225
|
191 m_FFTLength = (int) pow(2, nextpow2(ceil( m_dQ*m_FS/m_FMin )));
|
c@225
|
192
|
cannam@468
|
193 m_hop = m_FFTLength/8;
|
c@225
|
194
|
c@249
|
195 // std::cerr << "ConstantQ::initialise: -> fft length = " << m_FFTLength << ", hop = " << m_hop << std::endl;
|
c@245
|
196
|
c@225
|
197 // allocate memory for cqdata
|
c@225
|
198 m_CQdata = new double [2*m_uK];
|
c@225
|
199 }
|
c@225
|
200
|
c@225
|
201 void ConstantQ::deInitialise()
|
c@225
|
202 {
|
c@225
|
203 delete [] m_CQdata;
|
c@276
|
204 delete m_sparseKernel;
|
c@225
|
205 }
|
c@225
|
206
|
c@257
|
207 void ConstantQ::process(const double *FFTRe, const double* FFTIm,
|
c@257
|
208 double *CQRe, double *CQIm)
|
c@225
|
209 {
|
c@276
|
210 if (!m_sparseKernel) {
|
c@276
|
211 std::cerr << "ERROR: ConstantQ::process: Sparse kernel has not been initialised" << std::endl;
|
c@276
|
212 return;
|
c@276
|
213 }
|
c@276
|
214
|
c@276
|
215 SparseKernel *sk = m_sparseKernel;
|
c@276
|
216
|
cannam@483
|
217 for (unsigned row=0; row<m_uK; row++) {
|
cannam@483
|
218 CQRe[ row ] = 0;
|
cannam@483
|
219 CQIm[ row ] = 0;
|
c@225
|
220 }
|
c@225
|
221
|
c@276
|
222 const unsigned *fftbin = &(sk->is[0]);
|
c@276
|
223 const unsigned *cqbin = &(sk->js[0]);
|
c@276
|
224 const double *real = &(sk->real[0]);
|
c@276
|
225 const double *imag = &(sk->imag[0]);
|
c@276
|
226 const unsigned int sparseCells = sk->real.size();
|
cannam@483
|
227
|
cannam@483
|
228 for (unsigned i = 0; i<sparseCells; i++) {
|
cannam@483
|
229 const unsigned row = cqbin[i];
|
cannam@483
|
230 const unsigned col = fftbin[i];
|
cannam@469
|
231 if (col == 0) continue;
|
cannam@483
|
232 const double & r1 = real[i];
|
cannam@483
|
233 const double & i1 = imag[i];
|
cannam@483
|
234 const double & r2 = FFTRe[ m_FFTLength - col ];
|
cannam@483
|
235 const double & i2 = FFTIm[ m_FFTLength - col ];
|
cannam@483
|
236 // add the multiplication
|
cannam@483
|
237 CQRe[ row ] += (r1*r2 - i1*i2);
|
cannam@483
|
238 CQIm[ row ] += (r1*i2 + i1*r2);
|
c@225
|
239 }
|
c@225
|
240 }
|