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"
|
cannam@495
|
17 #include "base/Window.h"
|
c@225
|
18
|
c@245
|
19 #include <iostream>
|
c@245
|
20
|
c@225
|
21 //----------------------------------------------------------------------------
|
c@225
|
22
|
cannam@495
|
23 ConstantQ::ConstantQ( CQConfig config ) :
|
c@276
|
24 m_sparseKernel(0)
|
c@225
|
25 {
|
cannam@495
|
26 initialise(config);
|
c@225
|
27 }
|
c@225
|
28
|
c@225
|
29 ConstantQ::~ConstantQ()
|
c@225
|
30 {
|
c@225
|
31 deInitialise();
|
c@225
|
32 }
|
c@225
|
33
|
cannam@495
|
34 static double squaredModule(const double & xx, const double & yy) {
|
cannam@495
|
35 return xx*xx + yy*yy;
|
cannam@495
|
36 }
|
cannam@495
|
37
|
c@225
|
38 void ConstantQ::sparsekernel()
|
c@225
|
39 {
|
c@276
|
40 SparseKernel *sk = new SparseKernel();
|
c@276
|
41
|
cannam@495
|
42 double* windowRe = new double [ m_FFTLength ];
|
cannam@495
|
43 double* windowIm = new double [ m_FFTLength ];
|
cannam@495
|
44 double* transfWindowRe = new double [ m_FFTLength ];
|
cannam@495
|
45 double* transfWindowIm = new double [ m_FFTLength ];
|
cannam@483
|
46
|
cannam@495
|
47 // for each bin value K, calculate temporal kernel, take its fft
|
cannam@495
|
48 // to calculate the spectral kernel then threshold it to make it
|
cannam@495
|
49 // sparse and add it to the sparse kernels matrix
|
cannam@495
|
50
|
c@225
|
51 double squareThreshold = m_CQThresh * m_CQThresh;
|
c@225
|
52
|
cannam@495
|
53 FFT fft(m_FFTLength);
|
cannam@483
|
54
|
cannam@497
|
55 for (int j = m_uK - 1; j >= 0; --j) {
|
c@228
|
56
|
cannam@495
|
57 for (int i = 0; i < m_FFTLength; ++i) {
|
cannam@495
|
58 windowRe[i] = 0;
|
cannam@495
|
59 windowIm[i] = 0;
|
cannam@483
|
60 }
|
c@225
|
61
|
cannam@495
|
62 // Compute a complex sinusoid windowed with a hamming window
|
cannam@495
|
63 // of the right length
|
cannam@495
|
64
|
cannam@495
|
65 int windowLength = (int)ceil
|
cannam@495
|
66 (m_dQ * m_FS / (m_FMin * pow(2, (double)j / (double)m_BPO)));
|
cannam@495
|
67
|
cannam@495
|
68 int origin = m_FFTLength/2 - windowLength/2;
|
cannam@495
|
69
|
cannam@495
|
70 for (int i = 0; i < windowLength; ++i) {
|
cannam@495
|
71 double angle = (2.0 * M_PI * m_dQ * i) / windowLength;
|
cannam@495
|
72 windowRe[origin + i] = cos(angle);
|
cannam@495
|
73 windowIm[origin + i] = sin(angle);
|
cannam@495
|
74 }
|
cannam@495
|
75
|
cannam@495
|
76 // Shape with hamming window
|
cannam@495
|
77 Window<double> hamming(HammingWindow, windowLength);
|
cannam@495
|
78 hamming.cut(windowRe + origin);
|
cannam@495
|
79 hamming.cut(windowIm + origin);
|
cannam@495
|
80
|
cannam@495
|
81 // Scale
|
cannam@495
|
82 for (int i = 0; i < windowLength; ++i) {
|
cannam@495
|
83 windowRe[origin + i] /= windowLength;
|
cannam@495
|
84 }
|
cannam@495
|
85 for (int i = 0; i < windowLength; ++i) {
|
cannam@495
|
86 windowIm[origin + i] /= windowLength;
|
cannam@495
|
87 }
|
cannam@495
|
88
|
cannam@495
|
89 // Input is expected to have been fftshifted, so do the
|
cannam@495
|
90 // same to the input to the fft that contains the kernel
|
cannam@495
|
91 for (int i = 0; i < m_FFTLength/2; ++i) {
|
cannam@495
|
92 double temp = windowRe[i];
|
cannam@495
|
93 windowRe[i] = windowRe[i + m_FFTLength/2];
|
cannam@495
|
94 windowRe[i + m_FFTLength/2] = temp;
|
cannam@495
|
95 }
|
cannam@495
|
96 for (int i = 0; i < m_FFTLength/2; ++i) {
|
cannam@495
|
97 double temp = windowIm[i];
|
cannam@495
|
98 windowIm[i] = windowIm[i + m_FFTLength/2];
|
cannam@495
|
99 windowIm[i + m_FFTLength/2] = temp;
|
c@228
|
100 }
|
c@228
|
101
|
cannam@495
|
102 fft.process(false, windowRe, windowIm, transfWindowRe, transfWindowIm);
|
cannam@495
|
103
|
cannam@495
|
104 // convert to sparse form
|
cannam@495
|
105 for (int i = 0; i < m_FFTLength; i++) {
|
cannam@495
|
106
|
cannam@483
|
107 // perform thresholding
|
cannam@495
|
108 double mag = squaredModule(transfWindowRe[i], transfWindowIm[i]);
|
cannam@495
|
109 if (mag <= squareThreshold) continue;
|
cannam@483
|
110
|
cannam@483
|
111 // Insert non-zero position indexes
|
cannam@495
|
112 sk->is.push_back(i);
|
cannam@495
|
113 sk->js.push_back(j);
|
c@225
|
114
|
cannam@495
|
115 // take conjugate, normalise and add to array for sparse kernel
|
cannam@495
|
116 sk->real.push_back( transfWindowRe[i] / m_FFTLength);
|
cannam@495
|
117 sk->imag.push_back(-transfWindowIm[i] / m_FFTLength);
|
cannam@483
|
118 }
|
c@225
|
119 }
|
c@225
|
120
|
cannam@495
|
121 delete [] windowRe;
|
cannam@495
|
122 delete [] windowIm;
|
cannam@495
|
123 delete [] transfWindowRe;
|
cannam@495
|
124 delete [] transfWindowIm;
|
c@225
|
125
|
c@276
|
126 m_sparseKernel = sk;
|
cannam@495
|
127 }
|
cannam@495
|
128
|
cannam@495
|
129 void ConstantQ::initialise( CQConfig Config )
|
cannam@495
|
130 {
|
cannam@495
|
131 m_FS = Config.FS; // Sample rate
|
cannam@495
|
132 m_FMin = Config.min; // Minimum frequency
|
cannam@495
|
133 m_FMax = Config.max; // Maximum frequency
|
cannam@495
|
134 m_BPO = Config.BPO; // Bins per octave
|
cannam@495
|
135 m_CQThresh = Config.CQThresh; // Threshold for sparse kernel generation
|
cannam@495
|
136
|
cannam@495
|
137 // Q value for filter bank
|
cannam@495
|
138 m_dQ = 1/(pow(2,(1/(double)m_BPO))-1);
|
cannam@495
|
139
|
cannam@495
|
140 // No. of constant Q bins
|
cannam@495
|
141 m_uK = (int)ceil(m_BPO * log(m_FMax/m_FMin)/log(2.0));
|
cannam@495
|
142
|
cannam@495
|
143 // Length of fft required for this Constant Q filter bank
|
cannam@495
|
144 m_FFTLength = MathUtilities::nextPowerOfTwo(int(ceil(m_dQ * m_FS/m_FMin)));
|
cannam@495
|
145
|
cannam@495
|
146 // Hop from one frame to next
|
cannam@495
|
147 m_hop = m_FFTLength / 8;
|
cannam@495
|
148
|
cannam@495
|
149 // allocate memory for cqdata
|
cannam@495
|
150 m_CQdata = new double [2*m_uK];
|
cannam@495
|
151 }
|
cannam@495
|
152
|
cannam@495
|
153 void ConstantQ::deInitialise()
|
cannam@495
|
154 {
|
cannam@495
|
155 delete [] m_CQdata;
|
cannam@495
|
156 delete m_sparseKernel;
|
c@225
|
157 }
|
c@225
|
158
|
c@225
|
159 //-----------------------------------------------------------------------------
|
c@257
|
160 double* ConstantQ::process( const double* fftdata )
|
c@225
|
161 {
|
c@276
|
162 if (!m_sparseKernel) {
|
c@276
|
163 std::cerr << "ERROR: ConstantQ::process: Sparse kernel has not been initialised" << std::endl;
|
c@276
|
164 return m_CQdata;
|
c@276
|
165 }
|
c@276
|
166
|
c@276
|
167 SparseKernel *sk = m_sparseKernel;
|
c@276
|
168
|
cannam@495
|
169 for (int row=0; row < 2 * m_uK; row++) {
|
cannam@483
|
170 m_CQdata[ row ] = 0;
|
cannam@483
|
171 m_CQdata[ row+1 ] = 0;
|
c@225
|
172 }
|
cannam@495
|
173 const int *fftbin = &(sk->is[0]);
|
cannam@495
|
174 const int *cqbin = &(sk->js[0]);
|
cannam@495
|
175 const double *real = &(sk->real[0]);
|
cannam@495
|
176 const double *imag = &(sk->imag[0]);
|
cannam@495
|
177 const int sparseCells = int(sk->real.size());
|
cannam@483
|
178
|
cannam@495
|
179 for (int i = 0; i < sparseCells; i++) {
|
cannam@495
|
180 const int row = cqbin[i];
|
cannam@495
|
181 const int col = fftbin[i];
|
cannam@469
|
182 if (col == 0) continue;
|
cannam@495
|
183 const double & r1 = real[i];
|
cannam@495
|
184 const double & i1 = imag[i];
|
cannam@495
|
185 const double & r2 = fftdata[ (2*m_FFTLength) - 2*col - 2 ];
|
cannam@495
|
186 const double & i2 = fftdata[ (2*m_FFTLength) - 2*col - 2 + 1 ];
|
cannam@483
|
187 // add the multiplication
|
cannam@483
|
188 m_CQdata[ 2*row ] += (r1*r2 - i1*i2);
|
cannam@483
|
189 m_CQdata[ 2*row+1] += (r1*i2 + i1*r2);
|
c@225
|
190 }
|
c@225
|
191
|
c@225
|
192 return m_CQdata;
|
c@225
|
193 }
|
c@225
|
194
|
c@257
|
195 void ConstantQ::process(const double *FFTRe, const double* FFTIm,
|
c@257
|
196 double *CQRe, double *CQIm)
|
c@225
|
197 {
|
c@276
|
198 if (!m_sparseKernel) {
|
c@276
|
199 std::cerr << "ERROR: ConstantQ::process: Sparse kernel has not been initialised" << std::endl;
|
c@276
|
200 return;
|
c@276
|
201 }
|
c@276
|
202
|
c@276
|
203 SparseKernel *sk = m_sparseKernel;
|
c@276
|
204
|
cannam@495
|
205 for (int row = 0; row < m_uK; row++) {
|
cannam@483
|
206 CQRe[ row ] = 0;
|
cannam@483
|
207 CQIm[ row ] = 0;
|
c@225
|
208 }
|
c@225
|
209
|
cannam@495
|
210 const int *fftbin = &(sk->is[0]);
|
cannam@495
|
211 const int *cqbin = &(sk->js[0]);
|
cannam@495
|
212 const double *real = &(sk->real[0]);
|
cannam@495
|
213 const double *imag = &(sk->imag[0]);
|
cannam@495
|
214 const int sparseCells = int(sk->real.size());
|
cannam@483
|
215
|
cannam@495
|
216 for (int i = 0; i<sparseCells; i++) {
|
cannam@495
|
217 const int row = cqbin[i];
|
cannam@495
|
218 const int col = fftbin[i];
|
cannam@469
|
219 if (col == 0) continue;
|
cannam@495
|
220 const double & r1 = real[i];
|
cannam@495
|
221 const double & i1 = imag[i];
|
cannam@495
|
222 const double & r2 = FFTRe[ m_FFTLength - col ];
|
cannam@495
|
223 const double & i2 = FFTIm[ m_FFTLength - col ];
|
cannam@483
|
224 // add the multiplication
|
cannam@483
|
225 CQRe[ row ] += (r1*r2 - i1*i2);
|
cannam@483
|
226 CQIm[ row ] += (r1*i2 + i1*r2);
|
c@225
|
227 }
|
c@225
|
228 }
|