srfft.cc
Go to the documentation of this file.
1 // matrix/srfft.cc
2 
3 // Copyright 2009-2011 Microsoft Corporation; Go Vivace Inc.
4 
5 // See ../../COPYING for clarification regarding multiple authors
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 // http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
15 // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
16 // MERCHANTABLITY OR NON-INFRINGEMENT.
17 // See the Apache 2 License for the specific language governing permissions and
18 // limitations under the License.
19 //
20 
21 // This file includes a modified version of code originally published in Malvar,
22 // H., "Signal processing with lapped transforms, " Artech House, Inc., 1992. The
23 // current copyright holder of the original code, Henrique S. Malvar, has given
24 // his permission for the release of this modified version under the Apache
25 // License v2.0.
26 
27 
28 #include "matrix/srfft.h"
30 
31 namespace kaldi {
32 
33 
34 template<typename Real>
36  if ( (N & (N-1)) != 0 || N <= 1)
37  KALDI_ERR << "SplitRadixComplexFft called with invalid number of points "
38  << N;
39  N_ = N;
40  logn_ = 0;
41  while (N > 1) {
42  N >>= 1;
43  logn_ ++;
44  }
45  ComputeTables();
46 }
47 
48 template <typename Real>
50  const SplitRadixComplexFft<Real> &other):
51  N_(other.N_), logn_(other.logn_) {
52  // This code duplicates tables from a previously computed object.
53  // Compare with the code in ComputeTables().
54  MatrixIndexT lg2 = logn_ >> 1;
55  if (logn_ & 1) lg2++;
56  MatrixIndexT brseed_size = 1 << lg2;
57  brseed_ = new MatrixIndexT[brseed_size];
58  std::memcpy(brseed_, other.brseed_, sizeof(MatrixIndexT) * brseed_size);
59 
60  if (logn_ < 4) {
61  tab_ = NULL;
62  } else {
63  tab_ = new Real*[logn_ - 3];
64  for (MatrixIndexT i = logn_; i >= 4 ; i--) {
65  MatrixIndexT m = 1 << i, m2 = m / 2, m4 = m2 / 2;
66  MatrixIndexT this_array_size = 6 * (m4 - 2);
67  tab_[i-4] = new Real[this_array_size];
68  std::memcpy(tab_[i-4], other.tab_[i-4],
69  sizeof(Real) * this_array_size);
70  }
71  }
72 }
73 
74 template<typename Real>
76  MatrixIndexT imax, lg2, i, j;
77  MatrixIndexT m, m2, m4, m8, nel, n;
78  Real *cn, *spcn, *smcn, *c3n, *spc3n, *smc3n;
79  Real ang, c, s;
80 
81  lg2 = logn_ >> 1;
82  if (logn_ & 1) lg2++;
83  brseed_ = new MatrixIndexT[1 << lg2];
84  brseed_[0] = 0;
85  brseed_[1] = 1;
86  for (j = 2; j <= lg2; j++) {
87  imax = 1 << (j - 1);
88  for (i = 0; i < imax; i++) {
89  brseed_[i] <<= 1;
90  brseed_[i + imax] = brseed_[i] + 1;
91  }
92  }
93 
94  if (logn_ < 4) {
95  tab_ = NULL;
96  } else {
97  tab_ = new Real* [logn_-3];
98  for (i = logn_; i>=4 ; i--) {
99  /* Compute a few constants */
100  m = 1 << i; m2 = m / 2; m4 = m2 / 2; m8 = m4 /2;
101 
102  /* Allocate memory for tables */
103  nel = m4 - 2;
104 
105  tab_[i-4] = new Real[6*nel];
106 
107  /* Initialize pointers */
108  cn = tab_[i-4]; spcn = cn + nel; smcn = spcn + nel;
109  c3n = smcn + nel; spc3n = c3n + nel; smc3n = spc3n + nel;
110 
111  /* Compute tables */
112  for (n = 1; n < m4; n++) {
113  if (n == m8) continue;
114  ang = n * M_2PI / m;
115  c = std::cos(ang); s = std::sin(ang);
116  *cn++ = c; *spcn++ = - (s + c); *smcn++ = s - c;
117  ang = 3 * n * M_2PI / m;
118  c = std::cos(ang); s = std::sin(ang);
119  *c3n++ = c; *spc3n++ = - (s + c); *smc3n++ = s - c;
120  }
121  }
122  }
123 }
124 
125 template<typename Real>
127  delete [] brseed_;
128  if (tab_ != NULL) {
129  for (MatrixIndexT i = 0; i < logn_-3; i++)
130  delete [] tab_[i];
131  delete [] tab_;
132  }
133 }
134 
135 template<typename Real>
136 void SplitRadixComplexFft<Real>::Compute(Real *xr, Real *xi, bool forward) const {
137  if (!forward) { // reverse real and imaginary parts for complex FFT.
138  Real *tmp = xr;
139  xr = xi;
140  xi = tmp;
141  }
142  ComputeRecursive(xr, xi, logn_);
143  if (logn_ > 1) {
144  BitReversePermute(xr, logn_);
145  BitReversePermute(xi, logn_);
146  }
147 }
148 
149 template<typename Real>
150 void SplitRadixComplexFft<Real>::Compute(Real *x, bool forward,
151  std::vector<Real> *temp_buffer) const {
152  KALDI_ASSERT(temp_buffer != NULL);
153  if (temp_buffer->size() != N_)
154  temp_buffer->resize(N_);
155  Real *temp_ptr = &((*temp_buffer)[0]);
156  for (MatrixIndexT i = 0; i < N_; i++) {
157  x[i] = x[i * 2]; // put the real part in the first half of x.
158  temp_ptr[i] = x[i * 2 + 1]; // put the imaginary part in temp_buffer.
159  }
160  // copy the imaginary part back to the second half of x.
161  memcpy(static_cast<void*>(x + N_),
162  static_cast<void*>(temp_ptr),
163  sizeof(Real) * N_);
164 
165  Compute(x, x + N_, forward);
166  // Now change the format back to interleaved.
167  memcpy(static_cast<void*>(temp_ptr),
168  static_cast<void*>(x + N_),
169  sizeof(Real) * N_);
170  for (MatrixIndexT i = N_-1; i > 0; i--) { // don't include 0,
171  // in case MatrixIndexT is unsigned, the loop would not terminate.
172  // Treat it as a special case.
173  x[i*2] = x[i];
174  x[i*2 + 1] = temp_ptr[i];
175  }
176  x[1] = temp_ptr[0]; // special case of i = 0.
177 }
178 
179 template<typename Real>
180 void SplitRadixComplexFft<Real>::Compute(Real *x, bool forward) {
181  this->Compute(x, forward, &temp_buffer_);
182 }
183 
184 template<typename Real>
186  MatrixIndexT i, j, lg2, n;
187  MatrixIndexT off, fj, gno, *brp;
188  Real tmp, *xp, *xq;
189 
190  lg2 = logn >> 1;
191  n = 1 << lg2;
192  if (logn & 1) lg2++;
193 
194  /* Unshuffling loop */
195  for (off = 1; off < n; off++) {
196  fj = n * brseed_[off]; i = off; j = fj;
197  tmp = x[i]; x[i] = x[j]; x[j] = tmp;
198  xp = &x[i];
199  brp = &(brseed_[1]);
200  for (gno = 1; gno < brseed_[off]; gno++) {
201  xp += n;
202  j = fj + *brp++;
203  xq = x + j;
204  tmp = *xp; *xp = *xq; *xq = tmp;
205  }
206  }
207 }
208 
209 
210 template<typename Real>
211 void SplitRadixComplexFft<Real>::ComputeRecursive(Real *xr, Real *xi, MatrixIndexT logn) const {
212 
213  MatrixIndexT m, m2, m4, m8, nel, n;
214  Real *xr1, *xr2, *xi1, *xi2;
215  Real *cn = nullptr, *spcn = nullptr, *smcn = nullptr, *c3n = nullptr,
216  *spc3n = nullptr, *smc3n = nullptr;
217  Real tmp1, tmp2;
218  Real sqhalf = M_SQRT1_2;
219 
220  /* Check range of logn */
221  if (logn < 0)
222  KALDI_ERR << "Error: logn is out of bounds in SRFFT";
223 
224  /* Compute trivial cases */
225  if (logn < 3) {
226  if (logn == 2) { /* length m = 4 */
227  xr2 = xr + 2;
228  xi2 = xi + 2;
229  tmp1 = *xr + *xr2;
230  *xr2 = *xr - *xr2;
231  *xr = tmp1;
232  tmp1 = *xi + *xi2;
233  *xi2 = *xi - *xi2;
234  *xi = tmp1;
235  xr1 = xr + 1;
236  xi1 = xi + 1;
237  xr2++;
238  xi2++;
239  tmp1 = *xr1 + *xr2;
240  *xr2 = *xr1 - *xr2;
241  *xr1 = tmp1;
242  tmp1 = *xi1 + *xi2;
243  *xi2 = *xi1 - *xi2;
244  *xi1 = tmp1;
245  xr2 = xr + 1;
246  xi2 = xi + 1;
247  tmp1 = *xr + *xr2;
248  *xr2 = *xr - *xr2;
249  *xr = tmp1;
250  tmp1 = *xi + *xi2;
251  *xi2 = *xi - *xi2;
252  *xi = tmp1;
253  xr1 = xr + 2;
254  xi1 = xi + 2;
255  xr2 = xr + 3;
256  xi2 = xi + 3;
257  tmp1 = *xr1 + *xi2;
258  tmp2 = *xi1 + *xr2;
259  *xi1 = *xi1 - *xr2;
260  *xr2 = *xr1 - *xi2;
261  *xr1 = tmp1;
262  *xi2 = tmp2;
263  return;
264  }
265  else if (logn == 1) { /* length m = 2 */
266  xr2 = xr + 1;
267  xi2 = xi + 1;
268  tmp1 = *xr + *xr2;
269  *xr2 = *xr - *xr2;
270  *xr = tmp1;
271  tmp1 = *xi + *xi2;
272  *xi2 = *xi - *xi2;
273  *xi = tmp1;
274  return;
275  }
276  else if (logn == 0) return; /* length m = 1 */
277  }
278 
279  /* Compute a few constants */
280  m = 1 << logn; m2 = m / 2; m4 = m2 / 2; m8 = m4 /2;
281 
282 
283  /* Step 1 */
284  xr1 = xr; xr2 = xr1 + m2;
285  xi1 = xi; xi2 = xi1 + m2;
286  for (n = 0; n < m2; n++) {
287  tmp1 = *xr1 + *xr2;
288  *xr2 = *xr1 - *xr2;
289  xr2++;
290  *xr1++ = tmp1;
291  tmp2 = *xi1 + *xi2;
292  *xi2 = *xi1 - *xi2;
293  xi2++;
294  *xi1++ = tmp2;
295  }
296 
297  /* Step 2 */
298  xr1 = xr + m2; xr2 = xr1 + m4;
299  xi1 = xi + m2; xi2 = xi1 + m4;
300  for (n = 0; n < m4; n++) {
301  tmp1 = *xr1 + *xi2;
302  tmp2 = *xi1 + *xr2;
303  *xi1 = *xi1 - *xr2;
304  xi1++;
305  *xr2++ = *xr1 - *xi2;
306  *xr1++ = tmp1;
307  *xi2++ = tmp2;
308  // xr1++; xr2++; xi1++; xi2++;
309  }
310 
311  /* Steps 3 & 4 */
312  xr1 = xr + m2; xr2 = xr1 + m4;
313  xi1 = xi + m2; xi2 = xi1 + m4;
314  if (logn >= 4) {
315  nel = m4 - 2;
316  cn = tab_[logn-4]; spcn = cn + nel; smcn = spcn + nel;
317  c3n = smcn + nel; spc3n = c3n + nel; smc3n = spc3n + nel;
318  }
319  xr1++; xr2++; xi1++; xi2++;
320  // xr1++; xi1++;
321  for (n = 1; n < m4; n++) {
322  if (n == m8) {
323  tmp1 = sqhalf * (*xr1 + *xi1);
324  *xi1 = sqhalf * (*xi1 - *xr1);
325  *xr1 = tmp1;
326  tmp2 = sqhalf * (*xi2 - *xr2);
327  *xi2 = -sqhalf * (*xr2 + *xi2);
328  *xr2 = tmp2;
329  } else {
330  tmp2 = *cn++ * (*xr1 + *xi1);
331  tmp1 = *spcn++ * *xr1 + tmp2;
332  *xr1 = *smcn++ * *xi1 + tmp2;
333  *xi1 = tmp1;
334  tmp2 = *c3n++ * (*xr2 + *xi2);
335  tmp1 = *spc3n++ * *xr2 + tmp2;
336  *xr2 = *smc3n++ * *xi2 + tmp2;
337  *xi2 = tmp1;
338  }
339  xr1++; xr2++; xi1++; xi2++;
340  }
341 
342  /* Call ssrec again with half DFT length */
343  ComputeRecursive(xr, xi, logn-1);
344 
345  /* Call ssrec again twice with one quarter DFT length.
346  Constants have to be recomputed, because they are static! */
347  // m = 1 << logn; m2 = m / 2;
348  ComputeRecursive(xr + m2, xi + m2, logn - 2);
349  // m = 1 << logn;
350  m4 = 3 * (m / 4);
351  ComputeRecursive(xr + m4, xi + m4, logn - 2);
352 }
353 
354 
355 template<typename Real>
356 void SplitRadixRealFft<Real>::Compute(Real *data, bool forward) {
357  Compute(data, forward, &this->temp_buffer_);
358 }
359 
360 
361 // This code is mostly the same as the RealFft function. It would be
362 // possible to replace it with more efficient code from Rico's book.
363 template<typename Real>
364 void SplitRadixRealFft<Real>::Compute(Real *data, bool forward,
365  std::vector<Real> *temp_buffer) const {
366  MatrixIndexT N = N_, N2 = N/2;
367  KALDI_ASSERT(N%2 == 0);
368  if (forward) // call to base class
369  SplitRadixComplexFft<Real>::Compute(data, true, temp_buffer);
370 
371  Real rootN_re, rootN_im; // exp(-2pi/N), forward; exp(2pi/N), backward
372  int forward_sign = forward ? -1 : 1;
373  ComplexImExp(static_cast<Real>(M_2PI/N *forward_sign), &rootN_re, &rootN_im);
374  Real kN_re = -forward_sign, kN_im = 0.0; // exp(-2pik/N), forward; exp(-2pik/N), backward
375  // kN starts out as 1.0 for forward algorithm but -1.0 for backward.
376  for (MatrixIndexT k = 1; 2*k <= N2; k++) {
377  ComplexMul(rootN_re, rootN_im, &kN_re, &kN_im);
378 
379  Real Ck_re, Ck_im, Dk_re, Dk_im;
380  // C_k = 1/2 (B_k + B_{N/2 - k}^*) :
381  Ck_re = 0.5 * (data[2*k] + data[N - 2*k]);
382  Ck_im = 0.5 * (data[2*k + 1] - data[N - 2*k + 1]);
383  // re(D_k)= 1/2 (im(B_k) + im(B_{N/2-k})):
384  Dk_re = 0.5 * (data[2*k + 1] + data[N - 2*k + 1]);
385  // im(D_k) = -1/2 (re(B_k) - re(B_{N/2-k}))
386  Dk_im =-0.5 * (data[2*k] - data[N - 2*k]);
387  // A_k = C_k + 1^(k/N) D_k:
388  data[2*k] = Ck_re; // A_k <-- C_k
389  data[2*k+1] = Ck_im;
390  // now A_k += D_k 1^(k/N)
391  ComplexAddProduct(Dk_re, Dk_im, kN_re, kN_im, &(data[2*k]), &(data[2*k+1]));
392 
393  MatrixIndexT kdash = N2 - k;
394  if (kdash != k) {
395  // Next we handle the index k' = N/2 - k. This is necessary
396  // to do now, to avoid invalidating data that we will later need.
397  // The quantities C_{k'} and D_{k'} are just the conjugates of C_k
398  // and D_k, so the equations are simple modifications of the above,
399  // replacing Ck_im and Dk_im with their negatives.
400  data[2*kdash] = Ck_re; // A_k' <-- C_k'
401  data[2*kdash+1] = -Ck_im;
402  // now A_k' += D_k' 1^(k'/N)
403  // We use 1^(k'/N) = 1^((N/2 - k) / N) = 1^(1/2) 1^(-k/N) = -1 * (1^(k/N))^*
404  // so it's the same as 1^(k/N) but with the real part negated.
405  ComplexAddProduct(Dk_re, -Dk_im, -kN_re, kN_im, &(data[2*kdash]), &(data[2*kdash+1]));
406  }
407  }
408 
409  { // Now handle k = 0.
410  // In simple terms: after the complex fft, data[0] becomes the sum of real
411  // parts input[0], input[2]... and data[1] becomes the sum of imaginary
412  // pats input[1], input[3]...
413  // "zeroth" [A_0] is just the sum of input[0]+input[1]+input[2]..
414  // and "n2th" [A_{N/2}] is input[0]-input[1]+input[2]... .
415  Real zeroth = data[0] + data[1],
416  n2th = data[0] - data[1];
417  data[0] = zeroth;
418  data[1] = n2th;
419  if (!forward) {
420  data[0] /= 2;
421  data[1] /= 2;
422  }
423  }
424  if (!forward) { // call to base class
425  SplitRadixComplexFft<Real>::Compute(data, false, temp_buffer);
426  for (MatrixIndexT i = 0; i < N; i++)
427  data[i] *= 2.0;
428  // This is so we get a factor of N increase, rather than N/2 which we would
429  // otherwise get from [ComplexFft, forward] + [ComplexFft, backward] in dimension N/2.
430  // It's for consistency with our normal FFT convensions.
431  }
432 }
433 
434 template class SplitRadixComplexFft<float>;
435 template class SplitRadixComplexFft<double>;
436 template class SplitRadixRealFft<float>;
437 template class SplitRadixRealFft<double>;
438 
439 
440 } // end namespace kaldi
This code computes Goodness of Pronunciation (GOP) and extracts phone-level pronunciation feature for...
Definition: chain.dox:20
void BitReversePermute(Real *x, Integer logn) const
Definition: srfft.cc:185
#define M_SQRT1_2
Definition: kaldi-math.h:56
void ComputeRecursive(Real *xr, Real *xi, Integer logn) const
Definition: srfft.cc:211
int32 MatrixIndexT
Definition: matrix-common.h:98
void Compute(Real *xr, Real *xi, bool forward) const
Definition: srfft.cc:136
void ComplexAddProduct(const Real &a_re, const Real &a_im, const Real &b_re, const Real &b_im, Real *c_re, Real *c_im)
ComplexMul implements, inline, the complex operation c += (a * b).
struct rnnlm::@11::@12 n
#define KALDI_ERR
Definition: kaldi-error.h:147
void ComplexMul(const Real &a_re, const Real &a_im, Real *b_re, Real *b_im)
ComplexMul implements, inline, the complex multiplication b *= a.
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
#define M_2PI
Definition: kaldi-math.h:52
void Compute(Real *x, bool forward)
If forward == true, this function transforms from a sequence of N real points to its complex fourier ...
Definition: srfft.cc:356
void ComplexImExp(Real x, Real *a_re, Real *a_im)
ComplexImExp implements a <– exp(i x), inline.
SplitRadixComplexFft(Integer N)
Definition: srfft.cc:35