Skip to content
This repository
Browse code

Adds flock.ugen.math() for basic math operations powered by dspapi.js

  • Loading branch information...
commit 0fc01a7066d357c129762b81e56fc250ce457e19 1 parent b4036ba
Colin Clark authored
9  flocking/flocking-core.js
@@ -107,6 +107,15 @@ var flock = flock || {};
107 107
         return buf;
108 108
     };
109 109
     
  110
+    flock.generate.constant = function (bufOrSize, val) {
  111
+        var buf = typeof (bufOrSize) === "number" ? new Float32Array(bufOrSize) : bufOrSize,
  112
+            i;
  113
+        for (i = 0; i < buf.length; i++) {
  114
+            buf[i] = val;
  115
+        }
  116
+        return buf;
  117
+    };
  118
+    
110 119
     flock.generate.silence = function (bufOrSize) {
111 120
         if (typeof (bufOrSize) === "number") {
112 121
             return new Float32Array(bufOrSize);
57  flocking/flocking-ugens.js
@@ -196,6 +196,63 @@ var flock = flock || {};
196 196
     });
197 197
 
198 198
 
  199
+    flock.ugen.math = function (inputs, output, options) {
  200
+        var that = flock.ugen(inputs, output, options);
  201
+        that.expandedSource = new Float32Array(that.options.audioSettings.rates.control);
  202
+
  203
+        that.krSourceKrInputGen = function () {
  204
+            var op = that.activeInput,
  205
+                input = that.inputs[op],
  206
+                sourceBuf = flock.generate(that.expandedSource, that.inputs.source.output[0]);
  207
+            DSP[op](that.output, sourceBuf, input.output[0]);
  208
+        };
  209
+        
  210
+        that.krSourceArInputGen = function () {
  211
+            var op = that.activeInput,
  212
+                input = that.inputs[op],
  213
+                sourceBuf = flock.generate(that.expandedSource, that.inputs.source.output[0]);
  214
+            DSP[op](that.output, sourceBuf, input.output);
  215
+        };
  216
+        
  217
+        that.arSourceKrInputGen = function () {
  218
+            var op = that.activeInput,
  219
+                input = that.inputs[op],
  220
+                sourceBuf = that.inputs.source.output;
  221
+            DSP[op](that.output, sourceBuf, input.output[0]);
  222
+        };
  223
+        
  224
+        that.arSourceArInputGen = function () {
  225
+            var op = that.activeInput,
  226
+                input = that.inputs[op];
  227
+            DSP[op](that.output, that.inputs.source.output, input.output);
  228
+        };
  229
+        
  230
+        that.onInputChanged = function () {
  231
+            // Find the first input and use it. Multiple inputters, beware.
  232
+            // TODO: Support multiple operations.
  233
+            var inputs = Object.keys(that.inputs),
  234
+                i,
  235
+                input,
  236
+                isInputAudioRate;
  237
+            
  238
+            for (i = 0; i < inputs.length; i++) {
  239
+                input = inputs[i];
  240
+                if (input !== "source") {
  241
+                    that.activeInput = input;
  242
+                    isInputAudioRate = that.inputs[input].rate === "audio";
  243
+                    that.gen = that.inputs.source.rate === "audio" ?
  244
+                        (isInputAudioRate ? that.arSourceArInputGen : that.arSourceKrInputGen) :
  245
+                        (isInputAudioRate ? that.krSourceArInputGen : that.krSourceKrInputGen);
  246
+                    break;
  247
+                }
  248
+            }
  249
+        };
  250
+        
  251
+        that.onInputChanged();
  252
+        return that;
  253
+    };
  254
+    
  255
+    
199 256
     flock.ugen.sum = function (inputs, output, options) {
200 257
         var that = flock.ugen(inputs, output, options);
201 258
         
4  tests/flocking/html/ugen-test.html
@@ -8,10 +8,12 @@
8 8
         <script src="../../third-party/qunit/js/qunit.js"></script>
9 9
 
10 10
         <script src="../../../third-party/jquery/js/jquery.min.js"></script>
  11
+        <script src="../../../third-party/dspapi/js/dspapi.js"></script>
  12
+        
11 13
         <script src="../../../flocking/flocking-core.js"></script>
12 14
         <script src="../../../flocking/flocking-ugens.js"></script>
13 15
         <script src="../../../flocking/flocking-parser.js"></script>
14  
-                
  16
+        
15 17
         <script src="../js/flocking-test-utils.js"></script>
16 18
         <script src="../js/ugen-test.js"></script>
17 19
     </head>
9  tests/flocking/js/flocking-test-utils.js
@@ -42,14 +42,7 @@ var flock = flock || {};
42 42
         return new Float32Array(buf);
43 43
     };
44 44
     
45  
-    flock.test.constantBuffer = function (size, val) {
46  
-        var buf = new Float32Array(size),
47  
-            i;
48  
-        for (i = 0; i < size; i++) {
49  
-            buf[i] = val;
50  
-        }
51  
-        return buf;
52  
-    };
  45
+    flock.test.constantBuffer = flock.generate.constant;
53 46
     
54 47
     flock.test.assertArrayEquals = function (actual, expected, msg) {
55 48
         var i;
88  tests/flocking/js/ugen-test.js
@@ -1019,4 +1019,92 @@ flock.test = flock.test || {};
1019 1019
         deepEqual(normalizer.output, expected,
1020 1020
             "When the 'max' input is changed to 0.5, the signal should be normalized to 0.5");
1021 1021
     });
  1022
+    
  1023
+    module("flock.ugen.math() tests");
  1024
+    
  1025
+    var testMath = function (synthDef, expected, msg) {
  1026
+        synthDef.id = "math";
  1027
+        var synth = flock.synth(synthDef),
  1028
+            math = synth.ugens.named.math;
  1029
+        math.gen();
  1030
+        deepEqual(math.output, expected, msg);
  1031
+    };
  1032
+    
  1033
