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)
|