annotate fft/nayuki/fft.js @ 40:223f770b5341 kissfft-double tip

Try a double-precision kissfft
author Chris Cannam
date Wed, 07 Sep 2016 10:40:32 +0100
parents d7c216b6a84f
children
rev   line source
Chris@0 1 /*
Chris@0 2 * Free FFT and convolution (JavaScript)
Chris@0 3 *
Chris@0 4 * Copyright (c) 2014 Project Nayuki
Chris@0 5 * http://www.nayuki.io/page/free-small-fft-in-multiple-languages
Chris@0 6 *
Chris@0 7 * (MIT License)
Chris@0 8 * Permission is hereby granted, free of charge, to any person obtaining a copy of
Chris@0 9 * this software and associated documentation files (the "Software"), to deal in
Chris@0 10 * the Software without restriction, including without limitation the rights to
Chris@0 11 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
Chris@0 12 * the Software, and to permit persons to whom the Software is furnished to do so,
Chris@0 13 * subject to the following conditions:
Chris@0 14 * - The above copyright notice and this permission notice shall be included in
Chris@0 15 * all copies or substantial portions of the Software.
Chris@0 16 * - The Software is provided "as is", without warranty of any kind, express or
Chris@0 17 * implied, including but not limited to the warranties of merchantability,
Chris@0 18 * fitness for a particular purpose and noninfringement. In no event shall the
Chris@0 19 * authors or copyright holders be liable for any claim, damages or other
Chris@0 20 * liability, whether in an action of contract, tort or otherwise, arising from,
Chris@0 21 * out of or in connection with the Software or the use or other dealings in the
Chris@0 22 * Software.
Chris@0 23 */
Chris@0 24
Chris@0 25 "use strict";
Chris@0 26
Chris@0 27
Chris@0 28 /*
Chris@0 29 * Computes the discrete Fourier transform (DFT) of the given complex vector, storing the result back into the vector.
Chris@0 30 * The vector can have any length. This is a wrapper function.
Chris@0 31 */
Chris@0 32 function transform(real, imag) {
Chris@0 33 if (real.length != imag.length)
Chris@0 34 throw "Mismatched lengths";
Chris@0 35
Chris@0 36 var n = real.length;
Chris@0 37 if (n == 0)
Chris@0 38 return;
Chris@0 39 else if ((n & (n - 1)) == 0) // Is power of 2
Chris@0 40 transformRadix2(real, imag);
Chris@0 41 else // More complicated algorithm for arbitrary sizes
Chris@0 42 transformBluestein(real, imag);
Chris@0 43 }
Chris@0 44
Chris@0 45
Chris@0 46 /*
Chris@0 47 * Computes the inverse discrete Fourier transform (IDFT) of the given complex vector, storing the result back into the vector.
Chris@0 48 * The vector can have any length. This is a wrapper function. This transform does not perform scaling, so the inverse is not a true inverse.
Chris@0 49 */
Chris@0 50 function inverseTransform(real, imag) {
Chris@0 51 transform(imag, real);
Chris@0 52 }
Chris@0 53
Chris@0 54
Chris@0 55 /*
Chris@0 56 * Computes the discrete Fourier transform (DFT) of the given complex vector, storing the result back into the vector.
Chris@0 57 * The vector's length must be a power of 2. Uses the Cooley-Tukey decimation-in-time radix-2 algorithm.
Chris@0 58 */
Chris@0 59 function transformRadix2(real, imag) {
Chris@0 60 // Initialization
Chris@0 61 if (real.length != imag.length)
Chris@0 62 throw "Mismatched lengths";
Chris@0 63 var n = real.length;
Chris@0 64 if (n == 1) // Trivial transform
Chris@0 65 return;
Chris@0 66 var levels = -1;
Chris@0 67 for (var i = 0; i < 32; i++) {
Chris@0 68 if (1 << i == n)
Chris@0 69 levels = i; // Equal to log2(n)
Chris@0 70 }
Chris@0 71 if (levels == -1)
Chris@0 72 throw "Length is not a power of 2";
Chris@0 73 var cosTable = new Array(n / 2);
Chris@0 74 var sinTable = new Array(n / 2);
Chris@0 75 for (var i = 0; i < n / 2; i++) {
Chris@0 76 cosTable[i] = Math.cos(2 * Math.PI * i / n);
Chris@0 77 sinTable[i] = Math.sin(2 * Math.PI * i / n);
Chris@0 78 }
Chris@0 79
Chris@0 80 // Bit-reversed addressing permutation
Chris@0 81 for (var i = 0; i < n; i++) {
Chris@0 82 var j = reverseBits(i, levels);
Chris@0 83 if (j > i) {
Chris@0 84 var temp = real[i];
Chris@0 85 real[i] = real[j];
Chris@0 86 real[j] = temp;
Chris@0 87 temp = imag[i];
Chris@0 88 imag[i] = imag[j];
Chris@0 89 imag[j] = temp;
Chris@0 90 }
Chris@0 91 }
Chris@0 92
Chris@0 93 // Cooley-Tukey decimation-in-time radix-2 FFT
Chris@0 94 for (var size = 2; size <= n; size *= 2) {
Chris@0 95 var halfsize = size / 2;
Chris@0 96 var tablestep = n / size;
Chris@0 97 for (var i = 0; i < n; i += size) {
Chris@0 98 for (var j = i, k = 0; j < i + halfsize; j++, k += tablestep) {
Chris@0 99 var tpre = real[j+halfsize] * cosTable[k] + imag[j+halfsize] * sinTable[k];
Chris@0 100 var tpim = -real[j+halfsize] * sinTable[k] + imag[j+halfsize] * cosTable[k];
Chris@0 101 real[j + halfsize] = real[j] - tpre;
Chris@0 102 imag[j + halfsize] = imag[j] - tpim;
Chris@0 103 real[j] += tpre;
Chris@0 104 imag[j] += tpim;
Chris@0 105 }
Chris@0 106 }
Chris@0 107 }
Chris@0 108
Chris@0 109 // Returns the integer whose value is the reverse of the lowest 'bits' bits of the integer 'x'.
Chris@0 110 function reverseBits(x, bits) {
Chris@0 111 var y = 0;
Chris@0 112 for (var i = 0; i < bits; i++) {
Chris@0 113 y = (y << 1) | (x & 1);
Chris@0 114 x >>>= 1;
Chris@0 115 }
Chris@0 116 return y;
Chris@0 117 }
Chris@0 118 }
Chris@0 119
Chris@0 120
Chris@0 121 /*
Chris@0 122 * Computes the discrete Fourier transform (DFT) of the given complex vector, storing the result back into the vector.
Chris@0 123 * The vector can have any length. This requires the convolution function, which in turn requires the radix-2 FFT function.
Chris@0 124 * Uses Bluestein's chirp z-transform algorithm.
Chris@0 125 */
Chris@0 126 function transformBluestein(real, imag) {
Chris@0 127 // Find a power-of-2 convolution length m such that m >= n * 2 + 1
Chris@0 128 if (real.length != imag.length)
Chris@0 129 throw "Mismatched lengths";
Chris@0 130 var n = real.length;
Chris@0 131 var m = 1;
Chris@0 132 while (m < n * 2 + 1)
Chris@0 133 m *= 2;
Chris@0 134
Chris@0 135 // Trignometric tables
Chris@0 136 var cosTable = new Array(n);
Chris@0 137 var sinTable = new Array(n);
Chris@0 138 for (var i = 0; i < n; i++) {
Chris@0 139 var j = i * i % (n * 2); // This is more accurate than j = i * i
Chris@0 140 cosTable[i] = Math.cos(Math.PI * j / n);
Chris@0 141 sinTable[i] = Math.sin(Math.PI * j / n);
Chris@0 142 }
Chris@0 143
Chris@0 144 // Temporary vectors and preprocessing
Chris@0 145 var areal = new Array(m);
Chris@0 146 var aimag = new Array(m);
Chris@0 147 for (var i = 0; i < n; i++) {
Chris@0 148 areal[i] = real[i] * cosTable[i] + imag[i] * sinTable[i];
Chris@0 149 aimag[i] = -real[i] * sinTable[i] + imag[i] * cosTable[i];
Chris@0 150 }
Chris@0 151 for (var i = n; i < m; i++)
Chris@0 152 areal[i] = aimag[i] = 0;
Chris@0 153 var breal = new Array(m);
Chris@0 154 var bimag = new Array(m);
Chris@0 155 breal[0] = cosTable[0];
Chris@0 156 bimag[0] = sinTable[0];
Chris@0 157 for (var i = 1; i < n; i++) {
Chris@0 158 breal[i] = breal[m - i] = cosTable[i];
Chris@0 159 bimag[i] = bimag[m - i] = sinTable[i];
Chris@0 160 }
Chris@0 161 for (var i = n; i <= m - n; i++)
Chris@0 162 breal[i] = bimag[i] = 0;
Chris@0 163
Chris@0 164 // Convolution
Chris@0 165 var creal = new Array(m);
Chris@0 166 var cimag = new Array(m);
Chris@0 167 convolveComplex(areal, aimag, breal, bimag, creal, cimag);
Chris@0 168
Chris@0 169 // Postprocessing
Chris@0 170 for (var i = 0; i < n; i++) {
Chris@0 171 real[i] = creal[i] * cosTable[i] + cimag[i] * sinTable[i];
Chris@0 172 imag[i] = -creal[i] * sinTable[i] + cimag[i] * cosTable[i];
Chris@0 173 }
Chris@0 174 }
Chris@0 175
Chris@0 176
Chris@0 177 /*
Chris@0 178 * Computes the circular convolution of the given real vectors. Each vector's length must be the same.
Chris@0 179 */
Chris@0 180 function convolveReal(x, y, out) {
Chris@0 181 if (x.length != y.length || x.length != out.length)
Chris@0 182 throw "Mismatched lengths";
Chris@0 183 var zeros = new Array(x.length);
Chris@0 184 for (var i = 0; i < zeros.length; i++)
Chris@0 185 zeros[i] = 0;
Chris@0 186 convolveComplex(x, zeros, y, zeros.slice(0), out, zeros.slice(0));
Chris@0 187 }
Chris@0 188
Chris@0 189
Chris@0 190 /*
Chris@0 191 * Computes the circular convolution of the given complex vectors. Each vector's length must be the same.
Chris@0 192 */
Chris@0 193 function convolveComplex(xreal, ximag, yreal, yimag, outreal, outimag) {
Chris@0 194 if (xreal.length != ximag.length || xreal.length != yreal.length || yreal.length != yimag.length || xreal.length != outreal.length || outreal.length != outimag.length)
Chris@0 195 throw "Mismatched lengths";
Chris@0 196
Chris@0 197 var n = xreal.length;
Chris@0 198 xreal = xreal.slice(0);
Chris@0 199 ximag = ximag.slice(0);
Chris@0 200 yreal = yreal.slice(0);
Chris@0 201 yimag = yimag.slice(0);
Chris@0 202
Chris@0 203 transform(xreal, ximag);
Chris@0 204 transform(yreal, yimag);
Chris@0 205 for (var i = 0; i < n; i++) {
Chris@0 206 var temp = xreal[i] * yreal[i] - ximag[i] * yimag[i];
Chris@0 207 ximag[i] = ximag[i] * yreal[i] + xreal[i] * yimag[i];
Chris@0 208 xreal[i] = temp;
Chris@0 209 }
Chris@0 210 inverseTransform(xreal, ximag);
Chris@0 211 for (var i = 0; i < n; i++) { // Scaling (because this FFT implementation omits it)
Chris@0 212 outreal[i] = xreal[i] / n;
Chris@0 213 outimag[i] = ximag[i] / n;
Chris@0 214 }
Chris@0 215 }