+    test("flock.ugen.math()", function () {
  1034
+        testMath({
  1035
+            ugen: "flock.ugen.math",
  1036
+            inputs: {
  1037
+                source: 2,
  1038
+                add: 5
  1039
+            }
  1040
+        }, flock.test.constantBuffer(64, 7), "Value add");
  1041
+        
  1042
+        testMath({
  1043
+            ugen: "flock.ugen.math",
  1044
+            inputs: {
  1045
+                source: 3,
  1046
+                sub: 2
  1047
+            }
  1048
+        }, flock.test.constantBuffer(64, 1), "Value subtract");
  1049
+        
  1050
+        testMath({
  1051
+            ugen: "flock.ugen.math",
  1052
+            inputs: {
  1053
+                source: 3,
  1054
+                mul: 2
  1055
+            }
  1056
+        }, flock.test.constantBuffer(64, 6), "Value multiply");
  1057
+        
  1058
+        testMath({
  1059
+            ugen: "flock.ugen.math",
  1060
+            inputs: {
  1061
+                source: 3,
  1062
+                div: 2
  1063
+            }
  1064
+        }, flock.test.constantBuffer(64, 1.5), "Value divide");
  1065
+        
  1066
+        var incBuffer = flock.generate(64, function (i) {
  1067
+            return i + 1;
  1068
+        });
  1069
+        
  1070
+        var expected = flock.generate(64, function (i) {
  1071
+            return i + 4;
  1072
+        });
  1073
+        
  1074
+        var krArUGenDef = {
  1075
+            ugen: "flock.ugen.math",
  1076
+            inputs: {
  1077
+                source: {
  1078
+                    ugen: "flock.test.mockUGen",
  1079
+                    rate: "audio",
  1080
+                    options: {
  1081
+                        buffer: incBuffer
  1082
+                    }
  1083
+                },
  1084
+                add: 3
  1085
+            }
  1086
+        };
  1087
+        
  1088
+        testMath(krArUGenDef, expected, "Audio rate source, value add");
  1089
+        
  1090
+        krArUGenDef.inputs.source.rate = "control";
  1091
+        testMath(krArUGenDef, flock.test.constantBuffer(64, 4), "Control rate source, value add");
  1092
+        
  1093
+        krArUGenDef.inputs.add = {
  1094
+            ugen: "flock.test.mockUGen",
  1095
+            rate: "control",
  1096
+            options: {
  1097
+                buffer: incBuffer
  1098
+            }
  1099
+        };
  1100
+        testMath(krArUGenDef, flock.test.constantBuffer(64, 2), "Control rate source, control rate add.");
  1101
+        
  1102
+        krArUGenDef.inputs.source.rate = "audio";
  1103
+        krArUGenDef.inputs.add.rate = "audio";
  1104
+        testMath(krArUGenDef, flock.generate(64, function (i) {
  1105
+            var j = i + 1;
  1106
+            return j + j;
  1107
+        }), "Audio rate source, audio rate add.");
  1108
+    });
  1109
+    
1022 1110
 }());
1,042  third-party/dspapi/js/dspapi.js
... ...
@@ -0,0 +1,1042 @@
  1
+// -*- mode: javascript; tab-width: 2; indent-tabs-mode: nil; -*-
  2
+
  3
+//------------------------------------------------------------------------------
  4
+// DSP API - JavaScript shim
  5
+//
  6
+// Copyright (C) 2012 Marcus Geelnard
  7
+//
  8
+// Permission is hereby granted, free of charge, to any person obtaining a copy
  9
+// of this software and associated documentation files (the "Software"), to
  10
+// deal in the Software without restriction, including without limitation the
  11
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  12
+// sell copies of the Software, and to permit persons to whom the Software is
  13
+// furnished to do so, subject to the following conditions:
  14
+//
  15
+// The above copyright notice and this permission notice shall be included in
  16
+// all copies or substantial portions of the Software.
  17
+//
  18
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  23
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  24
+// IN THE SOFTWARE.
  25
+//------------------------------------------------------------------------------
  26
+
  27
+"use strict";
  28
+
  29
+
  30
+//------------------------------------------------------------------------------
  31
+// interface DSP
  32
+//------------------------------------------------------------------------------
  33
+
  34
