annotate fft/fft.js/lib/node.erb.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 <%= $:.unshift('.'); require "#{File.dirname(__FILE__)}/../src/complex.rb"; File.read "#{File.dirname(__FILE__)}/../LICENSE" %>
Chris@25 2
Chris@25 3 if (!FFT) {
Chris@25 4 var FFT = {}
Chris@25 5 }
Chris@25 6
Chris@25 7 void function (namespace) {
Chris@25 8 "use strict"
Chris@25 9
Chris@25 10 function butterfly2(output, outputOffset, outputStride, fStride, state, m) {
Chris@25 11 var t = state.twiddle
Chris@25 12
Chris@25 13 for (var i = 0; i < m; i++) {
Chris@25 14 <%= load('s0', 'output', 'outputOffset', 'i', 'outputStride') %>
Chris@25 15 <%= load('s1', 'output', 'outputOffset', 'i + m', 'outputStride') %>
Chris@25 16
Chris@25 17 <%= load('t1', 't', 0, 'i', 'fStride') %>
Chris@25 18
Chris@25 19 <%= cmul('v1', 's1', 't1') %>
Chris@25 20
Chris@25 21 <%= cadd('r0', 's0', 'v1') %>
Chris@25 22 <%= csub('r1', 's0', 'v1') %>
Chris@25 23
Chris@25 24 <%= store('r0', 'output', 'outputOffset', 'i', 'outputStride') %>
Chris@25 25 <%= store('r1', 'output', 'outputOffset', 'i + m', 'outputStride') %>
Chris@25 26 }
Chris@25 27 }
Chris@25 28
Chris@25 29 function butterfly3(output, outputOffset, outputStride, fStride, state, m) {
Chris@25 30 var t = state.twiddle
Chris@25 31 var m1 = m, m2 = 2 * m
Chris@25 32 var fStride1 = fStride, fStride2 = 2 * fStride
Chris@25 33
Chris@25 34 var e = <%= imag('t', 0, 'm', 'fStride') %>
Chris@25 35
Chris@25 36 for (var i = 0; i < m; i++) {
Chris@25 37 <%= load('s0', 'output', 'outputOffset', 'i', 'outputStride') %>
Chris@25 38
Chris@25 39 <%= load('s1', 'output', 'outputOffset', 'i + m1', 'outputStride') %>
Chris@25 40 <%= load('t1', 't', 0, 'i', 'fStride1') %>
Chris@25 41 <%= cmul('v1', 's1', 't1') %>
Chris@25 42
Chris@25 43 <%= load('s2', 'output', 'outputOffset', 'i + m2', 'outputStride') %>
Chris@25 44 <%= load('t2', 't', 0, 'i', 'fStride2') %>
Chris@25 45 <%= cmul('v2', 's2', 't2') %>
Chris@25 46
Chris@25 47 <%= cadd('i0', 'v1', 'v2') %>
Chris@25 48
Chris@25 49 <%= cadd('r0', 's0', 'i0') %>
Chris@25 50 <%= store('r0', 'output', 'outputOffset', 'i', 'outputStride') %>
Chris@25 51
Chris@25 52 var i1_r = s0_r - i0_r * 0.5
Chris@25 53 var i1_i = s0_i - i0_i * 0.5
Chris@25 54
Chris@25 55 var i2_r = (v1_r - v2_r) * e
Chris@25 56 var i2_i = (v1_i - v2_i) * e
Chris@25 57
Chris@25 58 var r1_r = i1_r - i2_i
Chris@25 59 var r1_i = i1_i + i2_r
Chris@25 60 <%= store('r1', 'output', 'outputOffset', 'i + m1', 'outputStride') %>
Chris@25 61
Chris@25 62 var r2_r = i1_r + i2_i
Chris@25 63 var r2_i = i1_i - i2_r
Chris@25 64 <%= store('r2', 'output', 'outputOffset', 'i + m2', 'outputStride') %>
Chris@25 65 }
Chris@25 66 }
Chris@25 67
Chris@25 68 function butterfly4(output, outputOffset, outputStride, fStride, state, m) {
Chris@25 69 var t = state.twiddle
Chris@25 70 var m1 = m, m2 = 2 * m, m3 = 3 * m
Chris@25 71 var fStride1 = fStride, fStride2 = 2 * fStride, fStride3 = 3 * fStride
Chris@25 72
Chris@25 73 for (var i = 0; i < m; i++) {
Chris@25 74 <%= load('s0', 'output', 'outputOffset', 'i', 'outputStride') %>
Chris@25 75
Chris@25 76 <%= load('s1', 'output', 'outputOffset', 'i + m1', 'outputStride') %>
Chris@25 77 <%= load('t1', 't', 0, 'i', 'fStride1') %>
Chris@25 78 <%= cmul('v1', 's1', 't1') %>
Chris@25 79
Chris@25 80 <%= load('s2', 'output', 'outputOffset', 'i + m2', 'outputStride') %>
Chris@25 81 <%= load('t2', 't', 0, 'i', 'fStride2') %>
Chris@25 82 <%= cmul('v2', 's2', 't2') %>
Chris@25 83
Chris@25 84 <%= load('s3', 'output', 'outputOffset', 'i + m3', 'outputStride') %>
Chris@25 85 <%= load('t3', 't', 0, 'i', 'fStride3') %>
Chris@25 86 <%= cmul('v3', 's3', 't3') %>
Chris@25 87
Chris@25 88 <%= cadd('i0', 's0', 'v2') %>
Chris@25 89 <%= csub('i1', 's0', 'v2') %>
Chris@25 90 <%= cadd('i2', 'v1', 'v3') %>
Chris@25 91 <%= csub('i3', 'v1', 'v3') %>
Chris@25 92
Chris@25 93 <%= cadd('r0', 'i0', 'i2') %>
Chris@25 94
Chris@25 95 if (state.inverse) {
Chris@25 96 var r1_r = i1_r - i3_i
Chris@25 97 var r1_i = i1_i + i3_r
Chris@25 98 } else {
Chris@25 99 var r1_r = i1_r + i3_i
Chris@25 100 var r1_i = i1_i - i3_r
Chris@25 101 }
Chris@25 102
Chris@25 103 <%= csub('r2', 'i0', 'i2') %>
Chris@25 104
Chris@25 105 if (state.inverse) {
Chris@25 106 var r3_r = i1_r + i3_i
Chris@25 107 var r3_i = i1_i - i3_r
Chris@25 108 } else {
Chris@25 109 var r3_r = i1_r - i3_i
Chris@25 110 var r3_i = i1_i + i3_r
Chris@25 111 }
Chris@25 112
Chris@25 113 <%= store('r0', 'output', 'outputOffset', 'i', 'outputStride') %>
Chris@25 114 <%= store('r1', 'output', 'outputOffset', 'i + m1', 'outputStride') %>
Chris@25 115 <%= store('r2', 'output', 'outputOffset', 'i + m2', 'outputStride') %>
Chris@25 116 <%= store('r3', 'output', 'outputOffset', 'i + m3', 'outputStride') %>
Chris@25 117 }
Chris@25 118 }
Chris@25 119
Chris@25 120 function butterfly(output, outputOffset, outputStride, fStride, state, m, p) {
Chris@25 121 var t = state.twiddle, n = state.n, scratch = new Float64Array(2 * p)
Chris@25 122
Chris@25 123 for (var u = 0; u < m; u++) {
Chris@25 124 for (var q1 = 0, k = u; q1 < p; q1++, k += m) {
Chris@25 125 <%= load('x0', 'output', 'outputOffset', 'k', 'outputStride') %>
Chris@25 126 <%= store('x0', 'scratch', 'q1') %>
Chris@25 127 }
Chris@25 128
Chris@25 129 for (var q1 = 0, k = u; q1 < p; q1++, k += m) {
Chris@25 130 var tOffset = 0
Chris@25 131
Chris@25 132 <%= load('x0', 'scratch', 0) %>
Chris@25 133 <%= store('x0', 'output', 'outputOffset', 'k', 'outputStride') %>
Chris@25 134
Chris@25 135 for (var q = 1; q < p; q++) {
Chris@25 136 tOffset = (tOffset + fStride * k) % n
Chris@25 137
Chris@25 138 <%= load('s0', 'output', 'outputOffset', 'k', 'outputStride') %>
Chris@25 139
Chris@25 140 <%= load('s1', 'scratch', 'q') %>
Chris@25 141 <%= load('t1', 't', 'tOffset') %>
Chris@25 142 <%= cmul('v1', 's1', 't1') %>
Chris@25 143
Chris@25 144 <%= cadd('r0', 's0', 'v1') %>
Chris@25 145 <%= store('r0', 'output', 'outputOffset', 'k', 'outputStride') %>
Chris@25 146 }
Chris@25 147 }
Chris@25 148 }
Chris@25 149 }
Chris@25 150
Chris@25 151 function work(output, outputOffset, outputStride, f, fOffset, fStride, inputStride, factors, state) {
Chris@25 152 var p = factors.shift()
Chris@25 153 var m = factors.shift()
Chris@25 154
Chris@25 155 if (m == 1) {
Chris@25 156 for (var i = 0; i < p * m; i++) {
Chris@25 157 <%= load('x0', 'f', 'fOffset', 'i', 'fStride * inputStride') %>
Chris@25 158 <%= store('x0', 'output', 'outputOffset', 'i', 'outputStride') %>
Chris@25 159 }
Chris@25 160 } else {
Chris@25 161 for (var i = 0; i < p; i++) {
Chris@25 162 work(output, outputOffset + outputStride * i * m, outputStride, f, fOffset + i * fStride * inputStride, fStride * p, inputStride, factors.slice(), state)
Chris@25 163 }
Chris@25 164 }
Chris@25 165
Chris@25 166 switch (p) {
Chris@25 167 case 2: butterfly2(output, outputOffset, outputStride, fStride, state, m); break
Chris@25 168 case 3: butterfly3(output, outputOffset, outputStride, fStride, state, m); break
Chris@25 169 case 4: butterfly4(output, outputOffset, outputStride, fStride, state, m); break
Chris@25 170 default: butterfly(output, outputOffset, outputStride, fStride, state, m, p); break
Chris@25 171 }
Chris@25 172 }
Chris@25 173
Chris@25 174 var complex = function (n, inverse) {
Chris@25 175 if (arguments.length < 2) {
Chris@25 176 throw new RangeError("You didn't pass enough arguments, passed `" + arguments.length + "'")
Chris@25 177 }
Chris@25 178
Chris@25 179 var n = ~~n, inverse = !!inverse
Chris@25 180
Chris@25 181 if (n < 1) {
Chris@25 182 throw new RangeError("n is outside range, should be positive integer, was `" + n + "'")
Chris@25 183 }
Chris@25 184
Chris@25 185 var state = {
Chris@25 186 n: n,
Chris@25 187 inverse: inverse,
Chris@25 188
Chris@25 189 factors: [],
Chris@25 190 twiddle: new Float64Array(2 * n),
Chris@25 191 scratch: new Float64Array(2 * n)
Chris@25 192 }
Chris@25 193
Chris@25 194 var t = state.twiddle, theta = 2 * Math.PI / n
Chris@25 195
Chris@25 196 for (var i = 0; i < n; i++) {
Chris@25 197 if (inverse) {
Chris@25 198 var phase = theta * i
Chris@25 199 } else {
Chris@25 200 var phase = -theta * i
Chris@25 201 }
Chris@25 202
Chris@25 203 <%= real('t', 'i') %> = Math.cos(phase)
Chris@25 204 <%= imag('t', 'i') %> = Math.sin(phase)
Chris@25 205 }
Chris@25 206
Chris@25 207 var p = 4, v = Math.floor(Math.sqrt(n))
Chris@25 208
Chris@25 209 while (n > 1) {
Chris@25 210 while (n % p) {
Chris@25 211 switch (p) {
Chris@25 212 case 4: p = 2; break
Chris@25 213 case 2: p = 3; break
Chris@25 214 default: p += 2; break
Chris@25 215 }
Chris@25 216
Chris@25 217 if (p > v) {
Chris@25 218 p = n
Chris@25 219 }
Chris@25 220 }
Chris@25 221
Chris@25 222 n /= p
Chris@25 223
Chris@25 224 state.factors.push(p)
Chris@25 225 state.factors.push(n)
Chris@25 226 }
Chris@25 227
Chris@25 228 this.state = state
Chris@25 229 }
Chris@25 230
Chris@25 231 complex.prototype.simple = function (output, input, t) {
Chris@25 232 this.process(output, 0, 1, input, 0, 1, t)
Chris@25 233 }
Chris@25 234
Chris@25 235 complex.prototype.process = function(output, outputOffset, outputStride, input, inputOffset, inputStride, t) {
Chris@25 236 var outputStride = ~~outputStride, inputStride = ~~inputStride
Chris@25 237
Chris@25 238 var type = t == 'real' ? t : 'complex'
Chris@25 239
Chris@25 240 if (outputStride < 1) {
Chris@25 241 throw new RangeError("outputStride is outside range, should be positive integer, was `" + outputStride + "'")
Chris@25 242 }
Chris@25 243
Chris@25 244 if (inputStride < 1) {
Chris@25 245 throw new RangeError("inputStride is outside range, should be positive integer, was `" + inputStride + "'")
Chris@25 246 }
Chris@25 247
Chris@25 248 if (type == 'real') {
Chris@25 249 for (var i = 0; i < this.state.n; i++) {
Chris@25 250 var x0_r = input[inputOffset + inputStride * i]
Chris@25 251 var x0_i = 0.0
Chris@25 252
Chris@25 253 <%= store('x0', 'this.state.scratch', 'i') %>
Chris@25 254 }
Chris@25 255
Chris@25 256 work(output, outputOffset, outputStride, this.state.scratch, 0, 1, 1, this.state.factors.slice(), this.state)
Chris@25 257 } else {
Chris@25 258 if (input == output) {
Chris@25 259 work(this.state.scratch, 0, 1, input, inputOffset, 1, inputStride, this.state.factors.slice(), this.state)
Chris@25 260
Chris@25 261 for (var i = 0; i < this.state.n; i++) {
Chris@25 262 <%= load('x0', 'this.state.scratch', 'i') %>
Chris@25 263
Chris@25 264 <%= store('x0', 'output', 'outputOffset', 'i', 'outputStride') %>
Chris@25 265 }
Chris@25 266 } else {
Chris@25 267 work(output, outputOffset, outputStride, input, inputOffset, 1, inputStride, this.state.factors.slice(), this.state)
Chris@25 268 }
Chris@25 269 }
Chris@25 270 }
Chris@25 271
Chris@25 272 namespace.complex = complex
Chris@25 273 }(FFT)
Chris@25 274
Chris@25 275 module.exports = FFT