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 |