+(function () {
  35
+  if (window.DSP) return;
  36
+
  37
+  var DSP = {};
  38
+
  39
+  DSP.add = function (dst, x, y) {
  40
+    var k;
  41
+    if (y instanceof Float32Array)
  42
+      for (k = Math.min(dst.length, x.length, y.length) - 1; k >= 0; --k)
  43
+        dst[k] = x[k] + y[k];
  44
+    else
  45
+      for (k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  46
+        dst[k] = x[k] + y;
  47
+  };
  48
+
  49
+  DSP.sub = function (dst, x, y) {
  50
+    var k;
  51
+    if (y instanceof Float32Array)
  52
+      for (k = Math.min(dst.length, x.length, y.length) - 1; k >= 0; --k)
  53
+        dst[k] = x[k] - y[k];
  54
+    else
  55
+      for (k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  56
+        dst[k] = x[k] - y;
  57
+  };
  58
+
  59
+  DSP.mul = function (dst, x, y) {
  60
+    var k;
  61
+    if (y instanceof Float32Array)
  62
+      for (k = Math.min(dst.length, x.length, y.length) - 1; k >= 0; --k)
  63
+        dst[k] = x[k] * y[k];
  64
+    else
  65
+      for (k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  66
+        dst[k] = x[k] * y;
  67
+  };
  68
+
  69
+  DSP.mulCplx = function (dstReal, dstImag, xReal, xImag, yReal, yImag) {
  70
+    var k, xr, xi, yr, yi;
  71
+    if (yReal instanceof Float32Array)
  72
+      for (k = Math.min(dstReal.length, dstImag.length, xReal.length, xImag.length, yReal.length, yImag.length) - 1; k >= 0; --k) {
  73
+        xr = xReal[k], xi = xImag[k], yr = yReal[k], yi = yImag[k];
  74
+        dstReal[k] = xr * yr - xi * yi;
  75
+        dstImag[k] = xr * yi + xi * yr;
  76
+      }
  77
+    else
  78
+      for (k = Math.min(dstReal.length, dstImag.length, xReal.length, xImag.length) - 1; k >= 0; --k) {
  79
+        xr = xReal[k], xi = xImag[k];
  80
+        dstReal[k] = xr * yReal - xi * yImag;
  81
+        dstImag[k] = xr * yImag + xi * yReal;
  82
+      }
  83
+  };
  84
+
  85
+  DSP.div = function (dst, x, y) {
  86
+    var k;
  87
+    if (y instanceof Float32Array)
  88
+      for (k = Math.min(dst.length, x.length, y.length) - 1; k >= 0; --k)
  89
+        dst[k] = x[k] / y[k];
  90
+    else
  91
+      for (k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  92
+        dst[k] = x[k] / y;
  93
+  };
  94
+
  95
+  DSP.divCplx = function (dstReal, dstImag, xReal, xImag, yReal, yImag) {
  96
+    var k, xr, xi, yr, yi, denom;
  97
+    if (yReal instanceof Float32Array)
  98
+      for (k = Math.min(dstReal.length, dstImag.length, xReal.length, xImag.length, yReal.length, yImag.length) - 1; k >= 0; --k) {
  99
+        xr = xReal[k], xi = xImag[k], yr = yReal[k], yi = yImag[k];
  100
+        denom = 1 / (yr * yr + yi * yi);
  101
+        dstReal[k] = (xr * yr + xi * yi) * denom;
  102
+        dstImag[k] = (xi * yr - xr * yi) * denom;
  103
+      }
  104
+    else {
  105
+      denom = 1 / (yReal * yReal + yImag * yImag);
  106
+      for (k = Math.min(dstReal.length, dstImag.length, xReal.length, xImag.length) - 1; k >= 0; --k) {
  107
+        xr = xReal[k], xi = xImag[k];
  108
+        dstReal[k] = (xr * yReal + xi * yImag) * denom;
  109
+        dstImag[k] = (xi * yReal - xr * yImag) * denom;
  110
+      }
  111
+    }
  112
+  };
  113
+
  114
+  DSP.madd = function (dst, x, y, z) {
  115
+    var k;
  116
+    if (z instanceof Float32Array)
  117
+      for (k = Math.min(dst.length, x.length, y.length, z.length) - 1; k >= 0; --k)
  118
+        dst[k] = x[k] + y[k] * z[k];
  119
+    else
  120
+      for (k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  121
+        dst[k] = x[k] + y[k] * z;
  122
+  };
  123
+
  124
+  DSP.abs = function (dst, x) {
  125
+    for (var k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  126
+      dst[k] = Math.abs(x[k]);
  127
+  };
  128
+
  129
+  DSP.absCplx = function (dst, real, imag) {
  130
+    for (var k = Math.min(dst.length, real.length, imag.length) - 1; k >= 0; --k)
  131
+      dst[k] = Math.sqrt(real[k] * real[k] + imag[k] * imag[k]);
  132
+  };
  133
+
  134
+  DSP.acos = function (dst, x) {
  135
+    for (var k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  136
+      dst[k] = Math.acos(x[k]);
  137
+  };
  138
+
  139
+  DSP.asin = function (dst, x) {
  140
+    for (var k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  141
+      dst[k] = Math.asin(x[k]);
  142
+  };
  143
+
  144
+  DSP.atan = function (dst, x) {
  145
+    for (var k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  146
+      dst[k] = Math.atan(x[k]);
  147
+  };
  148
+
  149
+  DSP.atan2 = function (dst, y, x) {
  150
+    for (var k = Math.min(dst.length, x.length, y.length) - 1; k >= 0; --k)
  151
+      dst[k] = Math.atan2(y[k], x[k]);
  152
+  };
  153
+
  154
+  DSP.ceil = function (dst, x) {
  155
+    for (var k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  156
+      dst[k] = Math.ceil(x[k]);
  157
+  };
  158
+
  159
+  DSP.cos = function (dst, x) {
  160
+    for (var k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  161
+      dst[k] = Math.cos(x[k]);
  162
+  };
  163
+
  164
+  DSP.exp = function (dst, x) {
  165
+    for (var k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  166
+      dst[k] = Math.exp(x[k]);
  167
+  };
  168
+
  169
+  DSP.floor = function (dst, x) {
  170
+    for (var k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  171
+      dst[k] = Math.floor(x[k]);
  172
+  };
  173
+
  174
+  DSP.log = function (dst, x) {
  175
+    for (var k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  176
+      dst[k] = Math.log(x[k]);
  177
+  };
  178
+
  179
+  DSP.max = function (x) {
  180
+    var ret = -Infinity;
  181
+    for (var k = x.length - 1; k >= 0; --k) {
  182
+      var val = x[k];
  183
+      if (val > ret)
  184
+        ret = val;
  185
+    }
  186
+    return ret;
  187
+  };
  188
+
  189
+  DSP.min = function (x) {
  190
+    var ret = Infinity;
  191
+    for (var k = x.length - 1; k >= 0; --k) {
  192
+      var val = x[k];
  193
+      if (val < ret)
  194
+        ret = val;
  195
+    }
  196
+    return ret;
  197
+  };
  198
+
  199
+  DSP.pow = function (dst, x, y) {
  200
+    var k;
  201
+    if (y instanceof Float32Array)
  202
+      for (k = Math.min(dst.length, x.length, y.length) - 1; k >= 0; --k)
  203
+        dst[k] = Math.pow(x[k], y[k]);
  204
+    else {
  205
+      // Optimize for special cases
  206
+      if (y == 2)
  207
+        for (k = Math.min(dst.length, x.length) - 1; k >= 0; --k) {
  208
+          var val = x[k];
  209
+          dst[k] = val * val;
  210
+        }
  211
+      else if (y == 3)
  212
+        for (k = Math.min(dst.length, x.length) - 1; k >= 0; --k) {
  213
+          var val = x[k];
  214
+          dst[k] = val * val * val;
  215
+        }
  216
+      else if (y == 4)
  217
+        for (k = Math.min(dst.length, x.length) - 1; k >= 0; --k) {
  218
+          var val = x[k];
  219
+          val = val * val;
  220
+          dst[k] = val * val;
  221
+        }
  222
+      else if (y == -1)
  223
+        for (k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  224
+          dst[k] = 1 / x[k]
  225
+      else if (y == -2)
  226
+        for (k = Math.min(dst.length, x.length) - 1; k >= 0; --k) {
  227
+          var val = 1 / x[k];
  228
+          dst[k] = val * val;
  229
+        }
  230
+      else
  231
+        for (k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  232
+          dst[k] = Math.pow(x[k], y);
  233
+    }
  234
+  };
  235
+
  236
+  DSP.random = function (dst, low, high) {
  237
+    if (!low)
  238
+      low = 0;
  239
+    if (isNaN(parseFloat(high)))
  240
+      high = 1;
  241
+    var scale = high - low;
  242
+    for (var k = dst.length - 1; k >= 0; --k)
  243
+      dst[k] = Math.random() * scale + low;
  244
+  };
  245
+
  246
+  DSP.round = function (dst, x) {
  247
+    for (var k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  248
+      dst[k] = Math.round(x[k]);
  249
+  };
  250
+
  251
+  DSP.sin = function (dst, x) {
  252
+    for (var k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  253
+      dst[k] = Math.sin(x[k]);
  254
+  };
  255
+
  256
+  DSP.sqrt = function (dst, x) {
  257
+    for (var k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  258
+      dst[k] = Math.sqrt(x[k]);
  259
+  };
  260
+
  261
+  DSP.tan = function (dst, x) {
  262
+    for (var k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  263
+      dst[k] = Math.tan(x[k]);
  264
+  };
  265
+
  266
+  DSP.clamp = function (dst, x, xMin, xMax) {
  267
+    for (var k = Math.min(dst.length, x.length) - 1; k >= 0; --k) {
  268
+      var val = x[k];
  269
+      dst[k] = val < xMin ? xMin : val > xMax ? xMax : val;
  270
+    }
  271
+  };
  272
+
  273
+  DSP.fract = function (dst, x) {
  274
+    for (var k = Math.min(dst.length, x.length) - 1; k >= 0; --k) {
  275
+      var val = x[k];
  276
+      dst[k] = val - Math.floor(val);
  277
+    }
  278
+  };
  279
+
  280
+  DSP.ramp = function (dst, first, last) {
  281
+    var maxIdx = dst.length - 1;
  282
+    if (maxIdx >= 0)
  283
+      dst[0] = first;
  284
+    if (maxIdx > 0) {
  285
+      var step = (last - first) / maxIdx;
  286
+      for (var k = 1; k <= maxIdx; ++k)
  287
+        dst[k] = first + step * k;
  288
+    }
  289
+  };
  290
+
  291
+  DSP.sign = function (dst, x) {
  292
+    for (var k = Math.min(dst.length, x.length) - 1; k >= 0; --k)
  293
+      dst[k] = x[k] < 0 ? -1 : 1;
  294
+  };
  295
+
  296
+  DSP.sum = function (x) {
  297
+    var ret = 0;
  298
+    for (var k = x.length - 1; k >= 0; --k)
  299
+      ret += x[k];
  300
+    return ret;
  301
+  };
  302
+
  303
+  DSP.sampleLinear = function (dst, x, t, repeat) {
  304
+    var xLen = x.length, maxIdx = xLen - 1;
  305
+    if (repeat)
  306
+      for (var k = Math.min(dst.length, t.length) - 1; k >= 0; --k) {
  307
+        var t2 = t[k];
  308
+        t2 = t2 - Math.floor(t2/xLen) * xLen;
  309
+        var idx = Math.floor(t2);
  310
+        var w = t2 - idx;
  311
+        var p1 = x[idx];
  312
+        var p2 = x[idx < maxIdx ? idx + 1 : 0];
  313
+        dst[k] = p1 + w * (p2 - p1);
  314
+      }
  315
+    else
  316
+      for (var k = Math.min(dst.length, t.length) - 1; k >= 0; --k) {
  317
+        var t2 = t[k];
  318
+        t2 = t2 < 0 ? 0 : t2 > maxIdx ? maxIdx : t2;
  319
+        var idx = Math.floor(t2);
  320
+        var w = t2 - idx;
  321
+        var p1 = x[idx];
  322
+        var p2 = x[idx < maxIdx ? idx + 1 : maxIdx];
  323
+        dst[k] = p1 + w * (p2 - p1);
  324
+      }
  325
+  };
  326
+
  327
+  DSP.sampleCubic = function (dst, x, t, repeat) {
  328
+    var xLen = x.length, maxIdx = xLen - 1;
  329
+    if (repeat)
  330
+      for (var k = Math.min(dst.length, t.length) - 1; k >= 0; --k) {
  331
+        var t2 = t[k];
  332
+        t2 = t2 - Math.floor(t2/xLen) * xLen;
  333
+        var idx = Math.floor(t2);
  334
+        var w = t2 - idx;
  335
+        var w2 = w * w;
  336
+        var w3 = w2 * w;
  337
+        var h2 = -2*w3 + 3*w2;
  338
+        var h1 = 1 - h2;
  339
+        var h4 = w3 - w2;
  340
+        var h3 = h4 - w2 + w;
  341
+        var p1 = x[idx > 0 ? idx - 1 : maxIdx];
  342
+        var p2 = x[idx];
  343
+        var p3 = x[idx < maxIdx ? idx + 1 : 0];
  344
+        var p4 = x[idx < maxIdx - 1 ? idx + 2 : (idx + 2 - Math.floor((idx + 2)/xLen) * xLen)];
  345
+        dst[k] = h1 * p2 + h2 * p3 + 0.5 * (h3 * (p3 - p1) + h4 * (p4 - p2));
  346
+      }
  347
+    else
  348
+      for (var k = Math.min(dst.length, t.length) - 1; k >= 0; --k) {
  349
+        var t2 = t[k];
  350
+        t2 = t2 < 0 ? 0 : t2 > maxIdx ? maxIdx : t2;
  351
+        var idx = Math.floor(t2);
  352
+        var w = t2 - idx;
  353
+        var w2 = w * w;
  354
+        var w3 = w2 * w;
  355
+        var h2 = -2*w3 + 3*w2;
  356
+        var h1 = 1 - h2;
  357
+        var h4 = w3 - w2;
  358
+        var h3 = h4 - w2 + w;
  359
+        var p1 = x[idx > 0 ? idx - 1 :  0];
  360
+        var p2 = x[idx];
  361
+        var p3 = x[idx < maxIdx ? idx + 1 : maxIdx];
  362
+        var p4 = x[idx < maxIdx - 1 ? idx + 2 : maxIdx];
  363
+        dst[k] = h1 * p2 + h2 * p3 + 0.5 * (h3 * (p3 - p1) + h4 * (p4 - p2));
  364
+      }
  365
+  };
  366
+
  367
+  DSP.pack = function (dst, offset, stride, src1, src2, src3, src4) {
  368
+    var dstCount = Math.floor((dst.length - offset) / stride);
  369
+    var count = Math.min(dstCount, src1.length);
  370
+    if (src2)
  371
+      if (src3)
  372
+        if (src4)
  373
+          for (var k = 0; k < count; ++k) {
  374
+            dst[offset] = src1[k];
  375
+            dst[offset + 1] = src2[k];
  376
+            dst[offset + 2] = src3[k];
  377
+            dst[offset + 3] = src4[k];
  378
+            offset += stride;
  379
+          }
  380
+        else
  381
+          for (var k = 0; k < count; ++k) {
  382
+            dst[offset] = src1[k];
  383
+            dst[offset + 1] = src2[k];
  384
+            dst[offset + 2] = src3[k];
  385
+            offset += stride;
  386
+          }
  387
+      else
  388
+        for (var k = 0; k < count; ++k) {
  389
+          dst[offset] = src1[k];
  390
+          dst[offset + 1] = src2[k];
  391
+          offset += stride;
  392
+        }
  393
+    else
  394
+      for (var k = 0; k < count; ++k) {
  395
+        dst[offset] = src1[k];
  396
+        offset += stride;
  397
+      }
  398
+  };
  399
+
  400
+  DSP.unpack = function (src, offset, stride, dst1, dst2, dst3, dst4) {
  401
+    var srcCount = Math.floor((src.length - offset) / stride);
  402
+    var count = Math.min(srcCount, dst1.length);
  403
+    if (dst2)
  404
+      if (dst3)
  405
+        if (dst4)
  406
+          for (var k = 0; k < count; ++k) {
  407
+            dst1[k] = src[offset];
  408
+            dst2[k] = src[offset + 1];
  409
+            dst3[k] = src[offset + 2];
  410
+            dst4[k] = src[offset + 3];
  411
+            offset += stride;
  412
+          }
  413
+        else
  414
+          for (var k = 0; k < count; ++k) {
  415
+            dst1[k] = src[offset];
  416
+            dst2[k] = src[offset + 1];
  417
+            dst3[k] = src[offset + 2];
  418
+            offset += stride;
  419
+          }
  420
+      else
  421
+        for (var k = 0; k < count; ++k) {
  422
+          dst1[k] = src[offset];
  423
+          dst2[k] = src[offset + 1];
  424
+          offset += stride;
  425
+        }
  426
+    else
  427
+      for (var k = 0; k < count; ++k) {
  428
+        dst1[k] = src[offset];
  429
+        offset += stride;
  430
+      }
  431
+  };
  432
+
  433
+  window.DSP = DSP;
  434
+})();
  435
+
  436
+
  437
+//------------------------------------------------------------------------------
  438
+// interface Filter
  439
+//------------------------------------------------------------------------------
  440
+
  441
+(function () {
  442
+  if (window.Filter) return;
  443
+
  444
+  var Filter = function (bSize, aSize) {
  445
+    if (isNaN(parseFloat(bSize)) || !isFinite(bSize))
  446
+      bSize = 1;
  447
+    if (!aSize)
  448
+      aSize = 0;
  449
+    this.b = new Float32Array(bSize);
  450
+    this.b[0] = 1;
  451
+    this.a = new Float32Array(aSize);
  452
+    this._bHist = new Float32Array(bSize);
  453
+    this._aHist = new Float32Array(aSize);
  454
+  };
  455
+
  456
+  Filter.prototype.filter = function (dst, x) {
  457
+    // Put commonly accessed objects and properties in local variables
  458
+    var a = this.a, aLen = a.length,
  459
+        b = this.b, bLen = b.length,
  460
+        aHist = this._aHist, bHist = this._bHist,
  461
+        xLen = x.length, dstLen = dst.length;
  462
+
  463
+    // FIXME: Optimize for long FIR filters
  464
+
  465
+    // Perform run-in part using the history (slow)
  466
+    var bHistRunIn = bLen - 1;
  467
+    var aHistRunIn = aLen;
  468
+    var k;
  469
+    for (k = 0; (bHistRunIn || aHistRunIn) && k < xLen; ++k) {
  470
+      var m, noHistLen;
  471
+
  472
+      // FIR part
  473
+      noHistLen = bLen - bHistRunIn;
  474
+      bHistRunIn && bHistRunIn--;
  475
+      var res = b[0] * x[k];
  476
+      for (m = 1; m < noHistLen; ++m)
  477
+        res += b[m] * x[k - m];
  478
+      for (; m < bLen; ++m)
  479
+        res += b[m] * bHist[m - noHistLen];
  480
+
  481
+      // Recursive part
  482
+      noHistLen = aLen - aHistRunIn;
  483
+      aHistRunIn && aHistRunIn--;
  484
+      for (m = 0; m < noHistLen; ++m)
  485
+        res -= a[m] * dst[k - 1 - m];
  486
+      for (; m < aLen; ++m)
  487
+        res -= a[m] * aHist[m - noHistLen];
  488
+
  489
+      dst[k] = res;
  490
+    }
  491
+
  492
+    // Perform history-free part (fast)
  493
+    if (bLen == 3 && aLen == 2) {
  494
+      // Optimized special case: biquad filter
  495
+      var b0 = b[0], b1 = b[1], b2 = b[2], a1 = a[0], a2 = a[1];
  496
+      var x0 = x[k-1], x1 = x[k-2], x2;
  497
+      var y0 = dst[k-1], y1 = dst[k-2], y2;
  498
+      for (; k < xLen; ++k) {
  499
+        x2 = x1;
  500
+        x1 = x0;
  501
+        x0 = x[k];
  502
+        y2 = y1;
  503
+        y1 = y0;
  504
+        y0 = b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2;
  505
+        dst[k] = y0;
  506
+      }
  507
+    }
  508
+    else {
  509
+      // Generic case
  510
+      for (; k < xLen; ++k) {
  511
+        var m;
  512
+
  513
+        // FIR part
  514
+        var res = b[0] * x[k];
  515
+        for (m = 1; m < bLen; ++m)
  516
+          res += b[m] * x[k - m];
  517
+
  518
+        // Recursive part
  519
+        for (m = 0; m < aLen; ++m)
  520
+          res -= a[m] * dst[k - 1 - m];
  521
+
  522
+        dst[k] = res;
  523
+      }
  524
+    }
  525
+
  526
+    // Update history state
  527
+    var histCopy = Math.min(bLen - 1, xLen);
  528
+    for (k = bLen - 2; k >= histCopy; --k)
  529
+      bHist[k] = bHist[k - histCopy];
  530
+    for (k = 0; k < histCopy; ++k)
  531
+      bHist[k] = x[xLen - 1 - k];
  532
+    histCopy = Math.min(aLen, dstLen);
  533
+    for (k = aLen - 1; k >= histCopy; --k)
  534
+      aHist[k] = aHist[k - histCopy];
  535
+    for (k = 0; k < histCopy; ++k)
  536
+      aHist[k] = dst[xLen - 1 - k];
  537
+  };
  538
+
  539
+  Filter.prototype.clearHistory = function () {
  540
+    for (var k = this._bHist.length - 1; k >= 0; --k)
  541
+      this._bHist[k] = 0;
  542
+    for (var k = this._aHist.length - 1; k >= 0; --k)
  543
+      this._aHist[k] = 0;
  544
+  };
  545
+
  546
+  window.Filter = Filter;
  547
+})();
  548
+
  549
+
  550
+//------------------------------------------------------------------------------
  551
+// interface FFT
  552
+//
  553
+// NOTE: This is essentially a hand-translation of the C language Kiss FFT
  554
+// library, copyright by Mark Borgerding, relicensed with permission from the
  555
+// author.
  556
+//
  557
+// The algorithm implements mixed radix FFT and supports transforms of any size
  558
+// (not just powers of 2). For optimal performance, use sizes that can be
  559
+// factorized into factors 2, 3, 4 and 5.
  560
+//------------------------------------------------------------------------------
  561
+
  562
+(function () {
  563
+  if (window.FFT) return;
  564
+
  565
+  var butterfly2 = function (outRe, outIm, outIdx, stride, twRe, twIm, m) {
  566
+    var scratch0Re, scratch0Im,
  567
+        out0Re, out0Im, out1Re, out1Im,
  568
+        tRe, tIm;
  569
+
  570
+    var tw1 = 0,
  571
+        idx0 = outIdx,
  572
+        idx1 = outIdx + m;
  573
+
  574
+    var idx0End = idx0 + m;
  575
+    while (idx0 < idx0End) {
  576
+      // out0 = out[idx0] / 2
  577
+      out0Re = outRe[idx0] * 0.5;
  578
+      out0Im = outIm[idx0] * 0.5;
  579
+      // out1 = out[idx1] / 2
  580
+      out1Re = outRe[idx1] * 0.5;
  581
+      out1Im = outIm[idx1] * 0.5;
  582
+
  583
+      // scratch0 = out1 * tw[tw1]
  584
+      tRe = twRe[tw1]; tIm = twIm[tw1];
  585
+      scratch0Re = out1Re * tRe - out1Im * tIm;
  586
+      scratch0Im = out1Re * tIm + out1Im * tRe;
  587
+
  588
+      // out[idx1] = out0 - scratch0
  589
+      outRe[idx1] = out0Re - scratch0Re;
  590
+      outIm[idx1] = out0Im - scratch0Im;
  591
+
  592
+      // out[idx0] = out0 + scratch0
  593
+      outRe[idx0] = out0Re + scratch0Re;
  594
+      outIm[idx0] = out0Im + scratch0Im;
  595
+
  596
+      tw1 += stride;
  597
+      ++idx0; ++idx1;
  598
+    }
  599
+  };
  600
+
  601
+  var butterfly3 = function (outRe, outIm, outIdx, stride, twRe, twIm, m) {
  602
+    var scratch0Re, scratch0Im, scratch1Re, scratch1Im,
  603
+        scratch2Re, scratch2Im, scratch3Re, scratch3Im,
  604
+        out0Re, out0Im, out1Re, out1Im, out2Re, out2Im,
  605
+        tRe, tIm;
  606
+
  607
+    var tw1 = 0,
  608
+        tw2 = 0,
  609
+        stride2 = 2 * stride,
  610
+        idx0 = outIdx,
  611
+        idx1 = outIdx + m,
  612
+        idx2 = outIdx + 2 * m;
  613
+
  614
+    var epi3Im = twIm[stride*m];
  615
+
  616
+    var div3 = 1 / 3;
  617
+    var idx0End = idx0 + m;
  618
+    while (idx0 < idx0End) {
  619
+      // out0 = out[idx0] / 3
  620
+      out0Re = outRe[idx0] * div3;
  621
+      out0Im = outIm[idx0] * div3;
  622
+      // out1 = out[idx1] / 3
  623
+      out1Re = outRe[idx1] * div3;
  624
+      out1Im = outIm[idx1] * div3;
  625
+      // out2 = out[idx2] / 3
  626
+      out2Re = outRe[idx2] * div3;
  627
+      out2Im = outIm[idx2] * div3;
  628
+
  629
+      // scratch1 = out1 * tw[tw1]
  630
+      tRe = twRe[tw1]; tIm = twIm[tw1];
  631
+      scratch1Re = out1Re * tRe - out1Im * tIm;
  632
+      scratch1Im = out1Re * tIm + out1Im * tRe;
  633
+
  634
+      // scratch2 = out2 * tw[tw2]
  635
+      tRe = twRe[tw2]; tIm = twIm[tw2];
  636
+      scratch2Re = out2Re * tRe - out2Im * tIm;
  637
+      scratch2Im = out2Re * tIm + out2Im * tRe;
  638
+
  639
+      // scratch3 = scratch1 + scratch2
  640
+      scratch3Re = scratch1Re + scratch2Re;
  641
+      scratch3Im = scratch1Im + scratch2Im;
  642
+
  643
+      // scratch0 = scratch1 - scratch2
  644
+      scratch0Re = scratch1Re - scratch2Re;
  645
+      scratch0Im = scratch1Im - scratch2Im;
  646
+
  647
+      // out1 = out0 - scratch3 / 2
  648
+      out1Re = out0Re - scratch3Re * 0.5;
  649
+      out1Im = out0Im - scratch3Im * 0.5;
  650
+
  651
+      // scratch0 *= epi3.i
  652
+      scratch0Re *= epi3Im;
  653
+      scratch0Im *= epi3Im;
  654
+
  655
+      // out[idx0] = out0 + scratch3
  656
+      outRe[idx0] = out0Re + scratch3Re;
  657
+      outIm[idx0] = out0Im + scratch3Im;
  658
+
  659
+      outRe[idx2] = out1Re + scratch0Im;
  660
+      outIm[idx2] = out1Im - scratch0Re;
  661
+
  662
+      outRe[idx1] = out1Re - scratch0Im;
  663
+      outIm[idx1] = out1Im + scratch0Re;
  664
+
  665
+      tw1 += stride; tw2 += stride2;
  666
+      ++idx0; ++idx1; ++idx2;
  667
+    }
  668
+  };
  669
+
  670
+  var butterfly4 = function (outRe, outIm, outIdx, stride, twRe, twIm, m, inverse) {
  671
+    var scratch0Re, scratch0Im, scratch1Re, scratch1Im, scratch2Re, scratch2Im,
  672
+        scratch3Re, scratch3Im, scratch4Re, scratch4Im, scratch5Re, scratch5Im,
  673
+        out0Re, out0Im, out1Re, out1Im, out2Re, out2Im, out3Re, out3Im,
  674
+        tRe, tIm;
  675
+
  676
+    var tw1 = 0,
  677
+        tw2 = 0,
  678
+        tw3 = 0,
  679
+        stride2 = 2 * stride,
  680
+        stride3 = 3 * stride,
  681
+        idx0 = outIdx,
  682
+        idx1 = outIdx + m,
  683
+        idx2 = outIdx + 2 * m,
  684
+        idx3 = outIdx + 3 * m;
  685
+
  686
+    var div4 = 1 / 4;
  687
+    var idx0End = idx0 + m;
  688
+    while (idx0 < idx0End) {
  689
+      // out0 = out[idx0] / 4
  690
+      out0Re = outRe[idx0] * div4;
  691
+      out0Im = outIm[idx0] * div4;
  692
+      // out1 = out[idx1] / 4
  693
+      out1Re = outRe[idx1] * div4;
  694
+      out1Im = outIm[idx1] * div4;
  695
+      // out2 = out[idx2] / 4
  696
+      out2Re = outRe[idx2] * div4;
  697
+      out2Im = outIm[idx2] * div4;
  698
+      // out3 = out[idx3] / 4
  699
+      out3Re = outRe[idx3] * div4;
  700
+      out3Im = outIm[idx3] * div4;
  701
+
  702
+      // scratch0 = out1 * tw[tw1]
  703
+      tRe = twRe[tw1]; tIm = twIm[tw1];
  704
+      scratch0Re = out1Re * tRe - out1Im * tIm;
  705
+      scratch0Im = out1Re * tIm + out1Im * tRe;
  706
+
  707
+      // scratch1 = out2 * tw[tw2]
  708
+      tRe = twRe[tw2]; tIm = twIm[tw2];
  709
+      scratch1Re = out2Re * tRe - out2Im * tIm;
  710
+      scratch1Im = out2Re * tIm + out2Im * tRe;
  711
+
  712
+      // scratch2 = out3 * tw[tw3]
  713
+      tRe = twRe[tw3]; tIm = twIm[tw3];
  714
+      scratch2Re = out3Re * tRe - out3Im * tIm;
  715
+      scratch2Im = out3Re * tIm + out3Im * tRe;
  716
+
  717
+      // scratch5 = out0 - scratch1
  718
+      scratch5Re = out0Re - scratch1Re;
  719
+      scratch5Im = out0Im - scratch1Im;
  720
+
  721
+      // out0 += scratch1
  722
+      out0Re += scratch1Re;
  723
+      out0Im += scratch1Im;
  724
+
  725
+      // scratch3 = scratch0 + scratch2
  726
+      scratch3Re = scratch0Re + scratch2Re;
  727
+      scratch3Im = scratch0Im + scratch2Im;
  728
+
  729
+      // scratch4 = scratch0 - scratch2
  730
+      scratch4Re = scratch0Re - scratch2Re;
  731
+      scratch4Im = scratch0Im - scratch2Im;
  732
+
  733
+      // out[idx2] = out0 - scratch3
  734
+      outRe[idx2] = out0Re - scratch3Re;
  735
+      outIm[idx2] = out0Im - scratch3Im;
  736
+
  737
+      // out[idx0] = out0 + scratch3
  738
+      outRe[idx0] = out0Re + scratch3Re;
  739
+      outIm[idx0] = out0Im + scratch3Im;
  740
+
  741
+      if (inverse) {
  742
+        outRe[idx1] = scratch5Re - scratch4Im;
  743
+        outIm[idx1] = scratch5Im + scratch4Re;
  744
+        outRe[idx3] = scratch5Re + scratch4Im;
  745
+        outIm[idx3] = scratch5Im - scratch4Re;
  746
+      }
  747
+      else {
  748
+        outRe[idx1] = scratch5Re + scratch4Im;
  749
+        outIm[idx1] = scratch5Im - scratch4Re;
  750
+        outRe[idx3] = scratch5Re - scratch4Im;
  751
+        outIm[idx3] = scratch5Im + scratch4Re;
  752
+      }
  753
+
  754
+      tw1 += stride; tw2 += stride2; tw3 += stride3;
  755
+      ++idx0; ++idx1; ++idx2; ++idx3;
  756
+    }
  757
+  };
  758
+
  759
+  var butterfly5 = function (outRe, outIm, outIdx, stride, twRe, twIm, m) {
  760
+    var scratch0Re, scratch0Im, scratch1Re, scratch1Im, scratch2Re, scratch2Im,
  761
+        scratch3Re, scratch3Im, scratch4Re, scratch4Im, scratch5Re, scratch5Im,
  762
+        scratch6Re, scratch6Im, scratch7Re, scratch7Im, scratch8Re, scratch8Im,
  763
+        scratch9Re, scratch9Im, scratch10Re, scratch10Im, scratch11Re, scratch11Im,
  764
+        scratch12Re, scratch12Im,
  765
+        out0Re, out0Im, out1Re, out1Im, out2Re, out2Im, out3Re, out3Im, out4Re, out4Im,
  766
+        tRe, tIm;
  767
+
  768
+    var tw1 = 0,
  769
+        tw2 = 0,
  770
+        tw3 = 0,
  771
+        tw4 = 0,
  772
+        stride2 = 2 * stride,
  773
+        stride3 = 3 * stride,
  774
+        stride4 = 4 * stride;
  775
+
  776
+    var idx0 = outIdx,
  777
+        idx1 = outIdx + m,
  778
+        idx2 = outIdx + 2 * m,
  779
+        idx3 = outIdx + 3 * m,
  780
+        idx4 = outIdx + 4 * m;
  781
+
  782
+    // ya = tw[stride*m];
  783
+    var yaRe = twRe[stride * m],
  784
+        yaIm = twIm[stride * m];
  785
+    // yb = tw[stride*2*m];
  786
+    var ybRe = twRe[stride * 2 * m],
  787
+        ybIm = twIm[stride * 2 * m];
  788
+
  789
+    var div5 = 1 / 5;
  790
+    var idx0End = idx0 + m;
  791
+    while (idx0 < idx0End) {
  792
+      // out0 = out[idx0] / 5
  793
+      out0Re = outRe[idx0] * div5;
  794
+      out0Im = outIm[idx0] * div5;
  795
+      // out1 = out[idx1] / 5
  796
+      out1Re = outRe[idx1] * div5;
  797
+      out1Im = outIm[idx1] * div5;
  798
+      // out2 = out[idx2] / 5
  799
+      out2Re = outRe[idx2] * div5;
  800
+      out2Im = outIm[idx2] * div5;
  801
+      // out3 = out[idx3] / 5
  802
+      out3Re = outRe[idx3] * div5;
  803
+      out3Im = outIm[idx3] * div5;
  804
+      // out4 = out[idx4] / 5
  805
+      out4Re = outRe[idx4] * div5;
  806
+      out4Im = outIm[idx4] * div5;
  807
+
  808
+      // scratch0 = out0;
  809
+      scratch0Re = out0Re;
  810
+      scratch0Im = out0Im;
  811
+
  812
+      // scratch1 = out1 * tw[tw1]
  813
+      tRe = twRe[tw1]; tIm = twIm[tw1];
  814
+      scratch1Re = out1Re * tRe - out1Im * tIm;
  815
+      scratch1Im = out1Re * tIm + out1Im * tRe;
  816
+      // scratch2 = out2 * tw[tw2]
  817
+      tRe = twRe[tw2]; tIm = twIm[tw2];
  818
+      scratch2Re = out2Re * tRe - out2Im * tIm;
  819
+      scratch2Im = out2Re * tIm + out2Im * tRe;
  820
+      // scratch3 = out3 * tw[tw3]
  821
+      tRe = twRe[tw3]; tIm = twIm[tw3];
  822
+      scratch3Re = out3Re * tRe - out3Im * tIm;
  823
+      scratch3Im = out3Re * tIm + out3Im * tRe;
  824
+      // scratch4 = out4 * tw[tw4]
  825
+      tRe = twRe[tw4]; tIm = twIm[tw4];
  826
+      scratch4Re = out4Re * tRe - out4Im * tIm;
  827
+      scratch4Im = out4Re * tIm + out4Im * tRe;
  828
+
  829
+      // scratch7 = scratch1 + scratch4
  830
+      scratch7Re = scratch1Re + scratch4Re;
  831
+      scratch7Im = scratch1Im + scratch4Im;
  832
+      // scratch10 = scratch1 - scratch4
  833
+      scratch10Re = scratch1Re - scratch4Re;
  834
+      scratch10Im = scratch1Im - scratch4Im;
  835
+      // scratch8 = scratch2 + scratch2
  836
+      scratch8Re = scratch2Re + scratch3Re;
  837
+      scratch8Im = scratch2Im + scratch3Im;
  838
+      // scratch9 = scratch2 - scratch3
  839
+      scratch9Re = scratch2Re - scratch3Re;
  840
+      scratch9Im = scratch2Im - scratch3Im;
  841
+
  842
+      // out[idx0] = out0 + scratch7 + scratch8
  843
+      outRe[idx0] = out0Re + scratch7Re + scratch8Re;
  844
+      outIm[idx0] = out0Im + scratch7Im + scratch8Im;
  845
+
  846
+      scratch5Re = scratch0Re + scratch7Re * yaRe + scratch8Re * ybRe;
  847
+      scratch5Im = scratch0Im + scratch7Im * yaRe + scratch8Im * ybRe;
  848
+
  849
+      scratch6Re = scratch10Im * yaIm + scratch9Im * ybIm;
  850
+      scratch6Im = -scratch10Re * yaIm - scratch9Re * ybIm;
  851
+
  852
+      // out[idx1] = scratch5 - scratch6
  853
+      outRe[idx1] = scratch5Re - scratch6Re;
  854
+      outIm[idx1] = scratch5Im - scratch6Im;
  855
+      // out[idx4] = scratch5 + scratch6
  856
+      outRe[idx4] = scratch5Re + scratch6Re;
  857
+      outIm[idx4] = scratch5Im + scratch6Im;
  858
+
  859
+      scratch11Re = scratch0Re + scratch7Re * ybRe + scratch8Re * yaRe;
  860
+      scratch11Im = scratch0Im + scratch7Im * ybRe + scratch8Im * yaRe;
  861
+
  862
+      scratch12Re = -scratch10Im * ybIm + scratch9Im * yaIm;
  863
+      scratch12Im = scratch10Re * ybIm - scratch9Re * yaIm;
  864
+
  865
+      // out[idx2] = scratch11 + scratch12
  866
+      outRe[idx2] = scratch11Re + scratch12Re;
  867
+      outIm[idx2] = scratch11Im + scratch12Im;
  868
+      // out[idx3] = scratch11 - scratch12
  869
+      outRe[idx3] = scratch11Re - scratch12Re;
  870
+      outIm[idx3] = scratch11Im - scratch12Im;
  871
+
  872
+      tw1 += stride; tw2 += stride2; tw3 += stride3; tw4 += stride4;
  873
+      ++idx0; ++idx1; ++idx2; ++idx3; ++idx4;
  874
+    }
  875
+  };
  876
+
  877
+  var butterflyN = function (outRe, outIm, outIdx, stride, twRe, twIm, m, p, size) {
  878
+    var u, q1, q, idx0;
  879
+    var out0Re, out0Im, aRe, aIm, tRe, tIm;
  880
+
  881
+    // FIXME: Allocate statically
  882
+    var scratchRe = new Float32Array(p);
  883
+    var scratchIm = new Float32Array(p);
  884
+
  885
+    var pInv = 1 / p;
  886
+    for (u = 0; u < m; ++u) {
  887
+      idx0 = outIdx + u;
  888
+      for (q1 = 0; q1 < p; ++q1) {
  889
+        // scratch[q1] = out[idx0] / p
  890
+        scratchRe[q1] = outRe[idx0] * pInv;
  891
+        scratchIm[q1] = outIm[idx0] * pInv;
  892
+        idx0 += m;
  893
+      }
  894
+
  895
+      idx0 = outIdx + u;
  896
+      var tw1Incr = stride * u;
  897
+      for (q1 = 0; q1 < p; ++q1) {
  898
+        // out0 = scratch[0]
  899
+        out0Re = scratchRe[0];
  900
+        out0Im = scratchIm[0];
  901
+
  902
+        var tw1 = 0;
  903
+        for (q = 1; q < p; ++q) {
  904
+          tw1 += tw1Incr;
  905
+          if (tw1 >= size)
  906
+            tw1 -= size;
  907
+
  908
+          // out0 += scratch[q] * tw[tw1]
  909
+          aRe = scratchRe[q], aIm = scratchIm[q];
  910
+          tRe = twRe[tw1], tIm = twIm[tw1];
  911
+          out0Re += aRe * tRe - aIm * tIm;
  912
+          out0Im += aRe * tIm + aIm * tRe;
  913
+        }
  914
+
  915
+        // out[idx0] = out0
  916
+        outRe[idx0] = out0Re;
  917
+        outIm[idx0] = out0Im;
  918
+
  919
+        idx0 += m;
  920
+        tw1Incr += stride;
  921
+      }
  922
+    }
  923
+  };
  924
+
  925
+  var work = function (outRe, outIm, outIdx, fRe, fIm, fIdx, stride, inStride, factors, factorsIdx, twRe, twIm, size, inverse) {
  926
+    var p = factors[factorsIdx++];  // Radix
  927
+    var m = factors[factorsIdx++];  // Stage's FFT length / p
  928
+
  929
+    var outIdxBeg = outIdx;
  930
+    var outIdxEnd = outIdx + p * m;
  931
+
  932
+    var fIdxIncr = stride * inStride;