annotate fft/jsfft/lib/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 66f9fd5ac611
children
rev   line source
Chris@25 1 'use strict';
Chris@25 2
Chris@25 3 !function(exports, complex_array) {
Chris@25 4
Chris@25 5 var
Chris@25 6 ComplexArray = complex_array.ComplexArray,
Chris@25 7 // Math constants and functions we need.
Chris@25 8 PI = Math.PI,
Chris@25 9 SQRT1_2 = Math.SQRT1_2,
Chris@25 10 sqrt = Math.sqrt,
Chris@25 11 cos = Math.cos,
Chris@25 12 sin = Math.sin
Chris@25 13
Chris@25 14 ComplexArray.prototype.FFT = function() {
Chris@25 15 return FFT(this, false)
Chris@25 16 }
Chris@25 17
Chris@25 18 exports.FFT = function(input) {
Chris@25 19 return ensureComplexArray(input).FFT()
Chris@25 20 }
Chris@25 21
Chris@25 22 ComplexArray.prototype.InvFFT = function() {
Chris@25 23 return FFT(this, true)
Chris@25 24 }
Chris@25 25
Chris@25 26 exports.InvFFT = function(input) {
Chris@25 27 return ensureComplexArray(input).InvFFT()
Chris@25 28 }
Chris@25 29
Chris@25 30 // Applies a frequency-space filter to input, and returns the real-space
Chris@25 31 // filtered input.
Chris@25 32 // filterer accepts freq, i, n and modifies freq.real and freq.imag.
Chris@25 33 ComplexArray.prototype.frequencyMap = function(filterer) {
Chris@25 34 return this.FFT().map(filterer).InvFFT()
Chris@25 35 }
Chris@25 36
Chris@25 37 exports.frequencyMap = function(input, filterer) {
Chris@25 38 return ensureComplexArray(input).frequencyMap(filterer)
Chris@25 39 }
Chris@25 40
Chris@25 41 function ensureComplexArray(input) {
Chris@25 42 return complex_array.isComplexArray(input) && input ||
Chris@25 43 new ComplexArray(input)
Chris@25 44 }
Chris@25 45
Chris@25 46 function FFT(input, inverse) {
Chris@25 47 var n = input.length
Chris@25 48
Chris@25 49 if (n & (n - 1)) {
Chris@25 50 return FFT_Recursive(input, inverse)
Chris@25 51 } else {
Chris@25 52 return FFT_2_Iterative(input, inverse)
Chris@25 53 }
Chris@25 54 }
Chris@25 55
Chris@25 56 function FFT_Recursive(input, inverse) {
Chris@25 57 var
Chris@25 58 n = input.length,
Chris@25 59 // Counters.
Chris@25 60 i, j,
Chris@25 61 output,
Chris@25 62 // Complex multiplier and its delta.
Chris@25 63 f_r, f_i, del_f_r, del_f_i,
Chris@25 64 // Lowest divisor and remainder.
Chris@25 65 p, m,
Chris@25 66 normalisation,
Chris@25 67 recursive_result,
Chris@25 68 _swap, _real, _imag
Chris@25 69
Chris@25 70 if (n === 1) {
Chris@25 71 return input
Chris@25 72 }
Chris@25 73
Chris@25 74 output = new ComplexArray(n, input.ArrayType)
Chris@25 75
Chris@25 76 // Use the lowest odd factor, so we are able to use FFT_2_Iterative in the
Chris@25 77 // recursive transforms optimally.
Chris@25 78 p = LowestOddFactor(n)
Chris@25 79 m = n / p
Chris@25 80 normalisation = 1 / sqrt(p)
Chris@25 81 recursive_result = new ComplexArray(m, input.ArrayType)
Chris@25 82
Chris@25 83 // Loops go like O(n Σ p_i), where p_i are the prime factors of n.
Chris@25 84 // for a power of a prime, p, this reduces to O(n p log_p n)
Chris@25 85 for(j = 0; j < p; j++) {
Chris@25 86 for(i = 0; i < m; i++) {
Chris@25 87 recursive_result.real[i] = input.real[i * p + j]
Chris@25 88 recursive_result.imag[i] = input.imag[i * p + j]
Chris@25 89 }
Chris@25 90 // Don't go deeper unless necessary to save allocs.
Chris@25 91 if (m > 1) {
Chris@25 92 recursive_result = FFT(recursive_result, inverse)
Chris@25 93 }
Chris@25 94
Chris@25 95 del_f_r = cos(2*PI*j/n)
Chris@25 96 del_f_i = (inverse ? -1 : 1) * sin(2*PI*j/n)
Chris@25 97 f_r = 1
Chris@25 98 f_i = 0
Chris@25 99
Chris@25 100 for(i = 0; i < n; i++) {
Chris@25 101 _real = recursive_result.real[i % m]
Chris@25 102 _imag = recursive_result.imag[i % m]
Chris@25 103
Chris@25 104 output.real[i] += f_r * _real - f_i * _imag
Chris@25 105 output.imag[i] += f_r * _imag + f_i * _real
Chris@25 106
Chris@25 107 _swap = f_r * del_f_r - f_i * del_f_i
Chris@25 108 f_i = f_r * del_f_i + f_i * del_f_r
Chris@25 109 f_r = _swap
Chris@25 110 }
Chris@25 111 }
Chris@25 112
Chris@25 113 // Copy back to input to match FFT_2_Iterative in-placeness
Chris@25 114 // TODO: faster way of making this in-place?
Chris@25 115 for(i = 0; i < n; i++) {
Chris@25 116 input.real[i] = normalisation * output.real[i]
Chris@25 117 input.imag[i] = normalisation * output.imag[i]
Chris@25 118 }
Chris@25 119
Chris@25 120 return input
Chris@25 121 }
Chris@25 122
Chris@25 123 function FFT_2_Iterative(input, inverse) {
Chris@25 124 var
Chris@25 125 n = input.length,
Chris@25 126 // Counters.
Chris@25 127 i, j,
Chris@25 128 output, output_r, output_i,
Chris@25 129 // Complex multiplier and its delta.
Chris@25 130 f_r, f_i, del_f_r, del_f_i, temp,
Chris@25 131 // Temporary loop variables.
Chris@25 132 l_index, r_index,
Chris@25 133 left_r, left_i, right_r, right_i,
Chris@25 134 // width of each sub-array for which we're iteratively calculating FFT.
Chris@25 135 width
Chris@25 136
Chris@25 137 output = BitReverseComplexArray(input)
Chris@25 138 output_r = output.real
Chris@25 139 output_i = output.imag
Chris@25 140 // Loops go like O(n log n):
Chris@25 141 // width ~ log n; i,j ~ n
Chris@25 142 width = 1
Chris@25 143 while (width < n) {
Chris@25 144 del_f_r = cos(PI/width)
Chris@25 145 del_f_i = (inverse ? -1 : 1) * sin(PI/width)
Chris@25 146 for (i = 0; i < n/(2*width); i++) {
Chris@25 147 f_r = 1
Chris@25 148 f_i = 0
Chris@25 149 for (j = 0; j < width; j++) {
Chris@25 150 l_index = 2*i*width + j
Chris@25 151 r_index = l_index + width
Chris@25 152
Chris@25 153 left_r = output_r[l_index]
Chris@25 154 left_i = output_i[l_index]
Chris@25 155 right_r = f_r * output_r[r_index] - f_i * output_i[r_index]
Chris@25 156 right_i = f_i * output_r[r_index] + f_r * output_i[r_index]
Chris@25 157
Chris@25 158 output_r[l_index] = SQRT1_2 * (left_r + right_r)
Chris@25 159 output_i[l_index] = SQRT1_2 * (left_i + right_i)
Chris@25 160 output_r[r_index] = SQRT1_2 * (left_r - right_r)
Chris@25 161 output_i[r_index] = SQRT1_2 * (left_i - right_i)
Chris@25 162 temp = f_r * del_f_r - f_i * del_f_i
Chris@25 163 f_i = f_r * del_f_i + f_i * del_f_r
Chris@25 164 f_r = temp
Chris@25 165 }
Chris@25 166 }
Chris@25 167 width <<= 1
Chris@25 168 }
Chris@25 169
Chris@25 170 return output
Chris@25 171 }
Chris@25 172
Chris@25 173 function BitReverseIndex(index, n) {
Chris@25 174 var bitreversed_index = 0
Chris@25 175
Chris@25 176 while (n > 1) {
Chris@25 177 bitreversed_index <<= 1
Chris@25 178 bitreversed_index += index & 1
Chris@25 179 index >>= 1
Chris@25 180 n >>= 1
Chris@25 181 }
Chris@25 182 return bitreversed_index
Chris@25 183 }
Chris@25 184
Chris@25 185 function BitReverseComplexArray(array) {
Chris@25 186 var n = array.length,
Chris@25 187 flips = {},
Chris@25 188 swap,
Chris@25 189 i
Chris@25 190
Chris@25 191 for(i = 0; i < n; i++) {
Chris@25 192 var r_i = BitReverseIndex(i, n)
Chris@25 193
Chris@25 194 if (flips.hasOwnProperty(i) || flips.hasOwnProperty(r_i)) continue
Chris@25 195
Chris@25 196 swap = array.real[r_i]
Chris@25 197 array.real[r_i] = array.real[i]
Chris@25 198 array.real[i] = swap
Chris@25 199
Chris@25 200 swap = array.imag[r_i]
Chris@25 201 array.imag[r_i] = array.imag[i]
Chris@25 202 array.imag[i] = swap
Chris@25 203
Chris@25 204 flips[i] = flips[r_i] = true
Chris@25 205 }
Chris@25 206
Chris@25 207 return array
Chris@25 208 }
Chris@25 209
Chris@25 210 function LowestOddFactor(n) {
Chris@25 211 var factor = 3,
Chris@25 212 sqrt_n = sqrt(n)
Chris@25 213
Chris@25 214 while(factor <= sqrt_n) {
Chris@25 215 if (n % factor === 0) return factor
Chris@25 216 factor = factor + 2
Chris@25 217 }
Chris@25 218 return n
Chris@25 219 }
Chris@25 220
Chris@25 221 }(
Chris@25 222 typeof exports === 'undefined' && (this.fft = {}) || exports,
Chris@25 223 typeof require === 'undefined' && (this.complex_array) ||
Chris@25 224 require('./complex_array')
Chris@25 225 )