annotate fft/fft.js/lib/node.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 /* Copyright (c) 2012, Jens Nockert <jens@ofmlabs.org>, Jussi Kalliokoski <jussi@ofmlabs.org>
Chris@25 2 * All rights reserved.
Chris@25 3 *
Chris@25 4 * Redistribution and use in source and binary forms, with or without
Chris@25 5 * modification, are permitted provided that the following conditions are met:
Chris@25 6 *
Chris@25 7 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Chris@25 8 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Chris@25 9 *
Chris@25 10 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
Chris@25 11 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
Chris@25 12 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
Chris@25 13 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
Chris@25 14 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
Chris@25 15 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
Chris@25 16 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
Chris@25 17 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Chris@25 18 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
Chris@25 19 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Chris@25 20 */
Chris@25 21
Chris@25 22 if (!FFT) {
Chris@25 23 var FFT = {}
Chris@25 24 }
Chris@25 25
Chris@25 26 void function (namespace) {
Chris@25 27 "use strict"
Chris@25 28
Chris@25 29 function butterfly2(output, outputOffset, outputStride, fStride, state, m) {
Chris@25 30 var t = state.twiddle
Chris@25 31
Chris@25 32 for (var i = 0; i < m; i++) {
Chris@25 33 var s0_r = output[2 * ((outputOffset) + (outputStride) * (i))], s0_i = output[2 * ((outputOffset) + (outputStride) * (i)) + 1]
Chris@25 34 var s1_r = output[2 * ((outputOffset) + (outputStride) * (i + m))], s1_i = output[2 * ((outputOffset) + (outputStride) * (i + m)) + 1]
Chris@25 35
Chris@25 36 var t1_r = t[2 * ((0) + (fStride) * (i))], t1_i = t[2 * ((0) + (fStride) * (i)) + 1]
Chris@25 37
Chris@25 38 var v1_r = s1_r * t1_r - s1_i * t1_i, v1_i = s1_r * t1_i + s1_i * t1_r
Chris@25 39
Chris@25 40 var r0_r = s0_r + v1_r, r0_i = s0_i + v1_i
Chris@25 41 var r1_r = s0_r - v1_r, r1_i = s0_i - v1_i
Chris@25 42
Chris@25 43 output[2 * ((outputOffset) + (outputStride) * (i))] = r0_r, output[2 * ((outputOffset) + (outputStride) * (i)) + 1] = r0_i
Chris@25 44 output[2 * ((outputOffset) + (outputStride) * (i + m))] = r1_r, output[2 * ((outputOffset) + (outputStride) * (i + m)) + 1] = r1_i
Chris@25 45 }
Chris@25 46 }
Chris@25 47
Chris@25 48 function butterfly3(output, outputOffset, outputStride, fStride, state, m) {
Chris@25 49 var t = state.twiddle
Chris@25 50 var m1 = m, m2 = 2 * m
Chris@25 51 var fStride1 = fStride, fStride2 = 2 * fStride
Chris@25 52
Chris@25 53 var e = t[2 * ((0) + (fStride) * (m)) + 1]
Chris@25 54
Chris@25 55 for (var i = 0; i < m; i++) {
Chris@25 56 var s0_r = output[2 * ((outputOffset) + (outputStride) * (i))], s0_i = output[2 * ((outputOffset) + (outputStride) * (i)) + 1]
Chris@25 57
Chris@25 58 var s1_r = output[2 * ((outputOffset) + (outputStride) * (i + m1))], s1_i = output[2 * ((outputOffset) + (outputStride) * (i + m1)) + 1]
Chris@25 59 var t1_r = t[2 * ((0) + (fStride1) * (i))], t1_i = t[2 * ((0) + (fStride1) * (i)) + 1]
Chris@25 60 var v1_r = s1_r * t1_r - s1_i * t1_i, v1_i = s1_r * t1_i + s1_i * t1_r
Chris@25 61
Chris@25 62 var s2_r = output[2 * ((outputOffset) + (outputStride) * (i + m2))], s2_i = output[2 * ((outputOffset) + (outputStride) * (i + m2)) + 1]
Chris@25 63 var t2_r = t[2 * ((0) + (fStride2) * (i))], t2_i = t[2 * ((0) + (fStride2) * (i)) + 1]
Chris@25 64 var v2_r = s2_r * t2_r - s2_i * t2_i, v2_i = s2_r * t2_i + s2_i * t2_r
Chris@25 65
Chris@25 66 var i0_r = v1_r + v2_r, i0_i = v1_i + v2_i
Chris@25 67
Chris@25 68 var r0_r = s0_r + i0_r, r0_i = s0_i + i0_i
Chris@25 69 output[2 * ((outputOffset) + (outputStride) * (i))] = r0_r, output[2 * ((outputOffset) + (outputStride) * (i)) + 1] = r0_i
Chris@25 70
Chris@25 71 var i1_r = s0_r - i0_r * 0.5
Chris@25 72 var i1_i = s0_i - i0_i * 0.5
Chris@25 73
Chris@25 74 var i2_r = (v1_r - v2_r) * e
Chris@25 75 var i2_i = (v1_i - v2_i) * e
Chris@25 76
Chris@25 77 var r1_r = i1_r - i2_i
Chris@25 78 var r1_i = i1_i + i2_r
Chris@25 79 output[2 * ((outputOffset) + (outputStride) * (i + m1))] = r1_r, output[2 * ((outputOffset) + (outputStride) * (i + m1)) + 1] = r1_i
Chris@25 80
Chris@25 81 var r2_r = i1_r + i2_i
Chris@25 82 var r2_i = i1_i - i2_r
Chris@25 83 output[2 * ((outputOffset) + (outputStride) * (i + m2))] = r2_r, output[2 * ((outputOffset) + (outputStride) * (i + m2)) + 1] = r2_i
Chris@25 84 }
Chris@25 85 }
Chris@25 86
Chris@25 87 function butterfly4(output, outputOffset, outputStride, fStride, state, m) {
Chris@25 88 var t = state.twiddle
Chris@25 89 var m1 = m, m2 = 2 * m, m3 = 3 * m
Chris@25 90 var fStride1 = fStride, fStride2 = 2 * fStride, fStride3 = 3 * fStride
Chris@25 91
Chris@25 92 for (var i = 0; i < m; i++) {
Chris@25 93 var s0_r = output[2 * ((outputOffset) + (outputStride) * (i))], s0_i = output[2 * ((outputOffset) + (outputStride) * (i)) + 1]
Chris@25 94
Chris@25 95 var s1_r = output[2 * ((outputOffset) + (outputStride) * (i + m1))], s1_i = output[2 * ((outputOffset) + (outputStride) * (i + m1)) + 1]
Chris@25 96 var t1_r = t[2 * ((0) + (fStride1) * (i))], t1_i = t[2 * ((0) + (fStride1) * (i)) + 1]
Chris@25 97 var v1_r = s1_r * t1_r - s1_i * t1_i, v1_i = s1_r * t1_i + s1_i * t1_r
Chris@25 98
Chris@25 99 var s2_r = output[2 * ((outputOffset) + (outputStride) * (i + m2))], s2_i = output[2 * ((outputOffset) + (outputStride) * (i + m2)) + 1]
Chris@25 100 var t2_r = t[2 * ((0) + (fStride2) * (i))], t2_i = t[2 * ((0) + (fStride2) * (i)) + 1]
Chris@25 101 var v2_r = s2_r * t2_r - s2_i * t2_i, v2_i = s2_r * t2_i + s2_i * t2_r
Chris@25 102
Chris@25 103 var s3_r = output[2 * ((outputOffset) + (outputStride) * (i + m3))], s3_i = output[2 * ((outputOffset) + (outputStride) * (i + m3)) + 1]
Chris@25 104 var t3_r = t[2 * ((0) + (fStride3) * (i))], t3_i = t[2 * ((0) + (fStride3) * (i)) + 1]
Chris@25 105 var v3_r = s3_r * t3_r - s3_i * t3_i, v3_i = s3_r * t3_i + s3_i * t3_r
Chris@25 106
Chris@25 107 var i0_r = s0_r + v2_r, i0_i = s0_i + v2_i
Chris@25 108 var i1_r = s0_r - v2_r, i1_i = s0_i - v2_i
Chris@25 109 var i2_r = v1_r + v3_r, i2_i = v1_i + v3_i
Chris@25 110 var i3_r = v1_r - v3_r, i3_i = v1_i - v3_i
Chris@25 111
Chris@25 112 var r0_r = i0_r + i2_r, r0_i = i0_i + i2_i
Chris@25 113
Chris@25 114 if (state.inverse) {
Chris@25 115 var r1_r = i1_r - i3_i
Chris@25 116 var r1_i = i1_i + i3_r
Chris@25 117 } else {
Chris@25 118 var r1_r = i1_r + i3_i
Chris@25 119 var r1_i = i1_i - i3_r
Chris@25 120 }
Chris@25 121
Chris@25 122 var r2_r = i0_r - i2_r, r2_i = i0_i - i2_i
Chris@25 123
Chris@25 124 if (state.inverse) {
Chris@25 125 var r3_r = i1_r + i3_i
Chris@25 126 var r3_i = i1_i - i3_r
Chris@25 127 } else {
Chris@25 128 var r3_r = i1_r - i3_i
Chris@25 129 var r3_i = i1_i + i3_r
Chris@25 130 }
Chris@25 131
Chris@25 132 output[2 * ((outputOffset) + (outputStride) * (i))] = r0_r, output[2 * ((outputOffset) + (outputStride) * (i)) + 1] = r0_i
Chris@25 133 output[2 * ((outputOffset) + (outputStride) * (i + m1))] = r1_r, output[2 * ((outputOffset) + (outputStride) * (i + m1)) + 1] = r1_i
Chris@25 134 output[2 * ((outputOffset) + (outputStride) * (i + m2))] = r2_r, output[2 * ((outputOffset) + (outputStride) * (i + m2)) + 1] = r2_i
Chris@25 135 output[2 * ((outputOffset) + (outputStride) * (i + m3))] = r3_r, output[2 * ((outputOffset) + (outputStride) * (i + m3)) + 1] = r3_i
Chris@25 136 }
Chris@25 137 }
Chris@25 138
Chris@25 139 function butterfly(output, outputOffset, outputStride, fStride, state, m, p) {
Chris@25 140 var t = state.twiddle, n = state.n, scratch = new Float64Array(2 * p)
Chris@25 141
Chris@25 142 for (var u = 0; u < m; u++) {
Chris@25 143 for (var q1 = 0, k = u; q1 < p; q1++, k += m) {
Chris@25 144 var x0_r = output[2 * ((outputOffset) + (outputStride) * (k))], x0_i = output[2 * ((outputOffset) + (outputStride) * (k)) + 1]
Chris@25 145 scratch[2 * (q1)] = x0_r, scratch[2 * (q1) + 1] = x0_i
Chris@25 146 }
Chris@25 147
Chris@25 148 for (var q1 = 0, k = u; q1 < p; q1++, k += m) {
Chris@25 149 var tOffset = 0
Chris@25 150
Chris@25 151 var x0_r = scratch[2 * (0)], x0_i = scratch[2 * (0) + 1]
Chris@25 152 output[2 * ((outputOffset) + (outputStride) * (k))] = x0_r, output[2 * ((outputOffset) + (outputStride) * (k)) + 1] = x0_i
Chris@25 153
Chris@25 154 for (var q = 1; q < p; q++) {
Chris@25 155 tOffset = (tOffset + fStride * k) % n
Chris@25 156
Chris@25 157 var s0_r = output[2 * ((outputOffset) + (outputStride) * (k))], s0_i = output[2 * ((outputOffset) + (outputStride) * (k)) + 1]
Chris@25 158
Chris@25 159 var s1_r = scratch[2 * (q)], s1_i = scratch[2 * (q) + 1]
Chris@25 160 var t1_r = t[2 * (tOffset)], t1_i = t[2 * (tOffset) + 1]
Chris@25 161 var v1_r = s1_r * t1_r - s1_i * t1_i, v1_i = s1_r * t1_i + s1_i * t1_r
Chris@25 162
Chris@25 163 var r0_r = s0_r + v1_r, r0_i = s0_i + v1_i
Chris@25 164 output[2 * ((outputOffset) + (outputStride) * (k))] = r0_r, output[2 * ((outputOffset) + (outputStride) * (k)) + 1] = r0_i
Chris@25 165 }
Chris@25 166 }
Chris@25 167 }
Chris@25 168 }
Chris@25 169
Chris@25 170 function work(output, outputOffset, outputStride, f, fOffset, fStride, inputStride, factors, state) {
Chris@25 171 var p = factors.shift()
Chris@25 172 var m = factors.shift()
Chris@25 173
Chris@25 174 if (m == 1) {
Chris@25 175 for (var i = 0; i < p * m; i++) {
Chris@25 176 var x0_r = f[2 * ((fOffset) + (fStride * inputStride) * (i))], x0_i = f[2 * ((fOffset) + (fStride * inputStride) * (i)) + 1]
Chris@25 177 output[2 * ((outputOffset) + (outputStride) * (i))] = x0_r, output[2 * ((outputOffset) + (outputStride) * (i)) + 1] = x0_i
Chris@25 178 }
Chris@25 179 } else {
Chris@25 180 for (var i = 0; i < p; i++) {
Chris@25 181 work(output, outputOffset + outputStride * i * m, outputStride, f, fOffset + i * fStride * inputStride, fStride * p, inputStride, factors.slice(), state)
Chris@25 182 }
Chris@25 183 }
Chris@25 184
Chris@25 185 switch (p) {
Chris@25 186 case 2: butterfly2(output, outputOffset, outputStride, fStride, state, m); break
Chris@25 187 case 3: butterfly3(output, outputOffset, outputStride, fStride, state, m); break
Chris@25 188 case 4: butterfly4(output, outputOffset, outputStride, fStride, state, m); break
Chris@25 189 default: butterfly(output, outputOffset, outputStride, fStride, state, m, p); break
Chris@25 190 }
Chris@25 191 }
Chris@25 192
Chris@25 193 var complex = function (n, inverse) {
Chris@25 194 if (arguments.length < 2) {
Chris@25 195 throw new RangeError("You didn't pass enough arguments, passed `" + arguments.length + "'")
Chris@25 196 }
Chris@25 197
Chris@25 198 var n = ~~n, inverse = !!inverse
Chris@25 199
Chris@25 200 if (n < 1) {
Chris@25 201 throw new RangeError("n is outside range, should be positive integer, was `" + n + "'")
Chris@25 202 }
Chris@25 203
Chris@25 204 var state = {
Chris@25 205 n: n,
Chris@25 206 inverse: inverse,
Chris@25 207
Chris@25 208 factors: [],
Chris@25 209 twiddle: new Float64Array(2 * n),
Chris@25 210 scratch: new Float64Array(2 * n)
Chris@25 211 }
Chris@25 212
Chris@25 213 var t = state.twiddle, theta = 2 * Math.PI / n
Chris@25 214
Chris@25 215 for (var i = 0; i < n; i++) {
Chris@25 216 if (inverse) {
Chris@25 217 var phase = theta * i
Chris@25 218 } else {
Chris@25 219 var phase = -theta * i
Chris@25 220 }
Chris@25 221
Chris@25 222 t[2 * (i)] = Math.cos(phase)
Chris@25 223 t[2 * (i) + 1] = Math.sin(phase)
Chris@25 224 }
Chris@25 225
Chris@25 226 var p = 4, v = Math.floor(Math.sqrt(n))
Chris@25 227
Chris@25 228 while (n > 1) {
Chris@25 229 while (n % p) {
Chris@25 230 switch (p) {
Chris@25 231 case 4: p = 2; break
Chris@25 232 case 2: p = 3; break
Chris@25 233 default: p += 2; break
Chris@25 234 }
Chris@25 235
Chris@25 236 if (p > v) {
Chris@25 237 p = n
Chris@25 238 }
Chris@25 239 }
Chris@25 240
Chris@25 241 n /= p
Chris@25 242
Chris@25 243 state.factors.push(p)
Chris@25 244 state.factors.push(n)
Chris@25 245 }
Chris@25 246
Chris@25 247 this.state = state
Chris@25 248 }
Chris@25 249
Chris@25 250 complex.prototype.simple = function (output, input, t) {
Chris@25 251 this.process(output, 0, 1, input, 0, 1, t)
Chris@25 252 }
Chris@25 253
Chris@25 254 complex.prototype.process = function(output, outputOffset, outputStride, input, inputOffset, inputStride, t) {
Chris@25 255 var outputStride = ~~outputStride, inputStride = ~~inputStride
Chris@25 256
Chris@25 257 var type = t == 'real' ? t : 'complex'
Chris@25 258
Chris@25 259 if (outputStride < 1) {
Chris@25 260 throw new RangeError("outputStride is outside range, should be positive integer, was `" + outputStride + "'")
Chris@25 261 }
Chris@25 262
Chris@25 263 if (inputStride < 1) {
Chris@25 264 throw new RangeError("inputStride is outside range, should be positive integer, was `" + inputStride + "'")
Chris@25 265 }
Chris@25 266
Chris@25 267 if (type == 'real') {
Chris@25 268 for (var i = 0; i < this.state.n; i++) {
Chris@25 269 var x0_r = input[inputOffset + inputStride * i]
Chris@25 270 var x0_i = 0.0
Chris@25 271
Chris@25 272 this.state.scratch[2 * (i)] = x0_r, this.state.scratch[2 * (i) + 1] = x0_i
Chris@25 273 }
Chris@25 274
Chris@25 275 work(output, outputOffset, outputStride, this.state.scratch, 0, 1, 1, this.state.factors.slice(), this.state)
Chris@25 276 } else {
Chris@25 277 if (input == output) {
Chris@25 278 work(this.state.scratch, 0, 1, input, inputOffset, 1, inputStride, this.state.factors.slice(), this.state)
Chris@25 279
Chris@25 280 for (var i = 0; i < this.state.n; i++) {
Chris@25 281 var x0_r = this.state.scratch[2 * (i)], x0_i = this.state.scratch[2 * (i) + 1]
Chris@25 282
Chris@25 283 output[2 * ((outputOffset) + (outputStride) * (i))] = x0_r, output[2 * ((outputOffset) + (outputStride) * (i)) + 1] = x0_i
Chris@25 284 }
Chris@25 285 } else {
Chris@25 286 work(output, outputOffset, outputStride, input, inputOffset, 1, inputStride, this.state.factors.slice(), this.state)
Chris@25 287 }
Chris@25 288 }
Chris@25 289 }
Chris@25 290
Chris@25 291 namespace.complex = complex
Chris@25 292 }(FFT)
Chris@25 293
Chris@25 294 module.exports = FFT