Mercurial > hg > qm-dsp
comparison dsp/chromagram/ConstantQ.cpp @ 225:49844bc8a895
* Queen Mary C++ DSP library
author | Chris Cannam <c.cannam@qmul.ac.uk> |
---|---|
date | Wed, 05 Apr 2006 17:35:59 +0000 |
parents | |
children | 07ac3de1e53b |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 225:49844bc8a895 |
---|---|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | |
2 /* | |
3 QM DSP Library | |
4 | |
5 Centre for Digital Music, Queen Mary, University of London. | |
6 This file copyright 2005-2006 Christian Landone. | |
7 All rights reserved. | |
8 */ | |
9 | |
10 #include "ConstantQ.h" | |
11 #include "dsp/transforms/FFT.h" | |
12 | |
13 //--------------------------------------------------------------------------- | |
14 // nextpow2 returns the smallest integer n such that 2^n >= x. | |
15 static double nextpow2(double x) { | |
16 double y = ceil(log(x)/log(2.0)); | |
17 return(y); | |
18 } | |
19 | |
20 static double squaredModule(const double & xx, const double & yy) { | |
21 return xx*xx + yy*yy; | |
22 } | |
23 | |
24 //---------------------------------------------------------------------------- | |
25 | |
26 ConstantQ::ConstantQ( CQConfig Config ) | |
27 { | |
28 initialise( Config ); | |
29 } | |
30 | |
31 ConstantQ::~ConstantQ() | |
32 { | |
33 deInitialise(); | |
34 } | |
35 | |
36 //---------------------------------------------------------------------------- | |
37 void ConstantQ::sparsekernel() | |
38 { | |
39 //generates spectral kernel matrix (upside down?) | |
40 // initialise temporal kernel with zeros, twice length to deal w. complex numbers | |
41 | |
42 double* hammingWindowRe = new double [ m_FFTLength ]; | |
43 double* hammingWindowIm = new double [ m_FFTLength ]; | |
44 double* transfHammingWindowRe = new double [ m_FFTLength ]; | |
45 double* transfHammingWindowIm = new double [ m_FFTLength ]; | |
46 | |
47 for (unsigned u=0; u < m_FFTLength; u++) | |
48 { | |
49 hammingWindowRe[u] = 0; | |
50 hammingWindowIm[u] = 0; | |
51 } | |
52 | |
53 | |
54 // Here, fftleng*2 is a guess of the number of sparse cells in the matrix | |
55 // The matrix K x fftlength but the non-zero cells are an antialiased | |
56 // square root function. So mostly is a line, with some grey point. | |
57 m_sparseKernelIs.reserve( m_FFTLength*2 ); | |
58 m_sparseKernelJs.reserve( m_FFTLength*2 ); | |
59 m_sparseKernelRealValues.reserve( m_FFTLength*2 ); | |
60 m_sparseKernelImagValues.reserve( m_FFTLength*2 ); | |
61 | |
62 // for each bin value K, calculate temporal kernel, take its fft to | |
63 //calculate the spectral kernel then threshold it to make it sparse and | |
64 //add it to the sparse kernels matrix | |
65 double squareThreshold = m_CQThresh * m_CQThresh; | |
66 | |
67 FFT m_FFT; | |
68 | |
69 for (unsigned k = m_uK; k--; ) | |
70 { | |
71 // Computing a hamming window | |
72 const unsigned hammingLength = (int) ceil( m_dQ * m_FS / ( m_FMin * pow(2,((double)(k))/(double)m_BPO))); | |
73 for (unsigned i=0; i<hammingLength; i++) | |
74 { | |
75 const double angle = 2*PI*m_dQ*i/hammingLength; | |
76 const double real = cos(angle); | |
77 const double imag = sin(angle); | |
78 const double absol = hamming(hammingLength, i)/hammingLength; | |
79 hammingWindowRe[ i ] = absol*real; | |
80 hammingWindowIm[ i ] = absol*imag; | |
81 } | |
82 | |
83 //do fft of hammingWindow | |
84 m_FFT.process( m_FFTLength, 0, hammingWindowRe, hammingWindowIm, transfHammingWindowRe, transfHammingWindowIm ); | |
85 | |
86 | |
87 for (unsigned j=0; j<( m_FFTLength ); j++) | |
88 { | |
89 // perform thresholding | |
90 const double squaredBin = squaredModule( transfHammingWindowRe[ j ], transfHammingWindowIm[ j ]); | |
91 if (squaredBin <= squareThreshold) continue; | |
92 | |
93 // Insert non-zero position indexes, doubled because they are floats | |
94 m_sparseKernelIs.push_back(j); | |
95 m_sparseKernelJs.push_back(k); | |
96 | |
97 // take conjugate, normalise and add to array sparkernel | |
98 m_sparseKernelRealValues.push_back( transfHammingWindowRe[ j ]/m_FFTLength); | |
99 m_sparseKernelImagValues.push_back(-transfHammingWindowIm[ j ]/m_FFTLength); | |
100 } | |
101 | |
102 } | |
103 | |
104 delete [] hammingWindowRe; | |
105 delete [] hammingWindowIm; | |
106 delete [] transfHammingWindowRe; | |
107 delete [] transfHammingWindowIm; | |
108 | |
109 } | |
110 | |
111 //----------------------------------------------------------------------------- | |
112 double* ConstantQ::process( double* fftdata ) | |
113 { | |
114 for (unsigned row=0; row<2*m_uK; row++) | |
115 { | |
116 m_CQdata[ row ] = 0; | |
117 m_CQdata[ row+1 ] = 0; | |
118 } | |
119 const unsigned *fftbin = &(m_sparseKernelIs[0]); | |
120 const unsigned *cqbin = &(m_sparseKernelJs[0]); | |
121 const double *real = &(m_sparseKernelRealValues[0]); | |
122 const double *imag = &(m_sparseKernelImagValues[0]); | |
123 const unsigned int sparseCells = m_sparseKernelRealValues.size(); | |
124 | |
125 for (unsigned i = 0; i<sparseCells; i++) | |
126 { | |
127 const unsigned row = cqbin[i]; | |
128 const unsigned col = fftbin[i]; | |
129 const double & r1 = real[i]; | |
130 const double & i1 = imag[i]; | |
131 const double & r2 = fftdata[ (2*m_FFTLength) - 2*col]; | |
132 const double & i2 = fftdata[ (2*m_FFTLength) - 2*col+1]; | |
133 // add the multiplication | |
134 m_CQdata[ 2*row ] += (r1*r2 - i1*i2); | |
135 m_CQdata[ 2*row+1] += (r1*i2 + i1*r2); | |
136 } | |
137 | |
138 return m_CQdata; | |
139 } | |
140 | |
141 | |
142 void ConstantQ::initialise( CQConfig Config ) | |
143 { | |
144 m_FS = Config.FS; | |
145 m_FMin = Config.min; // min freq | |
146 m_FMax = Config.max; // max freq | |
147 m_BPO = Config.BPO; // bins per octave | |
148 m_CQThresh = Config.CQThresh;// ConstantQ threshold for kernel generation | |
149 | |
150 m_dQ = 1/(pow(2,(1/(double)m_BPO))-1); // Work out Q value for Filter bank | |
151 m_uK = (unsigned int) ceil(m_BPO * log(m_FMax/m_FMin)/log(2.0)); // No. of constant Q bins | |
152 | |
153 // work out length of fft required for this constant Q Filter bank | |
154 m_FFTLength = (int) pow(2, nextpow2(ceil( m_dQ*m_FS/m_FMin ))); | |
155 | |
156 m_hop = m_FFTLength/8; // <------ hop size is window length divided by 32 | |
157 | |
158 // allocate memory for cqdata | |
159 m_CQdata = new double [2*m_uK]; | |
160 } | |
161 | |
162 void ConstantQ::deInitialise() | |
163 { | |
164 delete [] m_CQdata; | |
165 } | |
166 | |
167 void ConstantQ::process(double *FFTRe, double* FFTIm, double *CQRe, double *CQIm) | |
168 { | |
169 for (unsigned row=0; row<m_uK; row++) | |
170 { | |
171 CQRe[ row ] = 0; | |
172 CQIm[ row ] = 0; | |
173 } | |
174 | |
175 const unsigned *fftbin = &(m_sparseKernelIs[0]); | |
176 const unsigned *cqbin = &(m_sparseKernelJs[0]); | |
177 const double *real = &(m_sparseKernelRealValues[0]); | |
178 const double *imag = &(m_sparseKernelImagValues[0]); | |
179 const unsigned int sparseCells = m_sparseKernelRealValues.size(); | |
180 | |
181 for (unsigned i = 0; i<sparseCells; i++) | |
182 { | |
183 const unsigned row = cqbin[i]; | |
184 const unsigned col = fftbin[i]; | |
185 const double & r1 = real[i]; | |
186 const double & i1 = imag[i]; | |
187 const double & r2 = FFTRe[ m_FFTLength- col]; | |
188 const double & i2 = FFTIm[ m_FFTLength - col]; | |
189 // add the multiplication | |
190 CQRe[ row ] += (r1*r2 - i1*i2); | |
191 CQIm[ row ] += (r1*i2 + i1*r2); | |
192 } | |
193 } |