From 3df3b88c176048c5c8cb28bc59e436cf3f5f3030 Mon Sep 17 00:00:00 2001
From: danigb <danigb@gmail.com>
Date: Thu, 22 Dec 2016 02:16:32 +0100
Subject: [PATCH] Split source in modules. Generate API documentation. Add
 eslinter.

- Split the source code into modules inside `lib/`
- Generate browser ready distribution files into `dist/` (replaces the old `dsp.js` file)
- Add missing jsdoc documentation
- Generate API documentation into `docs/`
- Update the benchmarks code to use in node
- Update the examples to use the new `dsp.min.js` file
- Add eslint to make syntax coherent and prevent errors
- Add docs/README.md with the new install and development instructions
---
 .eslintrc.json                           |   21 +
 .gitignore                               |    2 +
 Makefile                                 |    6 +-
 bench/bench.js                           |    6 +-
 bench/deinterleave.js                    |   23 +-
 bench/dft.js                             |   12 +-
 bench/fft.js                             |   12 +-
 bench/index.js                           |    6 +
 bench/rfft.js                            |   12 +-
 dsp.js => dist/dsp.js                    | 3843 ++++++++++++----------
 dist/dsp.min.js                          |    3 +
 docs/API.md                              |  999 ++++++
 docs/README.md                           |   51 +
 examples/biquad.html                     |    2 +-
 examples/fft.html                        |   30 +-
 examples/filter.html                     |   26 +-
 examples/grapheq.html                    |    2 +-
 examples/nowave.html                     |   26 +-
 examples/rfft.html                       |    2 +-
 examples/sampler.html                    |    2 +-
 examples/squarewave.html                 |   40 +-
 examples/synthesizer.html                |  200 +-
 examples/vocoder.html                    |    2 +-
 lib/adsr.js                              |  134 +
 lib/biquad.js                            |  288 ++
 lib/dft.js                               |   57 +
 lib/dsp.js                               |  315 ++
 lib/fft.js                               |  198 ++
 lib/fourier.js                           |   57 +
 lib/graphical-eq.js                      |  117 +
 lib/iir-filter.js                        |  120 +
 lib/iir-filter2.js                       |   88 +
 lib/index.js                             |   34 +
 lib/multi-delay.js                       |  102 +
 lib/oscillator.js                        |  195 ++
 lib/reverb.js                            |  174 +
 lib/rfft.js                              |  312 ++
 lib/sampler.js                           |  133 +
 lib/single-delay.js                      |   93 +
 lib/sinh.js                              |   16 +
 lib/window-function.js                   |  115 +
 package.json                             |   23 +-
 test/{ => audio-test}/adsr-test.js       |    0
 test/{ => audio-test}/audio-harness.js   |    0
 test/{ => audio-test}/beatdetect-test.js |    0
 test/{ => audio-test}/beatdetektor.js    |    0
 test/{ => audio-test}/dft-test.js        |    0
 test/{ => audio-test}/fft-test.js        |    0
 test/{ => audio-test}/filter-test.js     |    0
 test/{ => audio-test}/iirfilter-test.js  |    0
 test/{ => audio-test}/multidelay-test.js |    0
 test/{ => audio-test}/oscillator-test.js |    0
 test/{ => audio-test}/reverb-test.js     |    0
 test/{ => audio-test}/samples.js         |    0
 test/test-exports.js                     |   16 +
 55 files changed, 5929 insertions(+), 1986 deletions(-)
 create mode 100644 .eslintrc.json
 create mode 100644 bench/index.js
 rename dsp.js => dist/dsp.js (85%)
 create mode 100644 dist/dsp.min.js
 create mode 100644 docs/API.md
 create mode 100644 docs/README.md
 create mode 100644 lib/adsr.js
 create mode 100644 lib/biquad.js
 create mode 100644 lib/dft.js
 create mode 100644 lib/dsp.js
 create mode 100644 lib/fft.js
 create mode 100644 lib/fourier.js
 create mode 100644 lib/graphical-eq.js
 create mode 100644 lib/iir-filter.js
 create mode 100644 lib/iir-filter2.js
 create mode 100644 lib/index.js
 create mode 100644 lib/multi-delay.js
 create mode 100644 lib/oscillator.js
 create mode 100644 lib/reverb.js
 create mode 100644 lib/rfft.js
 create mode 100644 lib/sampler.js
 create mode 100644 lib/single-delay.js
 create mode 100644 lib/sinh.js
 create mode 100644 lib/window-function.js
 rename test/{ => audio-test}/adsr-test.js (100%)
 rename test/{ => audio-test}/audio-harness.js (100%)
 rename test/{ => audio-test}/beatdetect-test.js (100%)
 rename test/{ => audio-test}/beatdetektor.js (100%)
 rename test/{ => audio-test}/dft-test.js (100%)
 rename test/{ => audio-test}/fft-test.js (100%)
 rename test/{ => audio-test}/filter-test.js (100%)
 rename test/{ => audio-test}/iirfilter-test.js (100%)
 rename test/{ => audio-test}/multidelay-test.js (100%)
 rename test/{ => audio-test}/oscillator-test.js (100%)
 rename test/{ => audio-test}/reverb-test.js (100%)
 rename test/{ => audio-test}/samples.js (100%)
 create mode 100644 test/test-exports.js

diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..4fdf69e
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,21 @@
+{
+    "env": {
+        "browser": true,
+        "node": true
+    },
+    "extends": "eslint:recommended",
+    "rules": {
+        "linebreak-style": [
+            "error",
+            "unix"
+        ],
+        "quotes": [
+            "error",
+            "double"
+        ],
+        "semi": [
+            "error",
+            "always"
+        ]
+    }
+}
diff --git a/.gitignore b/.gitignore
index b0dcffa..38d0df3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,5 @@ examples/audio
 .project
 *~
 .idea
+node_modules/
+npm-debug.log
diff --git a/Makefile b/Makefile
index 1ac12e9..4b47878 100644
--- a/Makefile
+++ b/Makefile
@@ -7,9 +7,9 @@ VERSION ?= $(error Specify a version for your release (e.g., VERSION=0.5))
 FREQUENCY ?= 440
 
 benchmark:
-	${JSSHELL} -m -j -p -e 'var FREQUENCY=${FREQUENCY};' -f dsp.js -f ./bench/bench.js -f ./bench/dft.js
-	${JSSHELL} -m -j -p -e 'var FREQUENCY=${FREQUENCY};' -f dsp.js -f ./bench/bench.js -f ./bench/fft.js
-	${JSSHELL} -m -j -p -e 'var FREQUENCY=${FREQUENCY};' -f dsp.js -f ./bench/bench.js -f ./bench/rfft.js
+	${JSSHELL} -m -j -p -e 'var FREQUENCY=${FREQUENCY};' -f ./dist/dsp.js -f ./bench/bench.js -f ./bench/dft.js
+	${JSSHELL} -m -j -p -e 'var FREQUENCY=${FREQUENCY};' -f ./dist/dsp.js -f ./bench/bench.js -f ./bench/fft.js
+	${JSSHELL} -m -j -p -e 'var FREQUENCY=${FREQUENCY};' -f ./dist/dsp.js -f ./bench/bench.js -f ./bench/rfft.js
 	${JSSHELL} -m -j -p -f dsp.js -f ./bench/bench.js -f ./bench/deinterleave.js
 
 clean:
diff --git a/bench/bench.js b/bench/bench.js
index 7894b94..b4268e4 100644
--- a/bench/bench.js
+++ b/bench/bench.js
@@ -1,12 +1,12 @@
-function benchmark(func, loopCount) {
+module.exports = function benchmark(func, loopCount) {
   loopCount = loopCount || 10000;
 
   var start = Date.now();
 
-  for (var i = 0; i < loopCount; i++) { 
+  for (var i = 0; i < loopCount; i++) {
     func();
   }
 
   var end = Date.now();
   return end - start;
-}
+};
diff --git a/bench/deinterleave.js b/bench/deinterleave.js
index 930d399..ff4abf1 100644
--- a/bench/deinterleave.js
+++ b/bench/deinterleave.js
@@ -1,32 +1,37 @@
+/* global Float32Array */
+/* eslint-disable no-console */
+var benchmark = require("./bench");
+var dsp = require("..");
 var bufferSize = 2048;
-var sampleRate = 44100;
+// var sampleRate = 44100;
 
 var buffer1 = new Float32Array(bufferSize);
 var buffer2 = new Float32Array(bufferSize);
 var buffer3 = new Float32Array(bufferSize);
 var buffer4 = new Float32Array(bufferSize);
 
-for (var i = 0; i < bufferSize; i++) {
+var i;
+for (i = 0; i < bufferSize; i++) {
   buffer1[i] = (i % 2 === 0) ? -Math.random() : Math.random();
 }
 
-for (var i = 0; i < bufferSize; i++) {
+for (i = 0; i < bufferSize; i++) {
   buffer2[i] = (i % 2 === 0) ? -Math.random() : Math.random();
 }
 
-for (var i = 0; i < bufferSize; i++) {
+for (i = 0; i < bufferSize; i++) {
   buffer3[i] = (i % 2 === 0) ? -Math.random() : Math.random();
 }
 
-for (var i = 0; i < bufferSize; i++) {
+for (i = 0; i < bufferSize; i++) {
   buffer4[i] = (i % 2 === 0) ? -Math.random() : Math.random();
 }
 
 var channel;
 var temp;
 
-var duration = benchmark(function() { 
-  channel = DSP.deinterleave(DSP.MIX, buffer1);
+var duration = benchmark(function() {
+  channel = dsp.DSP.deinterleave(dsp.DSP.MIX, buffer1);
 
   // cycle buffers
   temp = buffer1;
@@ -36,5 +41,5 @@ var duration = benchmark(function() {
   buffer4 = temp;
 }, 100000);
 
-print("Channel length: " + channel.length);
-print("100000 iterations: " + (duration) + " ms (" + ((duration) / 100000) + "ms per iter)\n");
+console.log("Channel length: " + channel.length);
+console.log("100000 iterations: " + (duration) + " ms (" + ((duration) / 100000) + "ms per iter)\n");
diff --git a/bench/dft.js b/bench/dft.js
index 40ac61e..a2c0c73 100644
--- a/bench/dft.js
+++ b/bench/dft.js
@@ -1,9 +1,13 @@
+/* global FREQUENCY */
+/* eslint-disable no-console */
+var benchmark = require("./bench");
+var dsp = require("..");
 var bufferSize = 2048;
 var sampleRate = 44100;
 var frequency = FREQUENCY || 440;
 
-var dft = new DFT(bufferSize, sampleRate);
-var osc = new Oscillator(DSP.SAW, frequency, 1.0, bufferSize, sampleRate);
+var dft = new dsp.DFT(bufferSize, sampleRate);
+var osc = new dsp.Oscillator(dsp.DSP.SAW, frequency, 1.0, bufferSize, sampleRate);
 var signal = osc.generate();
 
 var duration = benchmark(function() { dft.forward(signal); }, 20);
@@ -16,5 +20,5 @@ for (var i = 0; i < dft.spectrum.length; i++) {
 
 var peakFreq = dft.getBandFrequency(dft.peakBand);
 
-print("Detected peak: " + peakFreq + " Hz (error " + Math.abs(peakFreq - frequency) + " Hz)");
-print("20 DFTs: " + (duration) + " ms (" + ((duration) / 20) + "ms per DFT)\n");
+console.log("Detected peak: " + peakFreq + " Hz (error " + Math.abs(peakFreq - frequency) + " Hz)");
+console.log("20 DFTs: " + (duration) + " ms (" + ((duration) / 20) + "ms per DFT)\n");
diff --git a/bench/fft.js b/bench/fft.js
index 2a0fe58..95a5fe3 100644
--- a/bench/fft.js
+++ b/bench/fft.js
@@ -1,9 +1,13 @@
+/* global FREQUENCY */
+/* eslint-disable no-console */
+var benchmark = require("./bench");
+var dsp = require("..");
 var bufferSize = 2048;
 var sampleRate = 44100;
 var frequency = FREQUENCY || 440;
 
-var fft = new FFT(bufferSize, sampleRate);
-var osc = new Oscillator(DSP.SAW, frequency, 1.0, bufferSize, sampleRate);
+var fft = new dsp.FFT(bufferSize, sampleRate);
+var osc = new dsp.Oscillator(dsp.DSP.SAW, frequency, 1.0, bufferSize, sampleRate);
 var signal = osc.generate();
 
 var duration = benchmark(function() { fft.forward(signal); });
@@ -16,5 +20,5 @@ for (var i = 0; i < fft.spectrum.length; i++) {
 
 var peakFreq = fft.getBandFrequency(fft.peakBand);
 
-print("Detected peak: " + peakFreq + " Hz (error " + Math.abs(peakFreq - frequency) + " Hz)");
-print("10000 FFTs: " + (duration) + " ms (" + ((duration) / 10000) + "ms per FFT)\n");
+console.log("Detected peak: " + peakFreq + " Hz (error " + Math.abs(peakFreq - frequency) + " Hz)");
+console.log("10000 FFTs: " + (duration) + " ms (" + ((duration) / 10000) + "ms per FFT)\n");
diff --git a/bench/index.js b/bench/index.js
new file mode 100644
index 0000000..ca506a5
--- /dev/null
+++ b/bench/index.js
@@ -0,0 +1,6 @@
+/* eslint-disable */
+FREQUENCY = 440;
+require("./deinterleave");
+require("./dft");
+require("./fft");
+require("./rfft");
diff --git a/bench/rfft.js b/bench/rfft.js
index bc428d4..b70ad3d 100644
--- a/bench/rfft.js
+++ b/bench/rfft.js
@@ -1,9 +1,13 @@
+/* global FREQUENCY */
+/* eslint-disable no-console */
+var benchmark = require("./bench");
+var dsp = require("..");
 var bufferSize = 2048;
 var sampleRate = 44100;
 var frequency = FREQUENCY || 440;
 
-var fft = new RFFT(bufferSize, sampleRate);
-var osc = new Oscillator(DSP.SAW, frequency, 1.0, bufferSize, sampleRate);
+var fft = new dsp.RFFT(bufferSize, sampleRate);
+var osc = new dsp.Oscillator(dsp.DSP.SAW, frequency, 1.0, bufferSize, sampleRate);
 var signal = osc.generate();
 
 var duration = benchmark(function() { fft.forward(signal); });
@@ -16,5 +20,5 @@ for (var i = 0; i < fft.spectrum.length; i++) {
 
 var peakFreq = fft.getBandFrequency(fft.peakBand);
 
-print("Detected peak: " + peakFreq + " Hz (error " + Math.abs(peakFreq - frequency) + " Hz)");
-print("10000 FFTs: " + (duration) + " ms (" + ((duration) / 10000) + "ms per FFT)\n");
+console.log("Detected peak: " + peakFreq + " Hz (error " + Math.abs(peakFreq - frequency) + " Hz)");
+console.log("10000 FFTs: " + (duration) + " ms (" + ((duration) / 10000) + "ms per FFT)\n");
diff --git a/dsp.js b/dist/dsp.js
similarity index 85%
rename from dsp.js
rename to dist/dsp.js
index 7188235..2fb4a3d 100644
--- a/dsp.js
+++ b/dist/dsp.js
@@ -1,950 +1,1536 @@
-/* 
- *  DSP.js - a comprehensive digital signal processing  library for javascript
- * 
- *  Created by Corban Brook <corbanbrook@gmail.com> on 2010-01-01.
- *  Copyright 2010 Corban Brook. All rights reserved.
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+/**
+ * ADSR Envelope
  *
+ * @param {Number} attack The attack length in seconds
+ * @param {Number} decay The decay length in seconds
+ * @param {Number} sustain The sustain level
+ * @param {Number} release The release length in seconds
+ * @param {Number} sampleRate The the sample rate
+ * @constructor
  */
+function ADSR(attackLength, decayLength, sustainLevel, sustainLength, releaseLength, sampleRate) {
+  this.sampleRate = sampleRate;
+  // Length in seconds
+  this.attackLength  = attackLength;
+  this.decayLength   = decayLength;
+  this.sustainLevel  = sustainLevel;
+  this.sustainLength = sustainLength;
+  this.releaseLength = releaseLength;
+  this.sampleRate    = sampleRate;
 
-////////////////////////////////////////////////////////////////////////////////
-//                                  CONSTANTS                                 //
-////////////////////////////////////////////////////////////////////////////////
-
-/**
- * DSP is an object which contains general purpose utility functions and constants
- */
-var DSP = {
-  // Channels
-  LEFT:           0,
-  RIGHT:          1,
-  MIX:            2,
+  // Length in samples
+  this.attackSamples  = attackLength  * sampleRate;
+  this.decaySamples   = decayLength   * sampleRate;
+  this.sustainSamples = sustainLength * sampleRate;
+  this.releaseSamples = releaseLength * sampleRate;
 
-  // Waveforms
-  SINE:           1,
-  TRIANGLE:       2,
-  SAW:            3,
-  SQUARE:         4,
+  // Updates the envelope sample positions
+  this.update = function() {
+    this.attack         =                this.attackSamples;
+    this.decay          = this.attack  + this.decaySamples;
+    this.sustain        = this.decay   + this.sustainSamples;
+    this.release        = this.sustain + this.releaseSamples;
+  };
 
-  // Filters
-  LOWPASS:        0,
-  HIGHPASS:       1,
-  BANDPASS:       2,
-  NOTCH:          3,
+  this.update();
 
-  // Window functions
-  BARTLETT:       1,
-  BARTLETTHANN:   2,
-  BLACKMAN:       3,
-  COSINE:         4,
-  GAUSS:          5,
-  HAMMING:        6,
-  HANN:           7,
-  LANCZOS:        8,
-  RECTANGULAR:    9,
-  TRIANGULAR:     10,
+  this.samplesProcessed = 0;
+}
 
-  // Loop modes
-  OFF:            0,
-  FW:             1,
-  BW:             2,
-  FWBW:           3,
+/**
+ * Start the envelope
+ */
+ADSR.prototype.noteOn = function() {
+  this.samplesProcessed = 0;
+  this.sustainSamples = this.sustainLength * this.sampleRate;
+  this.update();
+};
 
-  // Math
-  TWO_PI:         2*Math.PI
+/**
+ * Stop the envelope
+ *
+ * Send a note off when using a sustain of infinity to let the envelope enter
+ * the release phase
+ */
+ADSR.prototype.noteOff = function() {
+  this.sustainSamples = this.samplesProcessed - this.decaySamples;
+  this.update();
 };
 
-// Setup arrays for platforms which do not support byte arrays
-function setupTypedArray(name, fallback) {
-  // check if TypedArray exists
-  // typeof on Minefield and Chrome return function, typeof on Webkit returns object.
-  if (typeof this[name] !== "function" && typeof this[name] !== "object") {
-    // nope.. check if WebGLArray exists
-    if (typeof this[fallback] === "function" && typeof this[fallback] !== "object") {
-      this[name] = this[fallback];
-    } else {
-      // nope.. set as Native JS array
-      this[name] = function(obj) {
-        if (obj instanceof Array) {
-          return obj;
-        } else if (typeof obj === "number") {
-          return new Array(obj);
-        }
-      };
-    }
+/**
+ * Process sample
+ */
+ADSR.prototype.processSample = function(sample) {
+  var amplitude = 0;
+
+  if ( this.samplesProcessed <= this.attack ) {
+    amplitude = 0 + (1 - 0) * ((this.samplesProcessed - 0) / (this.attack - 0));
+  } else if ( this.samplesProcessed > this.attack && this.samplesProcessed <= this.decay ) {
+    amplitude = 1 + (this.sustainLevel - 1) * ((this.samplesProcessed - this.attack) / (this.decay - this.attack));
+  } else if ( this.samplesProcessed > this.decay && this.samplesProcessed <= this.sustain ) {
+    amplitude = this.sustainLevel;
+  } else if ( this.samplesProcessed > this.sustain && this.samplesProcessed <= this.release ) {
+    amplitude = this.sustainLevel + (0 - this.sustainLevel) * ((this.samplesProcessed - this.sustain) / (this.release - this.sustain));
   }
-}
 
-setupTypedArray("Float64Array", "WebGLFloatArray");
-setupTypedArray("Int32Array",   "WebGLIntArray");
-setupTypedArray("Uint16Array",  "WebGLUnsignedShortArray");
-setupTypedArray("Uint8Array",   "WebGLUnsignedByteArray");
+  return sample * amplitude;
+};
 
+/**
+ * Get current value
+ * @return {Number} amplitude
+ */
+ADSR.prototype.value = function() {
+  var amplitude = 0;
 
-////////////////////////////////////////////////////////////////////////////////
-//                            DSP UTILITY FUNCTIONS                           //
-////////////////////////////////////////////////////////////////////////////////
+  if ( this.samplesProcessed <= this.attack ) {
+    amplitude = 0 + (1 - 0) * ((this.samplesProcessed - 0) / (this.attack - 0));
+  } else if ( this.samplesProcessed > this.attack && this.samplesProcessed <= this.decay ) {
+    amplitude = 1 + (this.sustainLevel - 1) * ((this.samplesProcessed - this.attack) / (this.decay - this.attack));
+  } else if ( this.samplesProcessed > this.decay && this.samplesProcessed <= this.sustain ) {
+    amplitude = this.sustainLevel;
+  } else if ( this.samplesProcessed > this.sustain && this.samplesProcessed <= this.release ) {
+    amplitude = this.sustainLevel + (0 - this.sustainLevel) * ((this.samplesProcessed - this.sustain) / (this.release - this.sustain));
+  }
+
+  return amplitude;
+};
 
 /**
- * Inverts the phase of a signal
- *
- * @param {Array} buffer A sample buffer
- *
- * @returns The inverted sample buffer
+ * Process a buffer
+ * @param {Array} buffer
  */
-DSP.invert = function(buffer) {
-  for (var i = 0, len = buffer.length; i < len; i++) {
-    buffer[i] *= -1;
+ADSR.prototype.process = function(buffer) {
+  for ( var i = 0; i < buffer.length; i++ ) {
+    buffer[i] *= this.value();
+
+    this.samplesProcessed++;
   }
 
   return buffer;
 };
 
 /**
- * Converts split-stereo (dual mono) sample buffers into a stereo interleaved sample buffer
- *
- * @param {Array} left  A sample buffer
- * @param {Array} right A sample buffer
- *
- * @returns The stereo interleaved buffer
+ * Test if the envelope is active
+ * @return {Boolean}
  */
-DSP.interleave = function(left, right) {
-  if (left.length !== right.length) {
-    throw "Can not interleave. Channel lengths differ.";
-  }
- 
-  var stereoInterleaved = new Float64Array(left.length * 2);
- 
-  for (var i = 0, len = left.length; i < len; i++) {
-    stereoInterleaved[2*i]   = left[i];
-    stereoInterleaved[2*i+1] = right[i];
+ADSR.prototype.isActive = function() {
+  if ( this.samplesProcessed > this.release || this.samplesProcessed === -1 ) {
+    return false;
+  } else {
+    return true;
   }
- 
-  return stereoInterleaved;
 };
 
 /**
- * Converts a stereo-interleaved sample buffer into split-stereo (dual mono) sample buffers
- *
- * @param {Array} buffer A stereo-interleaved sample buffer
- *
- * @returns an Array containing left and right channels
+ * Disable the envelope
  */
-DSP.deinterleave = (function() {
-  var left, right, mix, deinterleaveChannel = []; 
-
-  deinterleaveChannel[DSP.MIX] = function(buffer) {
-    for (var i = 0, len = buffer.length/2; i < len; i++) {
-      mix[i] = (buffer[2*i] + buffer[2*i+1]) / 2;
-    }
-    return mix;
-  };
-
-  deinterleaveChannel[DSP.LEFT] = function(buffer) {
-    for (var i = 0, len = buffer.length/2; i < len; i++) {
-      left[i]  = buffer[2*i];
-    }
-    return left;
-  };
-
-  deinterleaveChannel[DSP.RIGHT] = function(buffer) {
-    for (var i = 0, len = buffer.length/2; i < len; i++) {
-      right[i]  = buffer[2*i+1];
-    }
-    return right;
-  };
-
-  return function(channel, buffer) { 
-    left  = left  || new Float64Array(buffer.length/2);
-    right = right || new Float64Array(buffer.length/2);
-    mix   = mix   || new Float64Array(buffer.length/2);
+ADSR.prototype.disable = function() {
+  this.samplesProcessed = -1;
+};
 
-    if (buffer.length/2 !== left.length) {
-      left  = new Float64Array(buffer.length/2);
-      right = new Float64Array(buffer.length/2);
-      mix   = new Float64Array(buffer.length/2);
-    }
+module.exports = ADSR;
 
-    return deinterleaveChannel[channel](buffer);
-  };
-}());
+},{}],2:[function(require,module,exports){
+/* global Float64Array */
+var DSP = require("./dsp");
+var sinh = require("./sinh");
 
 /**
- * Separates a channel from a stereo-interleaved sample buffer
- *
- * @param {Array}  buffer A stereo-interleaved sample buffer
- * @param {Number} channel A channel constant (LEFT, RIGHT, MIX)
+ * Biquad filter
  *
- * @returns an Array containing a signal mono sample buffer
- */
-DSP.getChannel = DSP.deinterleave;
-
-/**
- * Helper method (for Reverb) to mix two (interleaved) samplebuffers. It's possible
- * to negate the second buffer while mixing and to perform a volume correction
- * on the final signal.
+ * Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
+ *  Copyright 2010 Ricard Marxer. All rights reserved.
  *
- * @param {Array} sampleBuffer1 Array containing Float values or a Float64Array
- * @param {Array} sampleBuffer2 Array containing Float values or a Float64Array
- * @param {Boolean} negate When true inverts/flips the audio signal
- * @param {Number} volumeCorrection When you add multiple sample buffers, use this to tame your signal ;)
+ * Implementation based on:
+ * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
  *
- * @returns A new Float64Array interleaved buffer.
+ * @param {Number} type
+ * @param {Number} sampleRate
+ * @constructor
  */
-DSP.mixSampleBuffers = function(sampleBuffer1, sampleBuffer2, negate, volumeCorrection){
-  var outputSamples = new Float64Array(sampleBuffer1);
+function Biquad(type, sampleRate) {
+  this.Fs = sampleRate;
+  this.type = type;  // type of the filter
+  this.parameterType = DSP.Q; // type of the parameter
 
-  for(var i = 0; i<sampleBuffer1.length; i++){
-    outputSamples[i] += (negate ? -sampleBuffer2[i] : sampleBuffer2[i]) / volumeCorrection;
-  }
- 
-  return outputSamples;
-}; 
+  this.x_1_l = 0;
+  this.x_2_l = 0;
+  this.y_1_l = 0;
+  this.y_2_l = 0;
 
-// Biquad filter types
-DSP.LPF = 0;                // H(s) = 1 / (s^2 + s/Q + 1)
-DSP.HPF = 1;                // H(s) = s^2 / (s^2 + s/Q + 1)
-DSP.BPF_CONSTANT_SKIRT = 2; // H(s) = s / (s^2 + s/Q + 1)  (constant skirt gain, peak gain = Q)
-DSP.BPF_CONSTANT_PEAK = 3;  // H(s) = (s/Q) / (s^2 + s/Q + 1)      (constant 0 dB peak gain)
-DSP.NOTCH = 4;              // H(s) = (s^2 + 1) / (s^2 + s/Q + 1)
-DSP.APF = 5;                // H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1)
-DSP.PEAKING_EQ = 6;         // H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
-DSP.LOW_SHELF = 7;          // H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1)
-DSP.HIGH_SHELF = 8;         // H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A)
+  this.x_1_r = 0;
+  this.x_2_r = 0;
+  this.y_1_r = 0;
+  this.y_2_r = 0;
 
-// Biquad filter parameter types
-DSP.Q = 1;
-DSP.BW = 2; // SHARED with BACKWARDS LOOP MODE
-DSP.S = 3;
+  this.b0 = 1;
+  this.a0 = 1;
 
-// Find RMS of signal
-DSP.RMS = function(buffer) {
-  var total = 0;
-  
-  for (var i = 0, n = buffer.length; i < n; i++) {
-    total += buffer[i] * buffer[i];
-  }
-  
-  return Math.sqrt(total / n);
-};
+  this.b1 = 0;
+  this.a1 = 0;
 
-// Find Peak of signal
-DSP.Peak = function(buffer) {
-  var peak = 0;
-  
-  for (var i = 0, n = buffer.length; i < n; i++) {
-    peak = (Math.abs(buffer[i]) > peak) ? Math.abs(buffer[i]) : peak; 
-  }
-  
-  return peak;
-};
+  this.b2 = 0;
+  this.a2 = 0;
 
-// Fourier Transform Module used by DFT, FFT, RFFT
-function FourierTransform(bufferSize, sampleRate) {
-  this.bufferSize = bufferSize;
-  this.sampleRate = sampleRate;
-  this.bandwidth  = 2 / bufferSize * sampleRate / 2;
+  this.b0a0 = this.b0 / this.a0;
+  this.b1a0 = this.b1 / this.a0;
+  this.b2a0 = this.b2 / this.a0;
+  this.a1a0 = this.a1 / this.a0;
+  this.a2a0 = this.a2 / this.a0;
 
-  this.spectrum   = new Float64Array(bufferSize/2);
-  this.real       = new Float64Array(bufferSize);
-  this.imag       = new Float64Array(bufferSize);
+  this.f0 = 3000;   // "wherever it"s happenin", man."  Center Frequency or
+                    // Corner Frequency, or shelf midpoint frequency, depending
+                    // on which filter type.  The "significant frequency".
 
-  this.peakBand   = 0;
-  this.peak       = 0;
+  this.dBgain = 12; // used only for peaking and shelving filters
 
-  /**
-   * Calculates the *middle* frequency of an FFT band.
-   *
-   * @param {Number} index The index of the FFT band.
-   *
-   * @returns The middle frequency in Hz.
-   */
-  this.getBandFrequency = function(index) {
-    return this.bandwidth * index + this.bandwidth / 2;
-  };
+  this.Q = 1;       // the EE kind of definition, except for peakingEQ in which A*Q is
+                    // the classic EE Q.  That adjustment in definition was made so that
+                    // a boost of N dB followed by a cut of N dB for identical Q and
+                    // f0/Fs results in a precisely flat unity gain filter or "wire".
 
-  this.calculateSpectrum = function() {
-    var spectrum  = this.spectrum,
-        real      = this.real,
-        imag      = this.imag,
-        bSi       = 2 / this.bufferSize,
-        sqrt      = Math.sqrt,
-        rval, 
-        ival,
-        mag;
+  this.BW = -3;     // the bandwidth in octaves (between -3 dB frequencies for BPF
+                    // and notch or between midpoint (dBgain/2) gain frequencies for
+                    // peaking EQ
 
-    for (var i = 0, N = bufferSize/2; i < N; i++) {
-      rval = real[i];
-      ival = imag[i];
-      mag = bSi * sqrt(rval * rval + ival * ival);
+  this.S = 1;       // a "shelf slope" parameter (for shelving EQ only).  When S = 1,
+                    // the shelf slope is as steep as it can be and remain monotonically
+                    // increasing or decreasing gain with frequency.  The shelf slope, in
+                    // dB/octave, remains proportional to S for all other values for a
+                    // fixed f0/Fs and dBgain.
 
-      if (mag > this.peak) {
-        this.peakBand = i;
-        this.peak = mag;
-      }
+  this.coefficients = function() {
+    var b = [this.b0, this.b1, this.b2];
+    var a = [this.a0, this.a1, this.a2];
+    return {b: b, a:a};
+  };
 
-      spectrum[i] = mag;
-    }
+  this.setFilterType = function(type) {
+    this.type = type;
+    this.recalculateCoefficients();
   };
-}
 
-/**
- * DFT is a class for calculating the Discrete Fourier Transform of a signal.
- *
- * @param {Number} bufferSize The size of the sample buffer to be computed
- * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
- *
- * @constructor
- */
-function DFT(bufferSize, sampleRate) {
-  FourierTransform.call(this, bufferSize, sampleRate);
+  this.setSampleRate = function(rate) {
+    this.Fs = rate;
+    this.recalculateCoefficients();
+  };
 
-  var N = bufferSize/2 * bufferSize;
-  var TWO_PI = 2 * Math.PI;
+  this.setQ = function(q) {
+    this.parameterType = DSP.Q;
+    this.Q = Math.max(Math.min(q, 115.0), 0.001);
+    this.recalculateCoefficients();
+  };
 
-  this.sinTable = new Float64Array(N);
-  this.cosTable = new Float64Array(N);
+  this.setBW = function(bw) {
+    this.parameterType = DSP.BW;
+    this.BW = bw;
+    this.recalculateCoefficients();
+  };
 
-  for (var i = 0; i < N; i++) {
-    this.sinTable[i] = Math.sin(i * TWO_PI / bufferSize);
-    this.cosTable[i] = Math.cos(i * TWO_PI / bufferSize);
-  }
-}
+  this.setS = function(s) {
+    this.parameterType = DSP.S;
+    this.S = Math.max(Math.min(s, 5.0), 0.0001);
+    this.recalculateCoefficients();
+  };
 
-/**
- * Performs a forward transform on the sample buffer.
- * Converts a time domain signal to frequency domain spectra.
- *
- * @param {Array} buffer The sample buffer
- *
- * @returns The frequency spectrum array
- */
-DFT.prototype.forward = function(buffer) {
-  var real = this.real, 
-      imag = this.imag,
-      rval,
-      ival;
+  this.setF0 = function(freq) {
+    this.f0 = freq;
+    this.recalculateCoefficients();
+  };
 
-  for (var k = 0; k < this.bufferSize/2; k++) {
-    rval = 0.0;
-    ival = 0.0;
+  this.setDbGain = function(g) {
+    this.dBgain = g;
+    this.recalculateCoefficients();
+  };
 
-    for (var n = 0; n < buffer.length; n++) {
-      rval += this.cosTable[k*n] * buffer[n];
-      ival += this.sinTable[k*n] * buffer[n];
+  this.recalculateCoefficients = function() {
+    var A;
+    if (type === DSP.PEAKING_EQ || type === DSP.LOW_SHELF || type === DSP.HIGH_SHELF ) {
+      A = Math.pow(10, (this.dBgain/40));  // for peaking and shelving EQ filters only
+    } else {
+      A  = Math.sqrt( Math.pow(10, (this.dBgain/20)) );
     }
 
-    real[k] = rval;
-    imag[k] = ival;
-  }
-
-  return this.calculateSpectrum();
-};
+    var w0 = DSP.TWO_PI * this.f0 / this.Fs;
 
+    var cosw0 = Math.cos(w0);
+    var sinw0 = Math.sin(w0);
 
-/**
- * FFT is a class for calculating the Discrete Fourier Transform of a signal
- * with the Fast Fourier Transform algorithm.
- *
- * @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2
- * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
- *
- * @constructor
- */
-function FFT(bufferSize, sampleRate) {
-  FourierTransform.call(this, bufferSize, sampleRate);
-   
-  this.reverseTable = new Uint32Array(bufferSize);
+    var alpha = 0;
 
-  var limit = 1;
-  var bit = bufferSize >> 1;
+    switch (this.parameterType) {
+      case DSP.Q:
+        alpha = sinw0/(2*this.Q);
+        break;
 
-  var i;
+      case DSP.BW:
+        alpha = sinw0 * sinh( Math.LN2/2 * this.BW * w0/sinw0 );
+        break;
 
-  while (limit < bufferSize) {
-    for (i = 0; i < limit; i++) {
-      this.reverseTable[i + limit] = this.reverseTable[i] + bit;
+      case DSP.S:
+        alpha = sinw0/2 * Math.sqrt( (A + 1/A)*(1/this.S - 1) + 2 );
+        break;
     }
 
-    limit = limit << 1;
-    bit = bit >> 1;
-  }
+    /**
+        FYI: The relationship between bandwidth and Q is
+             1/Q = 2*sinh(ln(2)/2*BW*w0/sin(w0))     (digital filter w BLT)
+        or   1/Q = 2*sinh(ln(2)/2*BW)             (analog filter prototype)
 
-  this.sinTable = new Float64Array(bufferSize);
-  this.cosTable = new Float64Array(bufferSize);
+        The relationship between shelf slope and Q is
+             1/Q = sqrt((A + 1/A)*(1/S - 1) + 2)
+    */
 
-  for (i = 0; i < bufferSize; i++) {
-    this.sinTable[i] = Math.sin(-Math.PI/i);
-    this.cosTable[i] = Math.cos(-Math.PI/i);
-  }
-}
+    var coeff;
 
-/**
- * Performs a forward transform on the sample buffer.
- * Converts a time domain signal to frequency domain spectra.
- *
- * @param {Array} buffer The sample buffer. Buffer Length must be power of 2
- *
- * @returns The frequency spectrum array
- */
-FFT.prototype.forward = function(buffer) {
-  // Locally scope variables for speed up
-  var bufferSize      = this.bufferSize,
-      cosTable        = this.cosTable,
-      sinTable        = this.sinTable,
-      reverseTable    = this.reverseTable,
-      real            = this.real,
-      imag            = this.imag,
-      spectrum        = this.spectrum;
+    switch (this.type) {
+      case DSP.LPF:       // H(s) = 1 / (s^2 + s/Q + 1)
+        this.b0 =  (1 - cosw0)/2;
+        this.b1 =   1 - cosw0;
+        this.b2 =  (1 - cosw0)/2;
+        this.a0 =   1 + alpha;
+        this.a1 =  -2 * cosw0;
+        this.a2 =   1 - alpha;
+        break;
 
-  var k = Math.floor(Math.log(bufferSize) / Math.LN2);
+      case DSP.HPF:       // H(s) = s^2 / (s^2 + s/Q + 1)
+        this.b0 =  (1 + cosw0)/2;
+        this.b1 = -(1 + cosw0);
+        this.b2 =  (1 + cosw0)/2;
+        this.a0 =   1 + alpha;
+        this.a1 =  -2 * cosw0;
+        this.a2 =   1 - alpha;
+        break;
 
-  if (Math.pow(2, k) !== bufferSize) { throw "Invalid buffer size, must be a power of 2."; }
-  if (bufferSize !== buffer.length)  { throw "Supplied buffer is not the same size as defined FFT. FFT Size: " + bufferSize + " Buffer Size: " + buffer.length; }
+      case DSP.BPF_CONSTANT_SKIRT:       // H(s) = s / (s^2 + s/Q + 1)  (constant skirt gain, peak gain = Q)
+        this.b0 =   sinw0/2;
+        this.b1 =   0;
+        this.b2 =  -sinw0/2;
+        this.a0 =   1 + alpha;
+        this.a1 =  -2*cosw0;
+        this.a2 =   1 - alpha;
+        break;
 
-  var halfSize = 1,
-      phaseShiftStepReal,
-      phaseShiftStepImag,
-      currentPhaseShiftReal,
-      currentPhaseShiftImag,
-      off,
-      tr,
-      ti,
-      tmpReal,
-      i;
-
-  for (i = 0; i < bufferSize; i++) {
-    real[i] = buffer[reverseTable[i]];
-    imag[i] = 0;
-  }
-
-  while (halfSize < bufferSize) {
-    //phaseShiftStepReal = Math.cos(-Math.PI/halfSize);
-    //phaseShiftStepImag = Math.sin(-Math.PI/halfSize);
-    phaseShiftStepReal = cosTable[halfSize];
-    phaseShiftStepImag = sinTable[halfSize];
-    
-    currentPhaseShiftReal = 1;
-    currentPhaseShiftImag = 0;
+      case DSP.BPF_CONSTANT_PEAK:       // H(s) = (s/Q) / (s^2 + s/Q + 1)      (constant 0 dB peak gain)
+        this.b0 =   alpha;
+        this.b1 =   0;
+        this.b2 =  -alpha;
+        this.a0 =   1 + alpha;
+        this.a1 =  -2*cosw0;
+        this.a2 =   1 - alpha;
+        break;
 
-    for (var fftStep = 0; fftStep < halfSize; fftStep++) {
-      i = fftStep;
+      case DSP.NOTCH:     // H(s) = (s^2 + 1) / (s^2 + s/Q + 1)
+        this.b0 =   1;
+        this.b1 =  -2*cosw0;
+        this.b2 =   1;
+        this.a0 =   1 + alpha;
+        this.a1 =  -2*cosw0;
+        this.a2 =   1 - alpha;
+        break;
 
-      while (i < bufferSize) {
-        off = i + halfSize;
-        tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]);
-        ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]);
+      case DSP.APF:       // H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1)
+        this.b0 =   1 - alpha;
+        this.b1 =  -2*cosw0;
+        this.b2 =   1 + alpha;
+        this.a0 =   1 + alpha;
+        this.a1 =  -2*cosw0;
+        this.a2 =   1 - alpha;
+        break;
 
-        real[off] = real[i] - tr;
-        imag[off] = imag[i] - ti;
-        real[i] += tr;
-        imag[i] += ti;
+      case DSP.PEAKING_EQ:  // H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
+        this.b0 =   1 + alpha*A;
+        this.b1 =  -2*cosw0;
+        this.b2 =   1 - alpha*A;
+        this.a0 =   1 + alpha/A;
+        this.a1 =  -2*cosw0;
+        this.a2 =   1 - alpha/A;
+        break;
 
-        i += halfSize << 1;
-      }
+      case DSP.LOW_SHELF:   // H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1)
+        coeff = sinw0 * Math.sqrt( (A^2 + 1)*(1/this.S - 1) + 2*A );
+        this.b0 =    A*((A+1) - (A-1)*cosw0 + coeff);
+        this.b1 =  2*A*((A-1) - (A+1)*cosw0);
+        this.b2 =    A*((A+1) - (A-1)*cosw0 - coeff);
+        this.a0 =       (A+1) + (A-1)*cosw0 + coeff;
+        this.a1 =   -2*((A-1) + (A+1)*cosw0);
+        this.a2 =       (A+1) + (A-1)*cosw0 - coeff;
+        break;
 
-      tmpReal = currentPhaseShiftReal;
-      currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag);
-      currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal);
+      case DSP.HIGH_SHELF:   // H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A)
+        coeff = sinw0 * Math.sqrt( (A^2 + 1)*(1/this.S - 1) + 2*A );
+        this.b0 =    A*((A+1) + (A-1)*cosw0 + coeff);
+        this.b1 = -2*A*((A-1) + (A+1)*cosw0);
+        this.b2 =    A*((A+1) + (A-1)*cosw0 - coeff);
+        this.a0 =       (A+1) - (A-1)*cosw0 + coeff;
+        this.a1 =    2*((A-1) - (A+1)*cosw0);
+        this.a2 =       (A+1) - (A-1)*cosw0 - coeff;
+        break;
     }
 
-    halfSize = halfSize << 1;
-  }
-
-  return this.calculateSpectrum();
-};
-
-FFT.prototype.inverse = function(real, imag) {
-  // Locally scope variables for speed up
-  var bufferSize      = this.bufferSize,
-      cosTable        = this.cosTable,
-      sinTable        = this.sinTable,
-      reverseTable    = this.reverseTable,
-      spectrum        = this.spectrum;
-     
-      real = real || this.real;
-      imag = imag || this.imag;
+    this.b0a0 = this.b0/this.a0;
+    this.b1a0 = this.b1/this.a0;
+    this.b2a0 = this.b2/this.a0;
+    this.a1a0 = this.a1/this.a0;
+    this.a2a0 = this.a2/this.a0;
+  };
 
-  var halfSize = 1,
-      phaseShiftStepReal,
-      phaseShiftStepImag,
-      currentPhaseShiftReal,
-      currentPhaseShiftImag,
-      off,
-      tr,
-      ti,
-      tmpReal,
-      i;
+  this.process = function(buffer) {
+      //y[n] = (b0/a0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2]
+      //       - (a1/a0)*y[n-1] - (a2/a0)*y[n-2]
 
-  for (i = 0; i < bufferSize; i++) {
-    imag[i] *= -1;
-  }
+      var len = buffer.length;
+      var output = new Float64Array(len);
 
-  var revReal = new Float64Array(bufferSize);
-  var revImag = new Float64Array(bufferSize);
- 
-  for (i = 0; i < real.length; i++) {
-    revReal[i] = real[reverseTable[i]];
-    revImag[i] = imag[reverseTable[i]];
-  }
- 
-  real = revReal;
-  imag = revImag;
+      for ( var i=0; i<buffer.length; i++ ) {
+        output[i] = this.b0a0*buffer[i] + this.b1a0*this.x_1_l + this.b2a0*this.x_2_l - this.a1a0*this.y_1_l - this.a2a0*this.y_2_l;
+        this.y_2_l = this.y_1_l;
+        this.y_1_l = output[i];
+        this.x_2_l = this.x_1_l;
+        this.x_1_l = buffer[i];
+      }
 
-  while (halfSize < bufferSize) {
-    phaseShiftStepReal = cosTable[halfSize];
-    phaseShiftStepImag = sinTable[halfSize];
-    currentPhaseShiftReal = 1;
-    currentPhaseShiftImag = 0;
+      return output;
+  };
 
-    for (var fftStep = 0; fftStep < halfSize; fftStep++) {
-      i = fftStep;
+  this.processStereo = function(buffer) {
+      //y[n] = (b0/a0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2]
+      //       - (a1/a0)*y[n-1] - (a2/a0)*y[n-2]
 
-      while (i < bufferSize) {
-        off = i + halfSize;
-        tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]);
-        ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]);
+      var len = buffer.length;
+      var output = new Float64Array(len);
 
-        real[off] = real[i] - tr;
-        imag[off] = imag[i] - ti;
-        real[i] += tr;
-        imag[i] += ti;
+      for (var i = 0; i < len/2; i++) {
+        output[2*i] = this.b0a0*buffer[2*i] + this.b1a0*this.x_1_l + this.b2a0*this.x_2_l - this.a1a0*this.y_1_l - this.a2a0*this.y_2_l;
+        this.y_2_l = this.y_1_l;
+        this.y_1_l = output[2*i];
+        this.x_2_l = this.x_1_l;
+        this.x_1_l = buffer[2*i];
 
-        i += halfSize << 1;
+        output[2*i+1] = this.b0a0*buffer[2*i+1] + this.b1a0*this.x_1_r + this.b2a0*this.x_2_r - this.a1a0*this.y_1_r - this.a2a0*this.y_2_r;
+        this.y_2_r = this.y_1_r;
+        this.y_1_r = output[2*i+1];
+        this.x_2_r = this.x_1_r;
+        this.x_1_r = buffer[2*i+1];
       }
 
-      tmpReal = currentPhaseShiftReal;
-      currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag);
-      currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal);
-    }
-
-    halfSize = halfSize << 1;
-  }
+      return output;
+  };
+}
 
-  var buffer = new Float64Array(bufferSize); // this should be reused instead
-  for (i = 0; i < bufferSize; i++) {
-    buffer[i] = real[i] / bufferSize;
-  }
+module.exports = Biquad;
 
-  return buffer;
-};
+},{"./dsp":4,"./sinh":17}],3:[function(require,module,exports){
+/* global Float64Array */
+var FourierTransform = require("./fourier");
 
 /**
- * RFFT is a class for calculating the Discrete Fourier Transform of a signal
- * with the Fast Fourier Transform algorithm.
- *
- * This method currently only contains a forward transform but is highly optimized.
+ * DFT is a class for calculating the Discrete Fourier Transform of a signal.
  *
- * @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2
+ * @param {Number} bufferSize The size of the sample buffer to be computed
  * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
  *
  * @constructor
  */
+function DFT(bufferSize, sampleRate) {
+  FourierTransform.call(this, bufferSize, sampleRate);
 
-// lookup tables don't really gain us any speed, but they do increase
-// cache footprint, so don't use them in here
-
-// also we don't use sepearate arrays for real/imaginary parts
+  var N = bufferSize/2 * bufferSize;
+  var TWO_PI = 2 * Math.PI;
 
-// this one a little more than twice as fast as the one in FFT
-// however I only did the forward transform
+  this.sinTable = new Float64Array(N);
+  this.cosTable = new Float64Array(N);
 
-// the rest of this was translated from C, see http://www.jjj.de/fxt/
-// this is the real split radix FFT
+  for (var i = 0; i < N; i++) {
+    this.sinTable[i] = Math.sin(i * TWO_PI / bufferSize);
+    this.cosTable[i] = Math.cos(i * TWO_PI / bufferSize);
+  }
+}
 
-function RFFT(bufferSize, sampleRate) {
-  FourierTransform.call(this, bufferSize, sampleRate);
+/**
+ * Performs a forward transform on the sample buffer.
+ * Converts a time domain signal to frequency domain spectra.
+ *
+ * @param {Array} buffer The sample buffer
+ *
+ * @returns The frequency spectrum array
+ */
+DFT.prototype.forward = function(buffer) {
+  var real = this.real,
+    imag = this.imag,
+    rval,
+    ival;
 
-  this.trans = new Float64Array(bufferSize);
+  for (var k = 0; k < this.bufferSize/2; k++) {
+    rval = 0.0;
+    ival = 0.0;
 
-  this.reverseTable = new Uint32Array(bufferSize);
+    for (var n = 0; n < buffer.length; n++) {
+      rval += this.cosTable[k*n] * buffer[n];
+      ival += this.sinTable[k*n] * buffer[n];
+    }
 
-  // don't use a lookup table to do the permute, use this instead
-  this.reverseBinPermute = function (dest, source) {
-    var bufferSize  = this.bufferSize, 
-        halfSize    = bufferSize >>> 1, 
-        nm1         = bufferSize - 1, 
-        i = 1, r = 0, h;
+    real[k] = rval;
+    imag[k] = ival;
+  }
 
-    dest[0] = source[0];
+  return this.calculateSpectrum();
+};
 
-    do {
-      r += halfSize;
-      dest[i] = source[r];
-      dest[r] = source[i];
-      
-      i++;
+module.exports = DFT;
 
-      h = halfSize << 1;
-      while (h = h >> 1, !((r ^= h) & h));
+},{"./fourier":6}],4:[function(require,module,exports){
+/* global Float64Array */
 
-      if (r >= i) { 
-        dest[i]     = source[r]; 
-        dest[r]     = source[i];
+////////////////////////////////////////////////////////////////////////////////
+//                                  CONSTANTS                                 //
+////////////////////////////////////////////////////////////////////////////////
 
-        dest[nm1-i] = source[nm1-r]; 
-        dest[nm1-r] = source[nm1-i];
-      }
-      i++;
-    } while (i < halfSize);
-    dest[nm1] = source[nm1];
-  };
+/**
+ * DSP is an object which contains general purpose utility functions and constants
+ */
+var DSP = {
+  // Channels
+  LEFT:           0,
+  RIGHT:          1,
+  MIX:            2,
 
-  this.generateReverseTable = function () {
-    var bufferSize  = this.bufferSize, 
-        halfSize    = bufferSize >>> 1, 
-        nm1         = bufferSize - 1, 
-        i = 1, r = 0, h;
+  // Waveforms
+  SINE:           1,
+  TRIANGLE:       2,
+  SAW:            3,
+  SQUARE:         4,
 
-    this.reverseTable[0] = 0;
+  // Filters
+  LOWPASS:        0,
+  HIGHPASS:       1,
+  BANDPASS:       2,
+  NOTCH:          3,
 
-    do {
-      r += halfSize;
-      
-      this.reverseTable[i] = r;
-      this.reverseTable[r] = i;
+  // Window functions
+  BARTLETT:       1,
+  BARTLETTHANN:   2,
+  BLACKMAN:       3,
+  COSINE:         4,
+  GAUSS:          5,
+  HAMMING:        6,
+  HANN:           7,
+  LANCZOS:        8,
+  RECTANGULAR:    9,
+  TRIANGULAR:     10,
 
-      i++;
+  // Loop modes
+  OFF:            0,
+  FW:             1,
+  BW:             2,
+  FWBW:           3,
 
-      h = halfSize << 1;
-      while (h = h >> 1, !((r ^= h) & h));
+  // Math
+  TWO_PI:         2*Math.PI
+};
 
-      if (r >= i) { 
-        this.reverseTable[i] = r;
-        this.reverseTable[r] = i;
+// Setup arrays for platforms which do not support byte arrays
+function setupTypedArray(name, fallback) {
+  // check if TypedArray exists
+  // typeof on Minefield and Chrome return function, typeof on Webkit returns object.
+  if (typeof this[name] !== "function" && typeof this[name] !== "object") {
+    // nope.. check if WebGLArray exists
+    if (typeof this[fallback] === "function" && typeof this[fallback] !== "object") {
+      this[name] = this[fallback];
+    } else {
+      // nope.. set as Native JS array
+      this[name] = function(obj) {
+        if (obj instanceof Array) {
+          return obj;
+        } else if (typeof obj === "number") {
+          return new Array(obj);
+        }
+      };
+    }
+  }
+}
 
-        this.reverseTable[nm1-i] = nm1-r;
-        this.reverseTable[nm1-r] = nm1-i;
-      }
-      i++;
-    } while (i < halfSize);
+setupTypedArray("Float64Array", "WebGLFloatArray");
+setupTypedArray("Int32Array",   "WebGLIntArray");
+setupTypedArray("Uint16Array",  "WebGLUnsignedShortArray");
+setupTypedArray("Uint8Array",   "WebGLUnsignedByteArray");
 
-    this.reverseTable[nm1] = nm1;
+
+////////////////////////////////////////////////////////////////////////////////
+//                            DSP UTILITY FUNCTIONS                           //
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Inverts the phase of a signal
+ *
+ * @param {Array} buffer A sample buffer
+ *
+ * @returns The inverted sample buffer
+ */
+DSP.invert = function(buffer) {
+  for (var i = 0, len = buffer.length; i < len; i++) {
+    buffer[i] *= -1;
+  }
+
+  return buffer;
+};
+
+/**
+ * Converts split-stereo (dual mono) sample buffers into a stereo interleaved sample buffer
+ *
+ * @param {Array} left  A sample buffer
+ * @param {Array} right A sample buffer
+ *
+ * @returns The stereo interleaved buffer
+ */
+DSP.interleave = function(left, right) {
+  if (left.length !== right.length) {
+    throw "Can not interleave. Channel lengths differ.";
+  }
+
+  var stereoInterleaved = new Float64Array(left.length * 2);
+
+  for (var i = 0, len = left.length; i < len; i++) {
+    stereoInterleaved[2*i]   = left[i];
+    stereoInterleaved[2*i+1] = right[i];
+  }
+
+  return stereoInterleaved;
+};
+
+/**
+ * Converts a stereo-interleaved sample buffer into split-stereo (dual mono) sample buffers
+ *
+ * @param {Array} buffer A stereo-interleaved sample buffer
+ *
+ * @returns an Array containing left and right channels
+ */
+DSP.deinterleave = (function() {
+  var left, right, mix, deinterleaveChannel = [];
+
+  deinterleaveChannel[DSP.MIX] = function(buffer) {
+    for (var i = 0, len = buffer.length/2; i < len; i++) {
+      mix[i] = (buffer[2*i] + buffer[2*i+1]) / 2;
+    }
+    return mix;
   };
 
-  this.generateReverseTable();
-}
+  deinterleaveChannel[DSP.LEFT] = function(buffer) {
+    for (var i = 0, len = buffer.length/2; i < len; i++) {
+      left[i]  = buffer[2*i];
+    }
+    return left;
+  };
 
+  deinterleaveChannel[DSP.RIGHT] = function(buffer) {
+    for (var i = 0, len = buffer.length/2; i < len; i++) {
+      right[i]  = buffer[2*i+1];
+    }
+    return right;
+  };
 
-// Ordering of output:
-//
-// trans[0]     = re[0] (==zero frequency, purely real)
-// trans[1]     = re[1]
-//             ...
-// trans[n/2-1] = re[n/2-1]
-// trans[n/2]   = re[n/2]    (==nyquist frequency, purely real)
-//
-// trans[n/2+1] = im[n/2-1]
-// trans[n/2+2] = im[n/2-2]
-//             ...
-// trans[n-1]   = im[1] 
+  return function(channel, buffer) {
+    left  = left  || new Float64Array(buffer.length/2);
+    right = right || new Float64Array(buffer.length/2);
+    mix   = mix   || new Float64Array(buffer.length/2);
 
-RFFT.prototype.forward = function(buffer) {
-  var n         = this.bufferSize, 
-      spectrum  = this.spectrum,
-      x         = this.trans, 
-      TWO_PI    = 2*Math.PI,
-      sqrt      = Math.sqrt,
-      i         = n >>> 1,
-      bSi       = 2 / n,
-      n2, n4, n8, nn, 
-      t1, t2, t3, t4, 
-      i1, i2, i3, i4, i5, i6, i7, i8, 
-      st1, cc1, ss1, cc3, ss3,
-      e, 
-      a,
-      rval, ival, mag; 
+    if (buffer.length/2 !== left.length) {
+      left  = new Float64Array(buffer.length/2);
+      right = new Float64Array(buffer.length/2);
+      mix   = new Float64Array(buffer.length/2);
+    }
 
-  this.reverseBinPermute(x, buffer);
+    return deinterleaveChannel[channel](buffer);
+  };
+}());
 
-  /*
-  var reverseTable = this.reverseTable;
+/**
+ * Separates a channel from a stereo-interleaved sample buffer
+ *
+ * @param {Array}  buffer A stereo-interleaved sample buffer
+ * @param {Number} channel A channel constant (LEFT, RIGHT, MIX)
+ *
+ * @returns an Array containing a signal mono sample buffer
+ */
+DSP.getChannel = DSP.deinterleave;
 
-  for (var k = 0, len = reverseTable.length; k < len; k++) {
-    x[k] = buffer[reverseTable[k]];
+/**
+ * Helper method (for Reverb) to mix two (interleaved) samplebuffers. It's possible
+ * to negate the second buffer while mixing and to perform a volume correction
+ * on the final signal.
+ *
+ * @param {Array} sampleBuffer1 Array containing Float values or a Float64Array
+ * @param {Array} sampleBuffer2 Array containing Float values or a Float64Array
+ * @param {Boolean} negate When true inverts/flips the audio signal
+ * @param {Number} volumeCorrection When you add multiple sample buffers, use this to tame your signal ;)
+ *
+ * @returns A new Float64Array interleaved buffer.
+ */
+DSP.mixSampleBuffers = function(sampleBuffer1, sampleBuffer2, negate, volumeCorrection){
+  var outputSamples = new Float64Array(sampleBuffer1);
+
+  for(var i = 0; i<sampleBuffer1.length; i++){
+    outputSamples[i] += (negate ? -sampleBuffer2[i] : sampleBuffer2[i]) / volumeCorrection;
   }
-  */
 
-  for (var ix = 0, id = 4; ix < n; id *= 4) {
-    for (var i0 = ix; i0 < n; i0 += id) {
-      //sumdiff(x[i0], x[i0+1]); // {a, b}  <--| {a+b, a-b}
-      st1 = x[i0] - x[i0+1];
-      x[i0] += x[i0+1];
-      x[i0+1] = st1;
-    } 
-    ix = 2*(id-1);
+  return outputSamples;
+};
+
+// Biquad filter types
+DSP.LPF = 0;                // H(s) = 1 / (s^2 + s/Q + 1)
+DSP.HPF = 1;                // H(s) = s^2 / (s^2 + s/Q + 1)
+DSP.BPF_CONSTANT_SKIRT = 2; // H(s) = s / (s^2 + s/Q + 1)  (constant skirt gain, peak gain = Q)
+DSP.BPF_CONSTANT_PEAK = 3;  // H(s) = (s/Q) / (s^2 + s/Q + 1)      (constant 0 dB peak gain)
+DSP.NOTCH = 4;              // H(s) = (s^2 + 1) / (s^2 + s/Q + 1)
+DSP.APF = 5;                // H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1)
+DSP.PEAKING_EQ = 6;         // H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
+DSP.LOW_SHELF = 7;          // H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1)
+DSP.HIGH_SHELF = 8;         // H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A)
+
+// Biquad filter parameter types
+DSP.Q = 1;
+DSP.BW = 2; // SHARED with BACKWARDS LOOP MODE
+DSP.S = 3;
+
+/**
+ * Find RMS of signal
+ * @param {Array} buffer
+ */
+DSP.RMS = function(buffer) {
+  var total = 0;
+
+  for (var i = 0, n = buffer.length; i < n; i++) {
+    total += buffer[i] * buffer[i];
   }
 
-  n2 = 2;
-  nn = n >>> 1;
+  return Math.sqrt(total / n);
+};
 
-  while((nn = nn >>> 1)) {
-    ix = 0;
-    n2 = n2 << 1;
-    id = n2 << 1;
-    n4 = n2 >>> 2;
-    n8 = n2 >>> 3;
-    do {
-      if(n4 !== 1) {
-        for(i0 = ix; i0 < n; i0 += id) {
-          i1 = i0;
-          i2 = i1 + n4;
-          i3 = i2 + n4;
-          i4 = i3 + n4;
-     
-          //diffsum3_r(x[i3], x[i4], t1); // {a, b, s} <--| {a, b-a, a+b}
-          t1 = x[i3] + x[i4];
-          x[i4] -= x[i3];
-          //sumdiff3(x[i1], t1, x[i3]);   // {a, b, d} <--| {a+b, b, a-b}
-          x[i3] = x[i1] - t1; 
-          x[i1] += t1;
-     
-          i1 += n8;
-          i2 += n8;
-          i3 += n8;
-          i4 += n8;
-         
-          //sumdiff(x[i3], x[i4], t1, t2); // {s, d}  <--| {a+b, a-b}
-          t1 = x[i3] + x[i4];
-          t2 = x[i3] - x[i4];
-         
-          t1 = -t1 * Math.SQRT1_2;
-          t2 *= Math.SQRT1_2;
-     
-          // sumdiff(t1, x[i2], x[i4], x[i3]); // {s, d}  <--| {a+b, a-b}
-          st1 = x[i2];
-          x[i4] = t1 + st1; 
-          x[i3] = t1 - st1;
-          
-          //sumdiff3(x[i1], t2, x[i2]); // {a, b, d} <--| {a+b, b, a-b}
-          x[i2] = x[i1] - t2;
-          x[i1] += t2;
-        }
-      } else {
-        for(i0 = ix; i0 < n; i0 += id) {
-          i1 = i0;
-          i2 = i1 + n4;
-          i3 = i2 + n4;
-          i4 = i3 + n4;
-     
-          //diffsum3_r(x[i3], x[i4], t1); // {a, b, s} <--| {a, b-a, a+b}
-          t1 = x[i3] + x[i4]; 
-          x[i4] -= x[i3];
-          
-          //sumdiff3(x[i1], t1, x[i3]);   // {a, b, d} <--| {a+b, b, a-b}
-          x[i3] = x[i1] - t1; 
-          x[i1] += t1;
-        }
-      }
-   
-      ix = (id << 1) - n2;
-      id = id << 2;
-    } while (ix < n);
- 
-    e = TWO_PI / n2;
+/**
+ * Find Peak of signal
+ * @param {Array} buffer
+ */
+DSP.Peak = function(buffer) {
+  var peak = 0;
 
-    for (var j = 1; j < n8; j++) {
-      a = j * e;
-      ss1 = Math.sin(a);
-      cc1 = Math.cos(a);
+  for (var i = 0, n = buffer.length; i < n; i++) {
+    peak = (Math.abs(buffer[i]) > peak) ? Math.abs(buffer[i]) : peak;
+  }
 
-      //ss3 = sin(3*a); cc3 = cos(3*a);
-      cc3 = 4*cc1*(cc1*cc1-0.75);
-      ss3 = 4*ss1*(0.75-ss1*ss1);
-   
-      ix = 0; id = n2 << 1;
-      do {
-        for (i0 = ix; i0 < n; i0 += id) {
-          i1 = i0 + j;
-          i2 = i1 + n4;
-          i3 = i2 + n4;
-          i4 = i3 + n4;
-       
-          i5 = i0 + n4 - j;
-          i6 = i5 + n4;
-          i7 = i6 + n4;
-          i8 = i7 + n4;
-       
-          //cmult(c, s, x, y, &u, &v)
-          //cmult(cc1, ss1, x[i7], x[i3], t2, t1); // {u,v} <--| {x*c-y*s, x*s+y*c}
-          t2 = x[i7]*cc1 - x[i3]*ss1; 
-          t1 = x[i7]*ss1 + x[i3]*cc1;
-          
-          //cmult(cc3, ss3, x[i8], x[i4], t4, t3);
-          t4 = x[i8]*cc3 - x[i4]*ss3; 
-          t3 = x[i8]*ss3 + x[i4]*cc3;
-       
-          //sumdiff(t2, t4);   // {a, b} <--| {a+b, a-b}
-          st1 = t2 - t4;
-          t2 += t4;
-          t4 = st1;
-          
-          //sumdiff(t2, x[i6], x[i8], x[i3]); // {s, d}  <--| {a+b, a-b}
-          //st1 = x[i6]; x[i8] = t2 + st1; x[i3] = t2 - st1;
-          x[i8] = t2 + x[i6]; 
-          x[i3] = t2 - x[i6];
-         
-          //sumdiff_r(t1, t3); // {a, b} <--| {a+b, b-a}
-          st1 = t3 - t1;
-          t1 += t3;
-          t3 = st1;
-          
-          //sumdiff(t3, x[i2], x[i4], x[i7]); // {s, d}  <--| {a+b, a-b}
-          //st1 = x[i2]; x[i4] = t3 + st1; x[i7] = t3 - st1;
-          x[i4] = t3 + x[i2]; 
-          x[i7] = t3 - x[i2];
-         
-          //sumdiff3(x[i1], t1, x[i6]);   // {a, b, d} <--| {a+b, b, a-b}
-          x[i6] = x[i1] - t1; 
-          x[i1] += t1;
-          
-          //diffsum3_r(t4, x[i5], x[i2]); // {a, b, s} <--| {a, b-a, a+b}
-          x[i2] = t4 + x[i5]; 
-          x[i5] -= t4;
-        }
-     
-        ix = (id << 1) - n2;
-        id = id << 2;
-   
-      } while (ix < n);
+  return peak;
+};
+
+/**
+ * Magnitude to decibels
+ *
+ * Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
+ * Copyright 2010 Ricard Marxer. All rights reserved.
+ *
+ * @param {Array} @buffer The array of magnitudes to convert to decibels
+ * @returns the array in decibels
+ *
+ */
+DSP.mag2db = function(buffer) {
+  var minDb = -120;
+  var minMag = Math.pow(10.0, minDb / 20.0);
+
+  var log = Math.log;
+  var max = Math.max;
+
+  var result = new Float64Array(buffer.length);
+  for (var i=0; i<buffer.length; i++) {
+    result[i] = 20.0*log(max(buffer[i], minMag));
+  }
+
+  return result;
+};
+
+/**
+ *  Frequency response
+ *
+ *  Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
+ *  Copyright 2010 Ricard Marxer. All rights reserved.
+ *
+ *  Calculates the frequency response at the given points.
+ *
+ *  @param {Number} b The coefficients of the filter
+ *  @param {Number} a The coefficients of the filter
+ *  @param {Number} w The points (normally between -PI and PI) where to calculate the frequency response
+ *
+ *  @returns the frequency response in magnitude
+ */
+DSP.freqz = function(b, a, w) {
+  var i, j;
+
+  if (!w) {
+    w = new Float64Array(200);
+    for (i=0;i<w.length; i++) {
+      w[i] = DSP.TWO_PI/w.length * i - Math.PI;
     }
   }
 
-  while (--i) {
-    rval = x[i];
-    ival = x[n-i-1];
-    mag = bSi * sqrt(rval * rval + ival * ival);
+  var result = new Float64Array(w.length);
 
-    if (mag > this.peak) {
-      this.peakBand = i;
-      this.peak = mag;
+  var sqrt = Math.sqrt;
+  var cos = Math.cos;
+  var sin = Math.sin;
+
+  for (i=0; i<w.length; i++) {
+    var numerator = {real:0.0, imag:0.0};
+    for (j=0; j<b.length; j++) {
+      numerator.real += b[j] * cos(-j*w[i]);
+      numerator.imag += b[j] * sin(-j*w[i]);
     }
 
-    spectrum[i] = mag;
-  }
+    var denominator = {real:0.0, imag:0.0};
+    for (j=0; j<a.length; j++) {
+      denominator.real += a[j] * cos(-j*w[i]);
+      denominator.imag += a[j] * sin(-j*w[i]);
+    }
 
-  spectrum[0] = bSi * x[0];
+    result[i] =  sqrt(numerator.real*numerator.real + numerator.imag*numerator.imag) / sqrt(denominator.real*denominator.real + denominator.imag*denominator.imag);
+  }
 
-  return spectrum;
+  return result;
 };
 
-function Sampler(file, bufferSize, sampleRate, playStart, playEnd, loopStart, loopEnd, loopMode) {
-  this.file = file;
-  this.bufferSize = bufferSize;
-  this.sampleRate = sampleRate;
-  this.playStart  = playStart || 0; // 0%
-  this.playEnd    = playEnd   || 1; // 100%
-  this.loopStart  = loopStart || 0;
-  this.loopEnd    = loopEnd   || 1;
-  this.loopMode   = loopMode  || DSP.OFF;
-  this.loaded     = false;
-  this.samples    = [];
-  this.signal     = new Float64Array(bufferSize);
-  this.frameCount = 0;
-  this.envelope   = null;
-  this.amplitude  = 1;
-  this.rootFrequency = 110; // A2 110
-  this.frequency  = 550;
-  this.step       = this.frequency / this.rootFrequency;
-  this.duration   = 0;
-  this.samplesProcessed = 0;
-  this.playhead   = 0;
- 
-  var audio = /* new Audio();*/ document.createElement("AUDIO");
-  var self = this;
- 
-  this.loadSamples = function(event) {
-    var buffer = DSP.getChannel(DSP.MIX, event.frameBuffer);
-    for ( var i = 0; i < buffer.length; i++) {
-      self.samples.push(buffer[i]);
+module.exports = DSP;
+
+},{}],5:[function(require,module,exports){
+/* global Float64Array Uint32Array */
+var FourierTransform = require("./fourier");
+
+/**
+ * FFT is a class for calculating the Discrete Fourier Transform of a signal
+ * with the Fast Fourier Transform algorithm.
+ *
+ * @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2
+ * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
+ *
+ * @constructor
+ */
+function FFT(bufferSize, sampleRate) {
+  FourierTransform.call(this, bufferSize, sampleRate);
+
+  this.reverseTable = new Uint32Array(bufferSize);
+
+  var limit = 1;
+  var bit = bufferSize >> 1;
+
+  var i;
+
+  while (limit < bufferSize) {
+    for (i = 0; i < limit; i++) {
+      this.reverseTable[i + limit] = this.reverseTable[i] + bit;
     }
-  };
- 
-  this.loadComplete = function() {
-    // convert flexible js array into a fast typed array
-    self.samples = new Float64Array(self.samples);
-    self.loaded = true;
-  };
- 
-  this.loadMetaData = function() {
-    self.duration = audio.duration;
-  };
- 
-  audio.addEventListener("MozAudioAvailable", this.loadSamples, false);
-  audio.addEventListener("loadedmetadata", this.loadMetaData, false);
-  audio.addEventListener("ended", this.loadComplete, false);
-  audio.muted = true;
-  audio.src = file;
-  audio.play();
+
+    limit = limit << 1;
+    bit = bit >> 1;
+  }
+
+  this.sinTable = new Float64Array(bufferSize);
+  this.cosTable = new Float64Array(bufferSize);
+
+  for (i = 0; i < bufferSize; i++) {
+    this.sinTable[i] = Math.sin(-Math.PI/i);
+    this.cosTable[i] = Math.cos(-Math.PI/i);
+  }
 }
 
-Sampler.prototype.applyEnvelope = function() {
-  this.envelope.process(this.signal);
-  return this.signal;
+/**
+ * Performs a forward transform on the sample buffer.
+ * Converts a time domain signal to frequency domain spectra.
+ *
+ * @param {Array} buffer The sample buffer. Buffer Length must be power of 2
+ *
+ * @returns The frequency spectrum array
+ */
+FFT.prototype.forward = function(buffer) {
+  // Locally scope variables for speed up
+  var bufferSize      = this.bufferSize,
+      cosTable        = this.cosTable,
+      sinTable        = this.sinTable,
+      reverseTable    = this.reverseTable,
+      real            = this.real,
+      imag            = this.imag;
+
+  var k = Math.floor(Math.log(bufferSize) / Math.LN2);
+
+  if (Math.pow(2, k) !== bufferSize) { throw "Invalid buffer size, must be a power of 2."; }
+  if (bufferSize !== buffer.length)  { throw "Supplied buffer is not the same size as defined FFT. FFT Size: " + bufferSize + " Buffer Size: " + buffer.length; }
+
+  var halfSize = 1,
+    phaseShiftStepReal,
+    phaseShiftStepImag,
+    currentPhaseShiftReal,
+    currentPhaseShiftImag,
+    off,
+    tr,
+    ti,
+    tmpReal,
+    i;
+
+  for (i = 0; i < bufferSize; i++) {
+    real[i] = buffer[reverseTable[i]];
+    imag[i] = 0;
+  }
+
+  while (halfSize < bufferSize) {
+    //phaseShiftStepReal = Math.cos(-Math.PI/halfSize);
+    //phaseShiftStepImag = Math.sin(-Math.PI/halfSize);
+    phaseShiftStepReal = cosTable[halfSize];
+    phaseShiftStepImag = sinTable[halfSize];
+
+    currentPhaseShiftReal = 1;
+    currentPhaseShiftImag = 0;
+
+    for (var fftStep = 0; fftStep < halfSize; fftStep++) {
+      i = fftStep;
+
+      while (i < bufferSize) {
+        off = i + halfSize;
+        tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]);
+        ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]);
+
+        real[off] = real[i] - tr;
+        imag[off] = imag[i] - ti;
+        real[i] += tr;
+        imag[i] += ti;
+
+        i += halfSize << 1;
+      }
+
+      tmpReal = currentPhaseShiftReal;
+      currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag);
+      currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal);
+    }
+
+    halfSize = halfSize << 1;
+  }
+
+  return this.calculateSpectrum();
+};
+
+/**
+ * Performs a inverse FFT transformation
+ * Converts a frequency domain spectra to a time domain signal
+ *
+ * @param {Array} real
+ * @param {Array} imag
+ *
+ * @returns The time domain signal
+ */
+FFT.prototype.inverse = function(real, imag) {
+  // Locally scope variables for speed up
+  var bufferSize      = this.bufferSize,
+      cosTable        = this.cosTable,
+      sinTable        = this.sinTable,
+      reverseTable    = this.reverseTable;
+
+      real = real || this.real;
+      imag = imag || this.imag;
+
+  var halfSize = 1,
+      phaseShiftStepReal,
+      phaseShiftStepImag,
+      currentPhaseShiftReal,
+      currentPhaseShiftImag,
+      off,
+      tr,
+      ti,
+      tmpReal,
+      i;
+
+  for (i = 0; i < bufferSize; i++) {
+    imag[i] *= -1;
+  }
+
+  var revReal = new Float64Array(bufferSize);
+  var revImag = new Float64Array(bufferSize);
+
+  for (i = 0; i < real.length; i++) {
+    revReal[i] = real[reverseTable[i]];
+    revImag[i] = imag[reverseTable[i]];
+  }
+
+  real = revReal;
+  imag = revImag;
+
+  while (halfSize < bufferSize) {
+    phaseShiftStepReal = cosTable[halfSize];
+    phaseShiftStepImag = sinTable[halfSize];
+    currentPhaseShiftReal = 1;
+    currentPhaseShiftImag = 0;
+
+    for (var fftStep = 0; fftStep < halfSize; fftStep++) {
+      i = fftStep;
+
+      while (i < bufferSize) {
+        off = i + halfSize;
+        tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]);
+        ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]);
+
+        real[off] = real[i] - tr;
+        imag[off] = imag[i] - ti;
+        real[i] += tr;
+        imag[i] += ti;
+
+        i += halfSize << 1;
+      }
+
+      tmpReal = currentPhaseShiftReal;
+      currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag);
+      currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal);
+    }
+
+    halfSize = halfSize << 1;
+  }
+
+  var buffer = new Float64Array(bufferSize); // this should be reused instead
+  for (i = 0; i < bufferSize; i++) {
+    buffer[i] = real[i] / bufferSize;
+  }
+
+  return buffer;
+};
+
+module.exports = FFT;
+
+},{"./fourier":6}],6:[function(require,module,exports){
+/* global Float64Array */
+
+/**
+ * A Mixin for every fourier module
+ * Fourier Transform Module used by DFT, FFT, RFFT
+ * @private
+ */
+module.exports = function FourierTransform(bufferSize, sampleRate) {
+  this.bufferSize = bufferSize;
+  this.sampleRate = sampleRate;
+  this.bandwidth  = 2 / bufferSize * sampleRate / 2;
+
+  this.spectrum   = new Float64Array(bufferSize/2);
+  this.real       = new Float64Array(bufferSize);
+  this.imag       = new Float64Array(bufferSize);
+
+  this.peakBand   = 0;
+  this.peak       = 0;
+
+  /**
+   * Calculates the *middle* frequency of an FFT band.
+   *
+   * @param {Number} index The index of the FFT band.
+   *
+   * @returns The middle frequency in Hz.
+   */
+  this.getBandFrequency = function(index) {
+    return this.bandwidth * index + this.bandwidth / 2;
+  };
+
+  /**
+   * Calculate the spectrum (amplitude magnitudes)
+   */
+  this.calculateSpectrum = function() {
+    var spectrum  = this.spectrum,
+      real      = this.real,
+      imag      = this.imag,
+      bSi       = 2 / this.bufferSize,
+      sqrt      = Math.sqrt,
+      rval,
+      ival,
+      mag;
+
+    for (var i = 0, N = bufferSize/2; i < N; i++) {
+      rval = real[i];
+      ival = imag[i];
+      mag = bSi * sqrt(rval * rval + ival * ival);
+
+      if (mag > this.peak) {
+        this.peakBand = i;
+        this.peak = mag;
+      }
+
+      spectrum[i] = mag;
+    }
+  };
+};
+
+},{}],7:[function(require,module,exports){
+/* global Float64Array */
+var DSP = require("./dsp");
+var Biquad = require("./biquad");
+
+/**
+ *  Create a Graphical Equalizer
+ *
+ *  Implementation of a graphic equalizer with a configurable bands-per-octave
+ *  and minimum and maximum frequencies
+ *
+ *  Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
+ *  Copyright 2010 Ricard Marxer. All rights reserved.
+ *
+ * @constructor
+ * @param {SampleRate}
+ * @example
+ * var eq = new GraphicalEq(44100)
+ */
+function GraphicalEq(sampleRate) {
+  this.FS = sampleRate;
+  this.minFreq = 40.0;
+  this.maxFreq = 16000.0;
+
+  this.bandsPerOctave = 1.0;
+
+  this.filters = [];
+  this.freqzs = [];
+
+  this.calculateFreqzs = true;
+
+  this.recalculateFilters = function() {
+    var bandCount = Math.round(Math.log(this.maxFreq/this.minFreq) * this.bandsPerOctave/ Math.LN2);
+
+    this.filters = [];
+    for (var i=0; i<bandCount; i++) {
+      var freq = this.minFreq*(Math.pow(2, i/this.bandsPerOctave));
+      var newFilter = new Biquad(DSP.PEAKING_EQ, this.FS);
+      newFilter.setDbGain(0);
+      newFilter.setBW(1/this.bandsPerOctave);
+      newFilter.setF0(freq);
+      this.filters[i] = newFilter;
+      this.recalculateFreqz(i);
+    }
+  };
+
+  this.setMinimumFrequency = function(freq) {
+    this.minFreq = freq;
+    this.recalculateFilters();
+  };
+
+  this.setMaximumFrequency = function(freq) {
+    this.maxFreq = freq;
+    this.recalculateFilters();
+  };
+
+  this.setBandsPerOctave = function(bands) {
+    this.bandsPerOctave = bands;
+    this.recalculateFilters();
+  };
+
+  this.setBandGain = function(bandIndex, gain) {
+    if (bandIndex < 0 || bandIndex > (this.filters.length-1)) {
+      throw "The band index of the graphical equalizer is out of bounds.";
+    }
+
+    if (!gain) {
+      throw "A gain must be passed.";
+    }
+
+    this.filters[bandIndex].setDbGain(gain);
+    this.recalculateFreqz(bandIndex);
+  };
+
+  this.recalculateFreqz = function(bandIndex) {
+    if (!this.calculateFreqzs) {
+      return;
+    }
+
+    if (bandIndex < 0 || bandIndex > (this.filters.length-1)) {
+      throw "The band index of the graphical equalizer is out of bounds. " + bandIndex + " is out of [" + 0 + ", " + this.filters.length-1 + "]";
+    }
+
+    if (!this.w) {
+      this.w = new Float64Array(400);
+      for (var i=0; i<this.w.length; i++) {
+         this.w[i] = Math.PI/this.w.length * i;
+      }
+    }
+
+    var b = [this.filters[bandIndex].b0, this.filters[bandIndex].b1, this.filters[bandIndex].b2];
+    var a = [this.filters[bandIndex].a0, this.filters[bandIndex].a1, this.filters[bandIndex].a2];
+
+    this.freqzs[bandIndex] = DSP.mag2db(DSP.freqz(b, a, this.w));
+  };
+
+  this.process = function(buffer) {
+    var output = buffer;
+
+    for (var i = 0; i < this.filters.length; i++) {
+      output = this.filters[i].process(output);
+    }
+
+    return output;
+  };
+
+  this.processStereo = function(buffer) {
+    var output = buffer;
+
+    for (var i = 0; i < this.filters.length; i++) {
+      output = this.filters[i].processStereo(output);
+    }
+
+    return output;
+  };
+}
+
+module.exports = GraphicalEq;
+
+},{"./biquad":2,"./dsp":4}],8:[function(require,module,exports){
+var DSP = require("./dsp");
+var ADSR = require("./adsr");
+
+/**
+ * IIRFilter
+ * @constructor
+ */
+function IIRFilter(type, cutoff, resonance, sampleRate) {
+  this.sampleRate = sampleRate;
+
+  switch(type) {
+    case DSP.LOWPASS:
+    case DSP.LP12:
+      this.func = new IIRFilter.LP12(cutoff, resonance, sampleRate);
+      break;
+  }
+}
+
+IIRFilter.prototype.__defineGetter__("cutoff",
+  function() {
+    return this.func.cutoff;
+  }
+);
+
+IIRFilter.prototype.__defineGetter__("resonance",
+  function() {
+    return this.func.resonance;
+  }
+);
+
+/**
+ * Set filter parameters
+ * @param {Number} cutoff
+ * @param {Number} resonance
+ */
+IIRFilter.prototype.set = function(cutoff, resonance) {
+  this.func.calcCoeff(cutoff, resonance);
+};
+
+/**
+ * Process a buffer
+ * @param {Array} buffer
+ */
+IIRFilter.prototype.process = function(buffer) {
+  this.func.process(buffer);
+};
+
+/**
+ * Add an envelope to the filter
+ * @param {ADSR} envelope
+ */
+IIRFilter.prototype.addEnvelope = function(envelope) {
+  if ( envelope instanceof ADSR ) {
+    this.func.addEnvelope(envelope);
+  } else {
+    throw "Not an envelope.";
+  }
+};
+
+/**
+ * LP12 filter
+ * @constructor
+ */
+IIRFilter.LP12 = function(cutoff, resonance, sampleRate) {
+  this.sampleRate = sampleRate;
+  this.vibraPos   = 0;
+  this.vibraSpeed = 0;
+  this.envelope = false;
+
+  this.calcCoeff = function(cutoff, resonance) {
+    this.w = 2.0 * Math.PI * cutoff / this.sampleRate;
+    this.q = 1.0 - this.w / (2.0 * (resonance + 0.5 / (1.0 + this.w)) + this.w - 2.0);
+    this.r = this.q * this.q;
+    this.c = this.r + 1.0 - 2.0 * Math.cos(this.w) * this.q;
+
+    this.cutoff = cutoff;
+    this.resonance = resonance;
+  };
+
+  this.calcCoeff(cutoff, resonance);
+
+  this.process = function(buffer) {
+    for ( var i = 0; i < buffer.length; i++ ) {
+      this.vibraSpeed += (buffer[i] - this.vibraPos) * this.c;
+      this.vibraPos   += this.vibraSpeed;
+      this.vibraSpeed *= this.r;
+
+      /*
+      var temp = this.vibraPos;
+
+      if ( temp > 1.0 ) {
+        temp = 1.0;
+      } else if ( temp < -1.0 ) {
+        temp = -1.0;
+      } else if ( temp != temp ) {
+        temp = 1;
+      }
+
+      buffer[i] = temp;
+      */
+
+      if (this.envelope) {
+        buffer[i] = (buffer[i] * (1 - this.envelope.value())) + (this.vibraPos * this.envelope.value());
+        this.envelope.samplesProcessed++;
+      } else {
+        buffer[i] = this.vibraPos;
+      }
+    }
+  };
+};
+
+/**
+ * Add an envelope to the filter
+ * @param {ADSR} envelope
+ */
+IIRFilter.LP12.prototype.addEnvelope = function(envelope) {
+  this.envelope = envelope;
+};
+
+module.exports = IIRFilter;
+
+},{"./adsr":1,"./dsp":4}],9:[function(require,module,exports){
+/* global Float64Array */
+var ADSR = require("./adsr");
+
+/**
+ * IIRFilter2
+ *
+ * @constructor
+ * @param {Number} type
+ * @param {Number} cutoff
+ * @param {Number} resonance
+ * @param {Number} sampleRate
+ */
+function IIRFilter2(type, cutoff, resonance, sampleRate) {
+  this.type = type;
+  this.cutoff = cutoff;
+  this.resonance = resonance;
+  this.sampleRate = sampleRate;
+
+  this.f = new Float64Array(4);
+  this.f[0] = 0.0; // lp
+  this.f[1] = 0.0; // hp
+  this.f[2] = 0.0; // bp
+  this.f[3] = 0.0; // br
+
+  this.calcCoeff = function(cutoff, resonance) {
+    this.freq = 2 * Math.sin(Math.PI * Math.min(0.25, cutoff/(this.sampleRate*2)));
+    this.damp = Math.min(2 * (1 - Math.pow(resonance, 0.25)), Math.min(2, 2/this.freq - this.freq * 0.5));
+  };
+
+  this.calcCoeff(cutoff, resonance);
+}
+
+/**
+ * Process a buffer
+ * @param {Array} buffer
+ */
+IIRFilter2.prototype.process = function(buffer) {
+  var input, output;
+  var f = this.f;
+
+  for ( var i = 0; i < buffer.length; i++ ) {
+    input = buffer[i];
+
+    // first pass
+    f[3] = input - this.damp * f[2];
+    f[0] = f[0] + this.freq * f[2];
+    f[1] = f[3] - f[0];
+    f[2] = this.freq * f[1] + f[2];
+    output = 0.5 * f[this.type];
+
+    // second pass
+    f[3] = input - this.damp * f[2];
+    f[0] = f[0] + this.freq * f[2];
+    f[1] = f[3] - f[0];
+    f[2] = this.freq * f[1] + f[2];
+    output += 0.5 * f[this.type];
+
+    if (this.envelope) {
+      buffer[i] = (buffer[i] * (1 - this.envelope.value())) + (output * this.envelope.value());
+      this.envelope.samplesProcessed++;
+    } else {
+      buffer[i] = output;
+    }
+  }
+};
+
+/**
+ * Add an envelope to the filter
+ * @param {ADSR} envelope
+ */
+IIRFilter2.prototype.addEnvelope = function(envelope) {
+  if ( envelope instanceof ADSR ) {
+    this.envelope = envelope;
+  } else {
+    throw "This is not an envelope.";
+  }
+};
+
+/**
+ * Set filter parameters
+ * @param {Number} cutoff
+ * @param {Number} resonance
+ */
+IIRFilter2.prototype.set = function(cutoff, resonance) {
+  this.calcCoeff(cutoff, resonance);
+};
+
+module.exports = IIRFilter2;
+
+},{"./adsr":1}],10:[function(require,module,exports){
+"use strict";
+
+/*
+ *  DSP.js - a comprehensive digital signal processing  library for javascript
+ *
+ *  Created by Corban Brook <corbanbrook@gmail.com> on 2010-01-01.
+ *  Copyright 2010 Corban Brook. All rights reserved.
+ *
+ */
+var DSPJS = {
+  DSP: require("./dsp"),
+  DFT: require("./dft"),
+  FFT: require("./fft"),
+  RFFT: require("./rfft"),
+  Sampler: require("./sampler"),
+  Oscillator: require("./oscillator"),
+  ADSR: require("./adsr"),
+  IIRFilter: require("./iir-filter"),
+  IIRFilter2: require("./iir-filter2"),
+  WindowFunction: require("./window-function"),
+  sinh: require("./sinh"),
+  Biquad: require("./biquad"),
+  GraphicalEq: require("./graphical-eq"),
+  MultiDelay: require("./multi-delay"),
+  SingleDelay: require("./single-delay"),
+  Reverb: require("./reverb")
+};
+
+if (typeof module === "object" && module.exports) module.exports = DSPJS;
+if (typeof window !== "undefined") {
+  Object.keys(DSPJS).forEach(function (k) {
+    window[k] = DSPJS[k];
+  });
+}
+
+},{"./adsr":1,"./biquad":2,"./dft":3,"./dsp":4,"./fft":5,"./graphical-eq":7,"./iir-filter":8,"./iir-filter2":9,"./multi-delay":11,"./oscillator":12,"./reverb":13,"./rfft":14,"./sampler":15,"./single-delay":16,"./sinh":17,"./window-function":18}],11:[function(require,module,exports){
+/* global Float64Array */
+/**
+ * MultiDelay effect by Almer Thie (http://code.almeros.com).
+ * Copyright 2010 Almer Thie. All rights reserved.
+ * Example: http://code.almeros.com/code-examples/delay-firefox-audio-api/
+ *
+ * This is a delay that feeds it's own delayed signal back into its circular
+ * buffer. Also known as a CombFilter.
+ *
+ * Compatible with interleaved stereo (or more channel) buffers and
+ * non-interleaved mono buffers.
+ *
+ * @param {Number} maxDelayInSamplesSize Maximum possible delay in samples (size of circular buffer)
+ * @param {Number} delayInSamples Initial delay in samples
+ * @param {Number} masterVolume Initial master volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ * @param {Number} delayVolume Initial feedback delay volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ *
+ * @constructor
+ */
+function MultiDelay(maxDelayInSamplesSize, delayInSamples, masterVolume, delayVolume) {
+  this.delayBufferSamples   = new Float64Array(maxDelayInSamplesSize); // The maximum size of delay
+  this.delayInputPointer     = delayInSamples;
+  this.delayOutputPointer   = 0;
+
+  this.delayInSamples   = delayInSamples;
+  this.masterVolume     = masterVolume;
+  this.delayVolume     = delayVolume;
+}
+
+/**
+ * Change the delay time in samples.
+ *
+ * @param {Number} delayInSamples Delay in samples
+ */
+MultiDelay.prototype.setDelayInSamples = function (delayInSamples) {
+  this.delayInSamples = delayInSamples;
+
+  this.delayInputPointer = this.delayOutputPointer + delayInSamples;
+
+  if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
+    this.delayInputPointer = this.delayInputPointer - this.delayBufferSamples.length;
+  }
 };
 
-Sampler.prototype.generate = function() {
-  var frameOffset = this.frameCount * this.bufferSize;
- 
-  var loopWidth = this.playEnd * this.samples.length - this.playStart * this.samples.length;
-  var playStartSamples = this.playStart * this.samples.length; // ie 0.5 -> 50% of the length
-  var playEndSamples = this.playEnd * this.samples.length; // ie 0.5 -> 50% of the length
-  var offset;
+/**
+ * Change the master volume.
+ *
+ * @param {Number} masterVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ */
+MultiDelay.prototype.setMasterVolume = function(masterVolume) {
+  this.masterVolume = masterVolume;
+};
 
-  for ( var i = 0; i < this.bufferSize; i++ ) {
-    switch (this.loopMode) {
-      case DSP.OFF:
-        this.playhead = Math.round(this.samplesProcessed * this.step + playStartSamples);
-        if (this.playhead < (this.playEnd * this.samples.length) ) {
-          this.signal[i] = this.samples[this.playhead] * this.amplitude;
-        } else {
-          this.signal[i] = 0;
-        }
-        break;
-     
-      case DSP.FW:
-        this.playhead = Math.round((this.samplesProcessed * this.step) % loopWidth + playStartSamples);
-        if (this.playhead < (this.playEnd * this.samples.length) ) {
-          this.signal[i] = this.samples[this.playhead] * this.amplitude;
-        }
-        break;
-       
-      case DSP.BW:
-        this.playhead = playEndSamples - Math.round((this.samplesProcessed * this.step) % loopWidth);
-        if (this.playhead < (this.playEnd * this.samples.length) ) {
-          this.signal[i] = this.samples[this.playhead] * this.amplitude;
-        }
-        break;
-       
-      case DSP.FWBW:
-        if ( Math.floor(this.samplesProcessed * this.step / loopWidth) % 2 === 0 ) {
-          this.playhead = Math.round((this.samplesProcessed * this.step) % loopWidth + playStartSamples);
-        } else {
-          this.playhead = playEndSamples - Math.round((this.samplesProcessed * this.step) % loopWidth);
-        }  
-        if (this.playhead < (this.playEnd * this.samples.length) ) {
-          this.signal[i] = this.samples[this.playhead] * this.amplitude;
-        }
-        break;
+/**
+ * Change the delay feedback volume.
+ *
+ * @param {Number} delayVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ */
+MultiDelay.prototype.setDelayVolume = function(delayVolume) {
+  this.delayVolume = delayVolume;
+};
+
+/**
+ * Process a given interleaved or mono non-interleaved float value Array and adds the delayed audio.
+ *
+ * @param {Array} samples Array containing Float values or a Float64Array
+ *
+ * @returns A new Float64Array interleaved or mono non-interleaved as was fed to this function.
+ */
+MultiDelay.prototype.process = function(samples) {
+  // NB. Make a copy to put in the output samples to return.
+  var outputSamples = new Float64Array(samples.length);
+
+  for (var i=0; i<samples.length; i++) {
+    // delayBufferSamples could contain initial NULL's, return silence in that case
+    var delaySample = (this.delayBufferSamples[this.delayOutputPointer] === null ? 0.0 : this.delayBufferSamples[this.delayOutputPointer]);
+
+    // Mix normal audio data with delayed audio
+    var sample = (delaySample * this.delayVolume) + samples[i];
+
+    // Add audio data with the delay in the delay buffer
+    this.delayBufferSamples[this.delayInputPointer] = sample;
+
+    // Return the audio with delay mix
+    outputSamples[i] = sample * this.masterVolume;
+
+    // Manage circulair delay buffer pointers
+    this.delayInputPointer++;
+    if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
+      this.delayInputPointer = 0;
     }
-    this.samplesProcessed++;
-  }
 
-  this.frameCount++;
+    this.delayOutputPointer++;
+    if (this.delayOutputPointer >= this.delayBufferSamples.length-1) {
+      this.delayOutputPointer = 0;
+    }
+  }
 
-  return this.signal;
+  return outputSamples;
 };
 
-Sampler.prototype.setFreq = function(frequency) {
-    var totalProcessed = this.samplesProcessed * this.step;
-    this.frequency = frequency;
-    this.step = this.frequency / this.rootFrequency;
-    this.samplesProcessed = Math.round(totalProcessed/this.step);
-};
+module.exports = MultiDelay;
 
-Sampler.prototype.reset = function() {
-  this.samplesProcessed = 0;
-  this.playhead = 0;
-};
+},{}],12:[function(require,module,exports){
+/* global Float64Array */
+var DSP = require("./dsp");
 
 /**
  * Oscillator class for generating and modifying signals
@@ -955,7 +1541,7 @@ Sampler.prototype.reset = function() {
  * @param {Number} bufferSize Size of the sample buffer to generate
  * @param {Number} sampleRate The sample rate of the signal
  *
- * @contructor
+ * @constructor
  */
 function Oscillator(type, frequency, amplitude, bufferSize, sampleRate) {
   this.frequency  = frequency;
@@ -964,7 +1550,7 @@ function Oscillator(type, frequency, amplitude, bufferSize, sampleRate) {
   this.sampleRate = sampleRate;
   //this.pulseWidth = pulseWidth;
   this.frameCount = 0;
- 
+
   this.waveTableLength = 2048;
 
   this.cyclesPerSample = frequency / sampleRate;
@@ -1001,14 +1587,14 @@ function Oscillator(type, frequency, amplitude, bufferSize, sampleRate) {
     }
   };
 
-  if ( typeof Oscillator.waveTable === 'undefined' ) {
+  if ( typeof Oscillator.waveTable === "undefined" ) {
     Oscillator.waveTable = {};
   }
 
-  if ( typeof Oscillator.waveTable[this.func] === 'undefined' ) {
+  if ( typeof Oscillator.waveTable[this.func] === "undefined" ) {
     this.generateWaveTable();
   }
- 
+
   this.waveTable = Oscillator.waveTable[this.func];
 }
 
@@ -1024,35 +1610,42 @@ Oscillator.prototype.setAmp = function(amplitude) {
     throw "Amplitude out of range (0..1).";
   }
 };
-  
+
 /**
  * Set the frequency of the signal
  *
  * @param {Number} frequency The frequency of the signal
- */  
+ */
 Oscillator.prototype.setFreq = function(frequency) {
   this.frequency = frequency;
   this.cyclesPerSample = frequency / this.sampleRate;
 };
-     
-// Add an oscillator
+
+/**
+ * Add an oscillator
+ * @param {Oscillator} oscillator The oscillator to be added to
+ * @return {Array} the current oscillator signal
+ */
 Oscillator.prototype.add = function(oscillator) {
   for ( var i = 0; i < this.bufferSize; i++ ) {
     //this.signal[i] += oscillator.valueAt(i);
     this.signal[i] += oscillator.signal[i];
   }
- 
+
   return this.signal;
 };
-     
-// Add a signal to the current generated osc signal
+
+/**
+ * Add a signal to the current generated osc signal
+ * @param {Array} signal
+ */
 Oscillator.prototype.addSignal = function(signal) {
   for ( var i = 0; i < signal.length; i++ ) {
     if ( i >= this.bufferSize ) {
       break;
     }
     this.signal[i] += signal[i];
-   
+
     /*
     // Constrain amplitude
     if ( this.signal[i] > 1 ) {
@@ -1064,20 +1657,34 @@ Oscillator.prototype.addSignal = function(signal) {
   }
   return this.signal;
 };
-     
-// Add an envelope to the oscillator
+
+/**
+ * Add an envelope to the oscillator
+ * @param {ADSR} envelope
+ */
 Oscillator.prototype.addEnvelope = function(envelope) {
   this.envelope = envelope;
 };
 
+/**
+ * Apply the oscillator envelope to its signal
+ */
 Oscillator.prototype.applyEnvelope = function() {
   this.envelope.process(this.signal);
 };
-     
+
+/**
+ * Get value
+ * @param {Number} offset
+ */
 Oscillator.prototype.valueAt = function(offset) {
   return this.waveTable[offset % this.waveTableLength];
 };
-     
+
+/**
+ * Generate the oscillator signal
+ * @return {Array} the signal
+ */
 Oscillator.prototype.generate = function() {
   var frameOffset = this.frameCount * this.bufferSize;
   var step = this.waveTableLength * this.frequency / this.sampleRate;
@@ -1087,967 +1694,671 @@ Oscillator.prototype.generate = function() {
     //var step = (frameOffset + i) * this.cyclesPerSample % 1;
     //this.signal[i] = this.func(step) * this.amplitude;
     //this.signal[i] = this.valueAt(Math.round((frameOffset + i) * step)) * this.amplitude;
-    offset = Math.round((frameOffset + i) * step);
-    this.signal[i] = this.waveTable[offset % this.waveTableLength] * this.amplitude;
-  }
-
-  this.frameCount++;
-
-  return this.signal;
-};
-
-Oscillator.Sine = function(step) {
-  return Math.sin(DSP.TWO_PI * step);
-};
-
-Oscillator.Square = function(step) {
-  return step < 0.5 ? 1 : -1;
-};
-
-Oscillator.Saw = function(step) {
-  return 2 * (step - Math.round(step));
-};
-
-Oscillator.Triangle = function(step) {
-  return 1 - 4 * Math.abs(Math.round(step) - step);
-};
-
-Oscillator.Pulse = function(step) {
-  // stub
-};
- 
-function ADSR(attackLength, decayLength, sustainLevel, sustainLength, releaseLength, sampleRate) {
-  this.sampleRate = sampleRate;
-  // Length in seconds
-  this.attackLength  = attackLength;
-  this.decayLength   = decayLength;
-  this.sustainLevel  = sustainLevel;
-  this.sustainLength = sustainLength;
-  this.releaseLength = releaseLength;
-  this.sampleRate    = sampleRate;
- 
-  // Length in samples
-  this.attackSamples  = attackLength  * sampleRate;
-  this.decaySamples   = decayLength   * sampleRate;
-  this.sustainSamples = sustainLength * sampleRate;
-  this.releaseSamples = releaseLength * sampleRate;
- 
-  // Updates the envelope sample positions
-  this.update = function() {
-    this.attack         =                this.attackSamples;
-    this.decay          = this.attack  + this.decaySamples;
-    this.sustain        = this.decay   + this.sustainSamples;
-    this.release        = this.sustain + this.releaseSamples;
-  };
- 
-  this.update();
- 
-  this.samplesProcessed = 0;
-}
-
-ADSR.prototype.noteOn = function() {
-  this.samplesProcessed = 0;
-  this.sustainSamples = this.sustainLength * this.sampleRate;
-  this.update();
-};
-
-// Send a note off when using a sustain of infinity to let the envelope enter the release phase
-ADSR.prototype.noteOff = function() {
-  this.sustainSamples = this.samplesProcessed - this.decaySamples;
-  this.update();
-};
-
-ADSR.prototype.processSample = function(sample) {
-  var amplitude = 0;
-
-  if ( this.samplesProcessed <= this.attack ) {
-    amplitude = 0 + (1 - 0) * ((this.samplesProcessed - 0) / (this.attack - 0));
-  } else if ( this.samplesProcessed > this.attack && this.samplesProcessed <= this.decay ) {
-    amplitude = 1 + (this.sustainLevel - 1) * ((this.samplesProcessed - this.attack) / (this.decay - this.attack));
-  } else if ( this.samplesProcessed > this.decay && this.samplesProcessed <= this.sustain ) {
-    amplitude = this.sustainLevel;
-  } else if ( this.samplesProcessed > this.sustain && this.samplesProcessed <= this.release ) {
-    amplitude = this.sustainLevel + (0 - this.sustainLevel) * ((this.samplesProcessed - this.sustain) / (this.release - this.sustain));
-  }
- 
-  return sample * amplitude;
-};
-
-ADSR.prototype.value = function() {
-  var amplitude = 0;
-
-  if ( this.samplesProcessed <= this.attack ) {
-    amplitude = 0 + (1 - 0) * ((this.samplesProcessed - 0) / (this.attack - 0));
-  } else if ( this.samplesProcessed > this.attack && this.samplesProcessed <= this.decay ) {
-    amplitude = 1 + (this.sustainLevel - 1) * ((this.samplesProcessed - this.attack) / (this.decay - this.attack));
-  } else if ( this.samplesProcessed > this.decay && this.samplesProcessed <= this.sustain ) {
-    amplitude = this.sustainLevel;
-  } else if ( this.samplesProcessed > this.sustain && this.samplesProcessed <= this.release ) {
-    amplitude = this.sustainLevel + (0 - this.sustainLevel) * ((this.samplesProcessed - this.sustain) / (this.release - this.sustain));
-  }
- 
-  return amplitude;
-};
-     
-ADSR.prototype.process = function(buffer) {
-  for ( var i = 0; i < buffer.length; i++ ) {
-    buffer[i] *= this.value();
-
-    this.samplesProcessed++;
-  }
- 
-  return buffer;
-};
-     
-     
-ADSR.prototype.isActive = function() {
-  if ( this.samplesProcessed > this.release || this.samplesProcessed === -1 ) {
-    return false;
-  } else {
-    return true;
-  }
-};
-
-ADSR.prototype.disable = function() {
-  this.samplesProcessed = -1;
-};
- 
-function IIRFilter(type, cutoff, resonance, sampleRate) {
-  this.sampleRate = sampleRate;
-
-  switch(type) {
-    case DSP.LOWPASS:
-    case DSP.LP12:
-      this.func = new IIRFilter.LP12(cutoff, resonance, sampleRate);
-      break;
+    offset = Math.round((frameOffset + i) * step);
+    this.signal[i] = this.waveTable[offset % this.waveTableLength] * this.amplitude;
   }
-}
 
-IIRFilter.prototype.__defineGetter__('cutoff',
-  function() {
-    return this.func.cutoff;
-  }
-);
+  this.frameCount++;
 
-IIRFilter.prototype.__defineGetter__('resonance',
-  function() {
-    return this.func.resonance;
-  }
-);
+  return this.signal;
+};
 
-IIRFilter.prototype.set = function(cutoff, resonance) {
-  this.func.calcCoeff(cutoff, resonance);
+Oscillator.Sine = function(step) {
+  return Math.sin(DSP.TWO_PI * step);
 };
 
-IIRFilter.prototype.process = function(buffer) {
-  this.func.process(buffer);
+Oscillator.Square = function(step) {
+  return step < 0.5 ? 1 : -1;
 };
 
-// Add an envelope to the filter
-IIRFilter.prototype.addEnvelope = function(envelope) {
-  if ( envelope instanceof ADSR ) {
-    this.func.addEnvelope(envelope);
-  } else {
-    throw "Not an envelope.";
-  }
+Oscillator.Saw = function(step) {
+  return 2 * (step - Math.round(step));
 };
 
-IIRFilter.LP12 = function(cutoff, resonance, sampleRate) {
-  this.sampleRate = sampleRate;
-  this.vibraPos   = 0;
-  this.vibraSpeed = 0;
-  this.envelope = false;
- 
-  this.calcCoeff = function(cutoff, resonance) {
-    this.w = 2.0 * Math.PI * cutoff / this.sampleRate;
-    this.q = 1.0 - this.w / (2.0 * (resonance + 0.5 / (1.0 + this.w)) + this.w - 2.0);
-    this.r = this.q * this.q;
-    this.c = this.r + 1.0 - 2.0 * Math.cos(this.w) * this.q;
-   
-    this.cutoff = cutoff;
-    this.resonance = resonance;
-  };
+Oscillator.Triangle = function(step) {
+  return 1 - 4 * Math.abs(Math.round(step) - step);
+};
 
-  this.calcCoeff(cutoff, resonance);
+Oscillator.Pulse = function() {
+  // stub
+};
 
-  this.process = function(buffer) {
-    for ( var i = 0; i < buffer.length; i++ ) {
-      this.vibraSpeed += (buffer[i] - this.vibraPos) * this.c;
-      this.vibraPos   += this.vibraSpeed;
-      this.vibraSpeed *= this.r;
-   
-      /*
-      var temp = this.vibraPos;
-     
-      if ( temp > 1.0 ) {
-        temp = 1.0;
-      } else if ( temp < -1.0 ) {
-        temp = -1.0;
-      } else if ( temp != temp ) {
-        temp = 1;
-      }
-     
-      buffer[i] = temp;
-      */
+module.exports = Oscillator;
 
-      if (this.envelope) {
-        buffer[i] = (buffer[i] * (1 - this.envelope.value())) + (this.vibraPos * this.envelope.value());
-        this.envelope.samplesProcessed++;
-      } else {
-        buffer[i] = this.vibraPos;
-      }
-    }
-  };
-}; 
+},{"./dsp":4}],13:[function(require,module,exports){
+/* global Float64Array */
+var DSP = require("./dsp");
+var IIRFilter2 = require("./iir-filter2");
+var SingleDelay = require("./single-delay");
+var MultiDelay = require("./multi-delay");
 
-IIRFilter.LP12.prototype.addEnvelope = function(envelope) {
-  this.envelope = envelope;
-};
+/**
+ * Reverb effect by Almer Thie (http://code.almeros.com).
+ * Copyright 2010 Almer Thie. All rights reserved.
+ * Example: http://code.almeros.com/code-examples/reverb-firefox-audio-api/
+ *
+ * This reverb consists of 6 SingleDelays, 6 MultiDelays and an IIRFilter2
+ * for each of the two stereo channels.
+ *
+ * Compatible with interleaved stereo buffers only!
+ *
+ * @param {Number} maxDelayInSamplesSize Maximum possible delay in samples (size of circular buffers)
+ * @param {Number} delayInSamples Initial delay in samples for internal (Single/Multi)delays
+ * @param {Number} masterVolume Initial master volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ * @param {Number} mixVolume Initial reverb signal mix volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ * @param {Number} delayVolume Initial feedback delay volume for internal (Single/Multi)delays. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ * @param {Number} dampFrequency Initial low pass filter frequency. 0 to 44100 (depending on your maximum sampling frequency)
+ *
+ * @constructor
+ */
+function Reverb(maxDelayInSamplesSize, delayInSamples, masterVolume, mixVolume, delayVolume, dampFrequency) {
+  this.delayInSamples   = delayInSamples;
+  this.masterVolume     = masterVolume;
+  this.mixVolume       = mixVolume;
+  this.delayVolume     = delayVolume;
+  this.dampFrequency     = dampFrequency;
 
-function IIRFilter2(type, cutoff, resonance, sampleRate) {
-  this.type = type;
-  this.cutoff = cutoff;
-  this.resonance = resonance;
-  this.sampleRate = sampleRate;
+  this.NR_OF_MULTIDELAYS = 6;
+  this.NR_OF_SINGLEDELAYS = 6;
 
-  this.f = Float64Array(4);
-  this.f[0] = 0.0; // lp
-  this.f[1] = 0.0; // hp
-  this.f[2] = 0.0; // bp
-  this.f[3] = 0.0; // br 
- 
-  this.calcCoeff = function(cutoff, resonance) {
-    this.freq = 2 * Math.sin(Math.PI * Math.min(0.25, cutoff/(this.sampleRate*2)));  
-    this.damp = Math.min(2 * (1 - Math.pow(resonance, 0.25)), Math.min(2, 2/this.freq - this.freq * 0.5));
-  };
+  this.LOWPASSL = new IIRFilter2(DSP.LOWPASS, dampFrequency, 0, 44100);
+  this.LOWPASSR = new IIRFilter2(DSP.LOWPASS, dampFrequency, 0, 44100);
 
-  this.calcCoeff(cutoff, resonance);
-}
+  this.singleDelays = [];
 
-IIRFilter2.prototype.process = function(buffer) {
-  var input, output;
-  var f = this.f;
+  var i, delayMultiply;
 
-  for ( var i = 0; i < buffer.length; i++ ) {
-    input = buffer[i];
+  for (i = 0; i < this.NR_OF_SINGLEDELAYS; i++) {
+    delayMultiply = 1.0 + (i/7.0); // 1.0, 1.1, 1.2...
+    this.singleDelays[i] = new SingleDelay(maxDelayInSamplesSize, Math.round(this.delayInSamples * delayMultiply), this.delayVolume);
+  }
 
-    // first pass
-    f[3] = input - this.damp * f[2];
-    f[0] = f[0] + this.freq * f[2];
-    f[1] = f[3] - f[0];
-    f[2] = this.freq * f[1] + f[2];
-    output = 0.5 * f[this.type];
+  this.multiDelays = [];
 
-    // second pass
-    f[3] = input - this.damp * f[2];
-    f[0] = f[0] + this.freq * f[2];
-    f[1] = f[3] - f[0];
-    f[2] = this.freq * f[1] + f[2];
-    output += 0.5 * f[this.type];
+  for (i = 0; i < this.NR_OF_MULTIDELAYS; i++) {
+    delayMultiply = 1.0 + (i/10.0); // 1.0, 1.1, 1.2...
+    this.multiDelays[i] = new MultiDelay(maxDelayInSamplesSize, Math.round(this.delayInSamples * delayMultiply), this.masterVolume, this.delayVolume);
+  }
+}
 
-    if (this.envelope) {
-      buffer[i] = (buffer[i] * (1 - this.envelope.value())) + (output * this.envelope.value());
-      this.envelope.samplesProcessed++;
-    } else {
-      buffer[i] = output;
-    }
+/**
+ * Change the delay time in samples as a base for all delays.
+ *
+ * @param {Number} delayInSamples Delay in samples
+ */
+Reverb.prototype.setDelayInSamples = function (delayInSamples){
+  this.delayInSamples = delayInSamples;
+
+  var i, delayMultiply;
+
+  for (i = 0; i < this.NR_OF_SINGLEDELAYS; i++) {
+    delayMultiply = 1.0 + (i/7.0); // 1.0, 1.1, 1.2...
+    this.singleDelays[i].setDelayInSamples( Math.round(this.delayInSamples * delayMultiply) );
   }
-};
 
-IIRFilter2.prototype.addEnvelope = function(envelope) {
-  if ( envelope instanceof ADSR ) {
-    this.envelope = envelope;
-  } else {
-    throw "This is not an envelope.";
+  for (i = 0; i < this.NR_OF_MULTIDELAYS; i++) {
+    delayMultiply = 1.0 + (i/10.0); // 1.0, 1.1, 1.2...
+    this.multiDelays[i].setDelayInSamples( Math.round(this.delayInSamples * delayMultiply) );
   }
 };
 
-IIRFilter2.prototype.set = function(cutoff, resonance) {
-  this.calcCoeff(cutoff, resonance);
+/**
+ * Change the master volume.
+ *
+ * @param {Number} masterVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ */
+Reverb.prototype.setMasterVolume = function (masterVolume){
+  this.masterVolume = masterVolume;
+};
+
+/**
+ * Change the reverb signal mix level.
+ *
+ * @param {Number} mixVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ */
+Reverb.prototype.setMixVolume = function (mixVolume){
+  this.mixVolume = mixVolume;
 };
 
+/**
+ * Change all delays feedback volume.
+ *
+ * @param {Number} delayVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ */
+Reverb.prototype.setDelayVolume = function (delayVolume){
+  this.delayVolume = delayVolume;
 
+  var i;
 
-function WindowFunction(type, alpha) {
-  this.alpha = alpha;
- 
-  switch(type) {
-    case DSP.BARTLETT:
-      this.func = WindowFunction.Bartlett;
-      break;
-     
-    case DSP.BARTLETTHANN:
-      this.func = WindowFunction.BartlettHann;
-      break;
-     
-    case DSP.BLACKMAN:
-      this.func = WindowFunction.Blackman;
-      this.alpha = this.alpha || 0.16;
-      break;
-   
-    case DSP.COSINE:
-      this.func = WindowFunction.Cosine;
-      break;
-     
-    case DSP.GAUSS:
-      this.func = WindowFunction.Gauss;
-      this.alpha = this.alpha || 0.25;
-      break;
-     
-    case DSP.HAMMING:
-      this.func = WindowFunction.Hamming;
-      break;
-     
-    case DSP.HANN:
-      this.func = WindowFunction.Hann;
-      break;
-   
-    case DSP.LANCZOS:
-      this.func = WindowFunction.Lanczoz;
-      break;
-     
-    case DSP.RECTANGULAR:
-      this.func = WindowFunction.Rectangular;
-      break;
-     
-    case DSP.TRIANGULAR:
-      this.func = WindowFunction.Triangular;
-      break;
+  for (i = 0; i<this.NR_OF_SINGLEDELAYS; i++) {
+    this.singleDelays[i].setDelayVolume(this.delayVolume);
   }
-}
 
-WindowFunction.prototype.process = function(buffer) {
-  var length = buffer.length;
-  for ( var i = 0; i < length; i++ ) {
-    buffer[i] *= this.func(length, i, this.alpha);
+  for (i = 0; i<this.NR_OF_MULTIDELAYS; i++) {
+    this.multiDelays[i].setDelayVolume(this.delayVolume);
   }
-  return buffer;
 };
 
-WindowFunction.Bartlett = function(length, index) {
-  return 2 / (length - 1) * ((length - 1) / 2 - Math.abs(index - (length - 1) / 2));
-};
+/**
+ * Change the Low Pass filter frequency.
+ *
+ * @param {Number} dampFrequency low pass filter frequency. 0 to 44100 (depending on your maximum sampling frequency)
+ */
+Reverb.prototype.setDampFrequency = function (dampFrequency){
+  this.dampFrequency = dampFrequency;
 
-WindowFunction.BartlettHann = function(length, index) {
-  return 0.62 - 0.48 * Math.abs(index / (length - 1) - 0.5) - 0.38 * Math.cos(DSP.TWO_PI * index / (length - 1));
+  this.LOWPASSL.set(dampFrequency, 0);
+  this.LOWPASSR.set(dampFrequency, 0);
 };
 
-WindowFunction.Blackman = function(length, index, alpha) {
-  var a0 = (1 - alpha) / 2;
-  var a1 = 0.5;
-  var a2 = alpha / 2;
+/**
+ * Process a given interleaved float value Array and copies and adds the reverb signal.
+ *
+ * @param {Array} samples Array containing Float values or a Float64Array
+ *
+ * @returns A new Float64Array interleaved buffer.
+ */
+Reverb.prototype.process = function (interleavedSamples){
+  // NB. Make a copy to put in the output samples to return.
+  var outputSamples = new Float64Array(interleavedSamples.length);
 
-  return a0 - a1 * Math.cos(DSP.TWO_PI * index / (length - 1)) + a2 * Math.cos(4 * Math.PI * index / (length - 1));
-};
+  // Perform low pass on the input samples to mimick damp
+  var leftRightMix = DSP.deinterleave(interleavedSamples);
+  this.LOWPASSL.process( leftRightMix[DSP.LEFT] );
+  this.LOWPASSR.process( leftRightMix[DSP.RIGHT] );
+  var filteredSamples = DSP.interleave(leftRightMix[DSP.LEFT], leftRightMix[DSP.RIGHT]);
 
-WindowFunction.Cosine = function(length, index) {
-  return Math.cos(Math.PI * index / (length - 1) - Math.PI / 2);
-};
+  var i;
 
-WindowFunction.Gauss = function(length, index, alpha) {
-  return Math.pow(Math.E, -0.5 * Math.pow((index - (length - 1) / 2) / (alpha * (length - 1) / 2), 2));
-};
+  // Process MultiDelays in parallel
+  for (i = 0; i<this.NR_OF_MULTIDELAYS; i++) {
+    // Invert the signal of every even multiDelay
+    outputSamples = DSP.mixSampleBuffers(outputSamples, this.multiDelays[i].process(filteredSamples), 2%i === 0, this.NR_OF_MULTIDELAYS);
+  }
 
-WindowFunction.Hamming = function(length, index) {
-  return 0.54 - 0.46 * Math.cos(DSP.TWO_PI * index / (length - 1));
-};
+  // Process SingleDelays in series
+  var singleDelaySamples = new Float64Array(outputSamples.length);
+  for (i = 0; i<this.NR_OF_SINGLEDELAYS; i++) {
+    // Invert the signal of every even singleDelay
+    singleDelaySamples = DSP.mixSampleBuffers(singleDelaySamples, this.singleDelays[i].process(outputSamples), 2%i === 0, 1);
+  }
 
-WindowFunction.Hann = function(length, index) {
-  return 0.5 * (1 - Math.cos(DSP.TWO_PI * index / (length - 1)));
-};
+  // Apply the volume of the reverb signal
+  for (i = 0; i<singleDelaySamples.length; i++) {
+    singleDelaySamples[i] *= this.mixVolume;
+  }
 
-WindowFunction.Lanczos = function(length, index) {
-  var x = 2 * index / (length - 1) - 1;
-  return Math.sin(Math.PI * x) / (Math.PI * x);
-};
+  // Mix the original signal with the reverb signal
+  outputSamples = DSP.mixSampleBuffers(singleDelaySamples, interleavedSamples, 0, 1);
 
-WindowFunction.Rectangular = function(length, index) {
-  return 1;
-};
+  // Apply the master volume to the complete signal
+  for (i = 0; i<outputSamples.length; i++) {
+    outputSamples[i] *= this.masterVolume;
+  }
 
-WindowFunction.Triangular = function(length, index) {
-  return 2 / length * (length / 2 - Math.abs(index - (length - 1) / 2));
+  return outputSamples;
 };
 
-function sinh (arg) {
-  // Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2 
-  //
-  // version: 1004.2314
-  // discuss at: http://phpjs.org/functions/sinh    // +   original by: Onno Marsman
-  // *     example 1: sinh(-0.9834330348825909);
-  // *     returns 1: -1.1497971402636502
-  return (Math.exp(arg) - Math.exp(-arg))/2;
-}
+module.exports = Reverb;
 
-/* 
- *  Biquad filter
- * 
- *  Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
- *  Copyright 2010 Ricard Marxer. All rights reserved.
+},{"./dsp":4,"./iir-filter2":9,"./multi-delay":11,"./single-delay":16}],14:[function(require,module,exports){
+/* global Float64Array Uint32Array */
+var FourierTransform = require("./fourier");
+
+/**
+ * RFFT is a class for calculating the Discrete Fourier Transform of a signal
+ * with the Fast Fourier Transform algorithm.
+ *
+ * This method currently only contains a forward transform but is highly optimized.
  *
+ * @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2
+ * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
+ *
+ * @constructor
  */
-// Implementation based on:
-// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
-function Biquad(type, sampleRate) {
-  this.Fs = sampleRate;
-  this.type = type;  // type of the filter
-  this.parameterType = DSP.Q; // type of the parameter
 
-  this.x_1_l = 0;
-  this.x_2_l = 0;
-  this.y_1_l = 0;
-  this.y_2_l = 0;
+// lookup tables don't really gain us any speed, but they do increase
+// cache footprint, so don't use them in here
 
-  this.x_1_r = 0;
-  this.x_2_r = 0;
-  this.y_1_r = 0;
-  this.y_2_r = 0;
+// also we don't use sepearate arrays for real/imaginary parts
 
-  this.b0 = 1;
-  this.a0 = 1;
+// this one a little more than twice as fast as the one in FFT
+// however I only did the forward transform
 
-  this.b1 = 0;
-  this.a1 = 0;
+// the rest of this was translated from C, see http://www.jjj.de/fxt/
+// this is the real split radix FFT
 
-  this.b2 = 0;
-  this.a2 = 0;
+function RFFT(bufferSize, sampleRate) {
+  FourierTransform.call(this, bufferSize, sampleRate);
 
-  this.b0a0 = this.b0 / this.a0;
-  this.b1a0 = this.b1 / this.a0;
-  this.b2a0 = this.b2 / this.a0;
-  this.a1a0 = this.a1 / this.a0;
-  this.a2a0 = this.a2 / this.a0;
+  this.trans = new Float64Array(bufferSize);
 
-  this.f0 = 3000;   // "wherever it's happenin', man."  Center Frequency or
-                    // Corner Frequency, or shelf midpoint frequency, depending
-                    // on which filter type.  The "significant frequency".
+  this.reverseTable = new Uint32Array(bufferSize);
 
-  this.dBgain = 12; // used only for peaking and shelving filters
+  // don't use a lookup table to do the permute, use this instead
+  this.reverseBinPermute = function (dest, source) {
+    var bufferSize  = this.bufferSize,
+        halfSize    = bufferSize >>> 1,
+        nm1         = bufferSize - 1,
+        i = 1, r = 0, h;
 
-  this.Q = 1;       // the EE kind of definition, except for peakingEQ in which A*Q is
-                    // the classic EE Q.  That adjustment in definition was made so that
-                    // a boost of N dB followed by a cut of N dB for identical Q and
-                    // f0/Fs results in a precisely flat unity gain filter or "wire".
+    dest[0] = source[0];
 
-  this.BW = -3;     // the bandwidth in octaves (between -3 dB frequencies for BPF
-                    // and notch or between midpoint (dBgain/2) gain frequencies for
-                    // peaking EQ
+    do {
+      r += halfSize;
+      dest[i] = source[r];
+      dest[r] = source[i];
 
-  this.S = 1;       // a "shelf slope" parameter (for shelving EQ only).  When S = 1,
-                    // the shelf slope is as steep as it can be and remain monotonically
-                    // increasing or decreasing gain with frequency.  The shelf slope, in
-                    // dB/octave, remains proportional to S for all other values for a
-                    // fixed f0/Fs and dBgain.
+      i++;
 
-  this.coefficients = function() {
-    var b = [this.b0, this.b1, this.b2];
-    var a = [this.a0, this.a1, this.a2];
-    return {b: b, a:a};
-  };
+      h = halfSize << 1;
+      while (h = h >> 1, !((r ^= h) & h));
 
-  this.setFilterType = function(type) {
-    this.type = type;
-    this.recalculateCoefficients();
-  };
+      if (r >= i) {
+        dest[i]     = source[r];
+        dest[r]     = source[i];
 
-  this.setSampleRate = function(rate) {
-    this.Fs = rate;
-    this.recalculateCoefficients();
+        dest[nm1-i] = source[nm1-r];
+        dest[nm1-r] = source[nm1-i];
+      }
+      i++;
+    } while (i < halfSize);
+    dest[nm1] = source[nm1];
   };
 
-  this.setQ = function(q) {
-    this.parameterType = DSP.Q;
-    this.Q = Math.max(Math.min(q, 115.0), 0.001);
-    this.recalculateCoefficients();
-  };
+  this.generateReverseTable = function () {
+    var bufferSize  = this.bufferSize,
+        halfSize    = bufferSize >>> 1,
+        nm1         = bufferSize - 1,
+        i = 1, r = 0, h;
 
-  this.setBW = function(bw) {
-    this.parameterType = DSP.BW;
-    this.BW = bw;
-    this.recalculateCoefficients();
-  };
+    this.reverseTable[0] = 0;
 
-  this.setS = function(s) {
-    this.parameterType = DSP.S;
-    this.S = Math.max(Math.min(s, 5.0), 0.0001);
-    this.recalculateCoefficients();
-  };
+    do {
+      r += halfSize;
 
-  this.setF0 = function(freq) {
-    this.f0 = freq;
-    this.recalculateCoefficients();
-  }; 
- 
-  this.setDbGain = function(g) {
-    this.dBgain = g;
-    this.recalculateCoefficients();
-  };
+      this.reverseTable[i] = r;
+      this.reverseTable[r] = i;
 
-  this.recalculateCoefficients = function() {
-    var A;
-    if (type === DSP.PEAKING_EQ || type === DSP.LOW_SHELF || type === DSP.HIGH_SHELF ) {
-      A = Math.pow(10, (this.dBgain/40));  // for peaking and shelving EQ filters only
-    } else {
-      A  = Math.sqrt( Math.pow(10, (this.dBgain/20)) );   
-    }
+      i++;
 
-    var w0 = DSP.TWO_PI * this.f0 / this.Fs;
+      h = halfSize << 1;
+      while (h = h >> 1, !((r ^= h) & h));
 
-    var cosw0 = Math.cos(w0);
-    var sinw0 = Math.sin(w0);
+      if (r >= i) {
+        this.reverseTable[i] = r;
+        this.reverseTable[r] = i;
 
-    var alpha = 0;
-   
-    switch (this.parameterType) {
-      case DSP.Q:
-        alpha = sinw0/(2*this.Q);
-        break;
-           
-      case DSP.BW:
-        alpha = sinw0 * sinh( Math.LN2/2 * this.BW * w0/sinw0 );
-        break;
+        this.reverseTable[nm1-i] = nm1-r;
+        this.reverseTable[nm1-r] = nm1-i;
+      }
+      i++;
+    } while (i < halfSize);
 
-      case DSP.S:
-        alpha = sinw0/2 * Math.sqrt( (A + 1/A)*(1/this.S - 1) + 2 );
-        break;
-    }
+    this.reverseTable[nm1] = nm1;
+  };
 
-    /**
-        FYI: The relationship between bandwidth and Q is
-             1/Q = 2*sinh(ln(2)/2*BW*w0/sin(w0))     (digital filter w BLT)
-        or   1/Q = 2*sinh(ln(2)/2*BW)             (analog filter prototype)
+  this.generateReverseTable();
+}
+
+
+/**
+ * Performs a forward transform on the sample buffer.
+ * Converts a time domain signal to frequency domain spectra.
+ *
+ * @param {Array} buffer The sample buffer. Buffer Length must be power of 2
+ *
+ * @returns The frequency spectrum array
+ */
+
+// Ordering of output:
+//
+// trans[0]     = re[0] (==zero frequency, purely real)
+// trans[1]     = re[1]
+//             ...
+// trans[n/2-1] = re[n/2-1]
+// trans[n/2]   = re[n/2]    (==nyquist frequency, purely real)
+//
+// trans[n/2+1] = im[n/2-1]
+// trans[n/2+2] = im[n/2-2]
+//             ...
+// trans[n-1]   = im[1]
+
+RFFT.prototype.forward = function(buffer) {
+  var n         = this.bufferSize,
+      spectrum  = this.spectrum,
+      x         = this.trans,
+      TWO_PI    = 2*Math.PI,
+      sqrt      = Math.sqrt,
+      i         = n >>> 1,
+      bSi       = 2 / n,
+      n2, n4, n8, nn,
+      t1, t2, t3, t4,
+      i1, i2, i3, i4, i5, i6, i7, i8,
+      st1, cc1, ss1, cc3, ss3,
+      e,
+      a,
+      rval, ival, mag;
 
-        The relationship between shelf slope and Q is
-             1/Q = sqrt((A + 1/A)*(1/S - 1) + 2)
-    */
+  this.reverseBinPermute(x, buffer);
 
-    var coeff;
+  /*
+  var reverseTable = this.reverseTable;
 
-    switch (this.type) {
-      case DSP.LPF:       // H(s) = 1 / (s^2 + s/Q + 1)
-        this.b0 =  (1 - cosw0)/2;
-        this.b1 =   1 - cosw0;
-        this.b2 =  (1 - cosw0)/2;
-        this.a0 =   1 + alpha;
-        this.a1 =  -2 * cosw0;
-        this.a2 =   1 - alpha;
-        break;
+  for (var k = 0, len = reverseTable.length; k < len; k++) {
+    x[k] = buffer[reverseTable[k]];
+  }
+  */
 
-      case DSP.HPF:       // H(s) = s^2 / (s^2 + s/Q + 1)
-        this.b0 =  (1 + cosw0)/2;
-        this.b1 = -(1 + cosw0);
-        this.b2 =  (1 + cosw0)/2;
-        this.a0 =   1 + alpha;
-        this.a1 =  -2 * cosw0;
-        this.a2 =   1 - alpha;
-        break;
+  for (var ix = 0, id = 4; ix < n; id *= 4) {
+    for (var i0 = ix; i0 < n; i0 += id) {
+      //sumdiff(x[i0], x[i0+1]); // {a, b}  <--| {a+b, a-b}
+      st1 = x[i0] - x[i0+1];
+      x[i0] += x[i0+1];
+      x[i0+1] = st1;
+    }
+    ix = 2*(id-1);
+  }
 
-      case DSP.BPF_CONSTANT_SKIRT:       // H(s) = s / (s^2 + s/Q + 1)  (constant skirt gain, peak gain = Q)
-        this.b0 =   sinw0/2;
-        this.b1 =   0;
-        this.b2 =  -sinw0/2;
-        this.a0 =   1 + alpha;
-        this.a1 =  -2*cosw0;
-        this.a2 =   1 - alpha;
-        break;
+  n2 = 2;
+  nn = n >>> 1;
 
-      case DSP.BPF_CONSTANT_PEAK:       // H(s) = (s/Q) / (s^2 + s/Q + 1)      (constant 0 dB peak gain)
-        this.b0 =   alpha;
-        this.b1 =   0;
-        this.b2 =  -alpha;
-        this.a0 =   1 + alpha;
-        this.a1 =  -2*cosw0;
-        this.a2 =   1 - alpha;
-        break;
+  while((nn = nn >>> 1)) {
+    ix = 0;
+    n2 = n2 << 1;
+    id = n2 << 1;
+    n4 = n2 >>> 2;
+    n8 = n2 >>> 3;
+    do {
+      if(n4 !== 1) {
+        for(i0 = ix; i0 < n; i0 += id) {
+          i1 = i0;
+          i2 = i1 + n4;
+          i3 = i2 + n4;
+          i4 = i3 + n4;
 
-      case DSP.NOTCH:     // H(s) = (s^2 + 1) / (s^2 + s/Q + 1)
-        this.b0 =   1;
-        this.b1 =  -2*cosw0;
-        this.b2 =   1;
-        this.a0 =   1 + alpha;
-        this.a1 =  -2*cosw0;
-        this.a2 =   1 - alpha;
-        break;
+          //diffsum3_r(x[i3], x[i4], t1); // {a, b, s} <--| {a, b-a, a+b}
+          t1 = x[i3] + x[i4];
+          x[i4] -= x[i3];
+          //sumdiff3(x[i1], t1, x[i3]);   // {a, b, d} <--| {a+b, b, a-b}
+          x[i3] = x[i1] - t1;
+          x[i1] += t1;
 
-      case DSP.APF:       // H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1)
-        this.b0 =   1 - alpha;
-        this.b1 =  -2*cosw0;
-        this.b2 =   1 + alpha;
-        this.a0 =   1 + alpha;
-        this.a1 =  -2*cosw0;
-        this.a2 =   1 - alpha;
-        break;
+          i1 += n8;
+          i2 += n8;
+          i3 += n8;
+          i4 += n8;
 
-      case DSP.PEAKING_EQ:  // H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
-        this.b0 =   1 + alpha*A;
-        this.b1 =  -2*cosw0;
-        this.b2 =   1 - alpha*A;
-        this.a0 =   1 + alpha/A;
-        this.a1 =  -2*cosw0;
-        this.a2 =   1 - alpha/A;
-        break;
+          //sumdiff(x[i3], x[i4], t1, t2); // {s, d}  <--| {a+b, a-b}
+          t1 = x[i3] + x[i4];
+          t2 = x[i3] - x[i4];
 
-      case DSP.LOW_SHELF:   // H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1)
-        coeff = sinw0 * Math.sqrt( (A^2 + 1)*(1/this.S - 1) + 2*A );
-        this.b0 =    A*((A+1) - (A-1)*cosw0 + coeff);
-        this.b1 =  2*A*((A-1) - (A+1)*cosw0);
-        this.b2 =    A*((A+1) - (A-1)*cosw0 - coeff);
-        this.a0 =       (A+1) + (A-1)*cosw0 + coeff;
-        this.a1 =   -2*((A-1) + (A+1)*cosw0);
-        this.a2 =       (A+1) + (A-1)*cosw0 - coeff;
-        break;
+          t1 = -t1 * Math.SQRT1_2;
+          t2 *= Math.SQRT1_2;
 
-      case DSP.HIGH_SHELF:   // H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A)
-        coeff = sinw0 * Math.sqrt( (A^2 + 1)*(1/this.S - 1) + 2*A );
-        this.b0 =    A*((A+1) + (A-1)*cosw0 + coeff);
-        this.b1 = -2*A*((A-1) + (A+1)*cosw0);
-        this.b2 =    A*((A+1) + (A-1)*cosw0 - coeff);
-        this.a0 =       (A+1) - (A-1)*cosw0 + coeff;
-        this.a1 =    2*((A-1) - (A+1)*cosw0);
-        this.a2 =       (A+1) - (A-1)*cosw0 - coeff;
-        break;
-    }
-   
-    this.b0a0 = this.b0/this.a0;
-    this.b1a0 = this.b1/this.a0;
-    this.b2a0 = this.b2/this.a0;
-    this.a1a0 = this.a1/this.a0;
-    this.a2a0 = this.a2/this.a0;
-  };
+          // sumdiff(t1, x[i2], x[i4], x[i3]); // {s, d}  <--| {a+b, a-b}
+          st1 = x[i2];
+          x[i4] = t1 + st1;
+          x[i3] = t1 - st1;
 
-  this.process = function(buffer) {
-      //y[n] = (b0/a0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2]
-      //       - (a1/a0)*y[n-1] - (a2/a0)*y[n-2]
+          //sumdiff3(x[i1], t2, x[i2]); // {a, b, d} <--| {a+b, b, a-b}
+          x[i2] = x[i1] - t2;
+          x[i1] += t2;
+        }
+      } else {
+        for(i0 = ix; i0 < n; i0 += id) {
+          i1 = i0;
+          i2 = i1 + n4;
+          i3 = i2 + n4;
+          i4 = i3 + n4;
 
-      var len = buffer.length;
-      var output = new Float64Array(len);
+          //diffsum3_r(x[i3], x[i4], t1); // {a, b, s} <--| {a, b-a, a+b}
+          t1 = x[i3] + x[i4];
+          x[i4] -= x[i3];
 
-      for ( var i=0; i<buffer.length; i++ ) {
-        output[i] = this.b0a0*buffer[i] + this.b1a0*this.x_1_l + this.b2a0*this.x_2_l - this.a1a0*this.y_1_l - this.a2a0*this.y_2_l;
-        this.y_2_l = this.y_1_l;
-        this.y_1_l = output[i];
-        this.x_2_l = this.x_1_l;
-        this.x_1_l = buffer[i];
+          //sumdiff3(x[i1], t1, x[i3]);   // {a, b, d} <--| {a+b, b, a-b}
+          x[i3] = x[i1] - t1;
+          x[i1] += t1;
+        }
       }
 
-      return output;
-  };
+      ix = (id << 1) - n2;
+      id = id << 2;
+    } while (ix < n);
 
-  this.processStereo = function(buffer) {
-      //y[n] = (b0/a0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2]
-      //       - (a1/a0)*y[n-1] - (a2/a0)*y[n-2]
+    e = TWO_PI / n2;
 
-      var len = buffer.length;
-      var output = new Float64Array(len);
-     
-      for (var i = 0; i < len/2; i++) {
-        output[2*i] = this.b0a0*buffer[2*i] + this.b1a0*this.x_1_l + this.b2a0*this.x_2_l - this.a1a0*this.y_1_l - this.a2a0*this.y_2_l;
-        this.y_2_l = this.y_1_l;
-        this.y_1_l = output[2*i];
-        this.x_2_l = this.x_1_l;
-        this.x_1_l = buffer[2*i];
+    for (var j = 1; j < n8; j++) {
+      a = j * e;
+      ss1 = Math.sin(a);
+      cc1 = Math.cos(a);
 
-        output[2*i+1] = this.b0a0*buffer[2*i+1] + this.b1a0*this.x_1_r + this.b2a0*this.x_2_r - this.a1a0*this.y_1_r - this.a2a0*this.y_2_r;
-        this.y_2_r = this.y_1_r;
-        this.y_1_r = output[2*i+1];
-        this.x_2_r = this.x_1_r;
-        this.x_1_r = buffer[2*i+1];
-      }
+      //ss3 = sin(3*a); cc3 = cos(3*a);
+      cc3 = 4*cc1*(cc1*cc1-0.75);
+      ss3 = 4*ss1*(0.75-ss1*ss1);
 
-      return output;
-  };
-}
+      ix = 0; id = n2 << 1;
+      do {
+        for (i0 = ix; i0 < n; i0 += id) {
+          i1 = i0 + j;
+          i2 = i1 + n4;
+          i3 = i2 + n4;
+          i4 = i3 + n4;
 
-/* 
- *  Magnitude to decibels
- * 
- *  Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
- *  Copyright 2010 Ricard Marxer. All rights reserved.
- *
- *  @buffer array of magnitudes to convert to decibels
- *
- *  @returns the array in decibels
- *
- */
-DSP.mag2db = function(buffer) {
-  var minDb = -120;
-  var minMag = Math.pow(10.0, minDb / 20.0);
+          i5 = i0 + n4 - j;
+          i6 = i5 + n4;
+          i7 = i6 + n4;
+          i8 = i7 + n4;
 
-  var log = Math.log;
-  var max = Math.max;
- 
-  var result = Float64Array(buffer.length);
-  for (var i=0; i<buffer.length; i++) {
-    result[i] = 20.0*log(max(buffer[i], minMag));
-  }
+          //cmult(c, s, x, y, &u, &v)
+          //cmult(cc1, ss1, x[i7], x[i3], t2, t1); // {u,v} <--| {x*c-y*s, x*s+y*c}
+          t2 = x[i7]*cc1 - x[i3]*ss1;
+          t1 = x[i7]*ss1 + x[i3]*cc1;
 
-  return result;
-};
+          //cmult(cc3, ss3, x[i8], x[i4], t4, t3);
+          t4 = x[i8]*cc3 - x[i4]*ss3;
+          t3 = x[i8]*ss3 + x[i4]*cc3;
 
-/* 
- *  Frequency response
- * 
- *  Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
- *  Copyright 2010 Ricard Marxer. All rights reserved.
- *
- *  Calculates the frequency response at the given points.
- *
- *  @b b coefficients of the filter
- *  @a a coefficients of the filter
- *  @w w points (normally between -PI and PI) where to calculate the frequency response
- *
- *  @returns the frequency response in magnitude
- *
- */
-DSP.freqz = function(b, a, w) {
-  var i, j;
+          //sumdiff(t2, t4);   // {a, b} <--| {a+b, a-b}
+          st1 = t2 - t4;
+          t2 += t4;
+          t4 = st1;
+
+          //sumdiff(t2, x[i6], x[i8], x[i3]); // {s, d}  <--| {a+b, a-b}
+          //st1 = x[i6]; x[i8] = t2 + st1; x[i3] = t2 - st1;
+          x[i8] = t2 + x[i6];
+          x[i3] = t2 - x[i6];
+
+          //sumdiff_r(t1, t3); // {a, b} <--| {a+b, b-a}
+          st1 = t3 - t1;
+          t1 += t3;
+          t3 = st1;
+
+          //sumdiff(t3, x[i2], x[i4], x[i7]); // {s, d}  <--| {a+b, a-b}
+          //st1 = x[i2]; x[i4] = t3 + st1; x[i7] = t3 - st1;
+          x[i4] = t3 + x[i2];
+          x[i7] = t3 - x[i2];
 
-  if (!w) {
-    w = Float64Array(200);
-    for (i=0;i<w.length; i++) {
-      w[i] = DSP.TWO_PI/w.length * i - Math.PI;
+          //sumdiff3(x[i1], t1, x[i6]);   // {a, b, d} <--| {a+b, b, a-b}
+          x[i6] = x[i1] - t1;
+          x[i1] += t1;
+
+          //diffsum3_r(t4, x[i5], x[i2]); // {a, b, s} <--| {a, b-a, a+b}
+          x[i2] = t4 + x[i5];
+          x[i5] -= t4;
+        }
+
+        ix = (id << 1) - n2;
+        id = id << 2;
+
+      } while (ix < n);
     }
   }
 
-  var result = Float64Array(w.length);
- 
-  var sqrt = Math.sqrt;
-  var cos = Math.cos;
-  var sin = Math.sin;
- 
-  for (i=0; i<w.length; i++) {
-    var numerator = {real:0.0, imag:0.0};
-    for (j=0; j<b.length; j++) {
-      numerator.real += b[j] * cos(-j*w[i]);
-      numerator.imag += b[j] * sin(-j*w[i]);
-    }
+  while (--i) {
+    rval = x[i];
+    ival = x[n-i-1];
+    mag = bSi * sqrt(rval * rval + ival * ival);
 
-    var denominator = {real:0.0, imag:0.0};
-    for (j=0; j<a.length; j++) {
-      denominator.real += a[j] * cos(-j*w[i]);
-      denominator.imag += a[j] * sin(-j*w[i]);
+    if (mag > this.peak) {
+      this.peakBand = i;
+      this.peak = mag;
     }
- 
-    result[i] =  sqrt(numerator.real*numerator.real + numerator.imag*numerator.imag) / sqrt(denominator.real*denominator.real + denominator.imag*denominator.imag);
+
+    spectrum[i] = mag;
   }
 
-  return result;
-};
+  spectrum[0] = bSi * x[0];
 
-/* 
- *  Graphical Equalizer
- *
- *  Implementation of a graphic equalizer with a configurable bands-per-octave
- *  and minimum and maximum frequencies
- * 
- *  Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
- *  Copyright 2010 Ricard Marxer. All rights reserved.
- *
- */
-function GraphicalEq(sampleRate) {
-  this.FS = sampleRate;
-  this.minFreq = 40.0;
-  this.maxFreq = 16000.0;
+  return spectrum;
+};
 
-  this.bandsPerOctave = 1.0;
+module.exports = RFFT;
 
-  this.filters = [];
-  this.freqzs = [];
+},{"./fourier":6}],15:[function(require,module,exports){
+/* global Float64Array */
+var DSP = require("./dsp");
 
-  this.calculateFreqzs = true;
+/**
+ * Sampler
+ *
+ * @constructor
+ * @param {} file
+ * @param {Number} bufferSize
+ * @param {Number} sampleRate
+ * @param {Number} playStart
+ * @param {Number} playEnd
+ * @param {Number} loopStart
+ * @param {Number} loopEnd
+ * @param {Number} loopMode
+ */
+function Sampler(file, bufferSize, sampleRate, playStart, playEnd, loopStart, loopEnd, loopMode) {
+  this.file = file;
+  this.bufferSize = bufferSize;
+  this.sampleRate = sampleRate;
+  this.playStart  = playStart || 0; // 0%
+  this.playEnd    = playEnd   || 1; // 100%
+  this.loopStart  = loopStart || 0;
+  this.loopEnd    = loopEnd   || 1;
+  this.loopMode   = loopMode  || DSP.OFF;
+  this.loaded     = false;
+  this.samples    = [];
+  this.signal     = new Float64Array(bufferSize);
+  this.frameCount = 0;
+  this.envelope   = null;
+  this.amplitude  = 1;
+  this.rootFrequency = 110; // A2 110
+  this.frequency  = 550;
+  this.step       = this.frequency / this.rootFrequency;
+  this.duration   = 0;
+  this.samplesProcessed = 0;
+  this.playhead   = 0;
 
-  this.recalculateFilters = function() {
-    var bandCount = Math.round(Math.log(this.maxFreq/this.minFreq) * this.bandsPerOctave/ Math.LN2);
+  var audio = /* new Audio();*/ document.createElement("AUDIO");
+  var self = this;
 
-    this.filters = [];
-    for (var i=0; i<bandCount; i++) {
-      var freq = this.minFreq*(Math.pow(2, i/this.bandsPerOctave));
-      var newFilter = new Biquad(DSP.PEAKING_EQ, this.FS);
-      newFilter.setDbGain(0);
-      newFilter.setBW(1/this.bandsPerOctave);
-      newFilter.setF0(freq);
-      this.filters[i] = newFilter;
-      this.recalculateFreqz(i);
+  this.loadSamples = function(event) {
+    var buffer = DSP.getChannel(DSP.MIX, event.frameBuffer);
+    for ( var i = 0; i < buffer.length; i++) {
+      self.samples.push(buffer[i]);
     }
   };
 
-  this.setMinimumFrequency = function(freq) {
-    this.minFreq = freq;
-    this.recalculateFilters();
-  };
-
-  this.setMaximumFrequency = function(freq) {
-    this.maxFreq = freq;
-    this.recalculateFilters();
-  };
-
-  this.setBandsPerOctave = function(bands) {
-    this.bandsPerOctave = bands;
-    this.recalculateFilters();
+  this.loadComplete = function() {
+    // convert flexible js array into a fast typed array
+    self.samples = new Float64Array(self.samples);
+    self.loaded = true;
   };
 
-  this.setBandGain = function(bandIndex, gain) {
-    if (bandIndex < 0 || bandIndex > (this.filters.length-1)) {
-      throw "The band index of the graphical equalizer is out of bounds.";
-    }
-
-    if (!gain) {
-      throw "A gain must be passed.";
-    }
-   
-    this.filters[bandIndex].setDbGain(gain);
-    this.recalculateFreqz(bandIndex);
+  this.loadMetaData = function() {
+    self.duration = audio.duration;
   };
- 
-  this.recalculateFreqz = function(bandIndex) {
-    if (!this.calculateFreqzs) {
-      return;
-    }
 
-    if (bandIndex < 0 || bandIndex > (this.filters.length-1)) {
-      throw "The band index of the graphical equalizer is out of bounds. " + bandIndex + " is out of [" + 0 + ", " + this.filters.length-1 + "]";
-    }
-       
-    if (!this.w) {
-      this.w = Float64Array(400);
-      for (var i=0; i<this.w.length; i++) {
-         this.w[i] = Math.PI/this.w.length * i;
-      }
-    }
-   
-    var b = [this.filters[bandIndex].b0, this.filters[bandIndex].b1, this.filters[bandIndex].b2];
-    var a = [this.filters[bandIndex].a0, this.filters[bandIndex].a1, this.filters[bandIndex].a2];
+  audio.addEventListener("MozAudioAvailable", this.loadSamples, false);
+  audio.addEventListener("loadedmetadata", this.loadMetaData, false);
+  audio.addEventListener("ended", this.loadComplete, false);
+  audio.muted = true;
+  audio.src = file;
+  audio.play();
+}
 
-    this.freqzs[bandIndex] = DSP.mag2db(DSP.freqz(b, a, this.w));
-  };
+Sampler.prototype.applyEnvelope = function() {
+  this.envelope.process(this.signal);
+  return this.signal;
+};
 
-  this.process = function(buffer) {
-    var output = buffer;
+Sampler.prototype.generate = function() {
+  var loopWidth = this.playEnd * this.samples.length - this.playStart * this.samples.length;
+  var playStartSamples = this.playStart * this.samples.length; // ie 0.5 -> 50% of the length
+  var playEndSamples = this.playEnd * this.samples.length; // ie 0.5 -> 50% of the length
 
-    for (var i = 0; i < this.filters.length; i++) {
-      output = this.filters[i].process(output);
-    }
+  for ( var i = 0; i < this.bufferSize; i++ ) {
+    switch (this.loopMode) {
+      case DSP.OFF:
+        this.playhead = Math.round(this.samplesProcessed * this.step + playStartSamples);
+        if (this.playhead < (this.playEnd * this.samples.length) ) {
+          this.signal[i] = this.samples[this.playhead] * this.amplitude;
+        } else {
+          this.signal[i] = 0;
+        }
+        break;
 
-    return output;
-  };
+      case DSP.FW:
+        this.playhead = Math.round((this.samplesProcessed * this.step) % loopWidth + playStartSamples);
+        if (this.playhead < (this.playEnd * this.samples.length) ) {
+          this.signal[i] = this.samples[this.playhead] * this.amplitude;
+        }
+        break;
 
-  this.processStereo = function(buffer) {
-    var output = buffer;
+      case DSP.BW:
+        this.playhead = playEndSamples - Math.round((this.samplesProcessed * this.step) % loopWidth);
+        if (this.playhead < (this.playEnd * this.samples.length) ) {
+          this.signal[i] = this.samples[this.playhead] * this.amplitude;
+        }
+        break;
 
-    for (var i = 0; i < this.filters.length; i++) {
-      output = this.filters[i].processStereo(output);
+      case DSP.FWBW:
+        if ( Math.floor(this.samplesProcessed * this.step / loopWidth) % 2 === 0 ) {
+          this.playhead = Math.round((this.samplesProcessed * this.step) % loopWidth + playStartSamples);
+        } else {
+          this.playhead = playEndSamples - Math.round((this.samplesProcessed * this.step) % loopWidth);
+        }
+        if (this.playhead < (this.playEnd * this.samples.length) ) {
+          this.signal[i] = this.samples[this.playhead] * this.amplitude;
+        }
+        break;
     }
-
-    return output;
-  };
-}
-
-/**
- * MultiDelay effect by Almer Thie (http://code.almeros.com).
- * Copyright 2010 Almer Thie. All rights reserved.
- * Example: http://code.almeros.com/code-examples/delay-firefox-audio-api/
- *
- * This is a delay that feeds it's own delayed signal back into its circular
- * buffer. Also known as a CombFilter.
- *
- * Compatible with interleaved stereo (or more channel) buffers and
- * non-interleaved mono buffers.
- *
- * @param {Number} maxDelayInSamplesSize Maximum possible delay in samples (size of circular buffer)
- * @param {Number} delayInSamples Initial delay in samples
- * @param {Number} masterVolume Initial master volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- * @param {Number} delayVolume Initial feedback delay volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- *
- * @constructor
- */
-function MultiDelay(maxDelayInSamplesSize, delayInSamples, masterVolume, delayVolume) {
-  this.delayBufferSamples   = new Float64Array(maxDelayInSamplesSize); // The maximum size of delay
-  this.delayInputPointer     = delayInSamples;
-  this.delayOutputPointer   = 0;
- 
-  this.delayInSamples   = delayInSamples;
-  this.masterVolume     = masterVolume;
-  this.delayVolume     = delayVolume;
-}
-
-/**
- * Change the delay time in samples.
- *
- * @param {Number} delayInSamples Delay in samples
- */
-MultiDelay.prototype.setDelayInSamples = function (delayInSamples) {
-  this.delayInSamples = delayInSamples;
- 
-  this.delayInputPointer = this.delayOutputPointer + delayInSamples;
-
-  if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
-    this.delayInputPointer = this.delayInputPointer - this.delayBufferSamples.length; 
+    this.samplesProcessed++;
   }
-};
 
-/**
- * Change the master volume.
- *
- * @param {Number} masterVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- */
-MultiDelay.prototype.setMasterVolume = function(masterVolume) {
-  this.masterVolume = masterVolume;
+  this.frameCount++;
+
+  return this.signal;
 };
 
-/**
- * Change the delay feedback volume.
- *
- * @param {Number} delayVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- */
-MultiDelay.prototype.setDelayVolume = function(delayVolume) {
-  this.delayVolume = delayVolume;
+Sampler.prototype.setFreq = function(frequency) {
+    var totalProcessed = this.samplesProcessed * this.step;
+    this.frequency = frequency;
+    this.step = this.frequency / this.rootFrequency;
+    this.samplesProcessed = Math.round(totalProcessed/this.step);
 };
 
-/**
- * Process a given interleaved or mono non-interleaved float value Array and adds the delayed audio.
- *
- * @param {Array} samples Array containing Float values or a Float64Array
- *
- * @returns A new Float64Array interleaved or mono non-interleaved as was fed to this function.
- */
-MultiDelay.prototype.process = function(samples) {
-  // NB. Make a copy to put in the output samples to return.
-  var outputSamples = new Float64Array(samples.length);
-
-  for (var i=0; i<samples.length; i++) {
-    // delayBufferSamples could contain initial NULL's, return silence in that case
-    var delaySample = (this.delayBufferSamples[this.delayOutputPointer] === null ? 0.0 : this.delayBufferSamples[this.delayOutputPointer]);
-   
-    // Mix normal audio data with delayed audio
-    var sample = (delaySample * this.delayVolume) + samples[i];
-   
-    // Add audio data with the delay in the delay buffer
-    this.delayBufferSamples[this.delayInputPointer] = sample;
-   
-    // Return the audio with delay mix
-    outputSamples[i] = sample * this.masterVolume;
-   
-    // Manage circulair delay buffer pointers
-    this.delayInputPointer++;
-    if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
-      this.delayInputPointer = 0;
-    }
-     
-    this.delayOutputPointer++;
-    if (this.delayOutputPointer >= this.delayBufferSamples.length-1) {
-      this.delayOutputPointer = 0; 
-    } 
-  }
- 
-  return outputSamples;
+Sampler.prototype.reset = function() {
+  this.samplesProcessed = 0;
+  this.playhead = 0;
 };
 
+module.exports = Sampler;
+
+},{"./dsp":4}],16:[function(require,module,exports){
+/* global Float64Array */
+
 /**
  * SingleDelay effect by Almer Thie (http://code.almeros.com).
  * Copyright 2010 Almer Thie. All rights reserved.
  * Example: See usage in Reverb class
  *
- * This is a delay that does NOT feeds it's own delayed signal back into its 
+ * This is a delay that does NOT feeds it's own delayed signal back into its
  * circular buffer, neither does it return the original signal. Also known as
  * an AllPassFilter(?).
  *
@@ -2060,12 +2371,11 @@ MultiDelay.prototype.process = function(samples) {
  *
  * @constructor
  */
-
 function SingleDelay(maxDelayInSamplesSize, delayInSamples, delayVolume) {
   this.delayBufferSamples = new Float64Array(maxDelayInSamplesSize); // The maximum size of delay
   this.delayInputPointer  = delayInSamples;
   this.delayOutputPointer = 0;
- 
+
   this.delayInSamples     = delayInSamples;
   this.delayVolume        = delayVolume;
 }
@@ -2080,7 +2390,7 @@ SingleDelay.prototype.setDelayInSamples = function(delayInSamples) {
   this.delayInputPointer = this.delayOutputPointer + delayInSamples;
 
   if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
-    this.delayInputPointer = this.delayInputPointer - this.delayBufferSamples.length; 
+    this.delayInputPointer = this.delayInputPointer - this.delayBufferSamples.length;
   }
 };
 
@@ -2109,7 +2419,7 @@ SingleDelay.prototype.process = function(samples) {
 
     // Add audio data with the delay in the delay buffer
     this.delayBufferSamples[this.delayInputPointer] = samples[i];
-   
+
     // delayBufferSamples could contain initial NULL's, return silence in that case
     var delaySample = this.delayBufferSamples[this.delayOutputPointer];
 
@@ -2122,201 +2432,152 @@ SingleDelay.prototype.process = function(samples) {
     if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
       this.delayInputPointer = 0;
     }
-     
+
     this.delayOutputPointer++;
 
     if (this.delayOutputPointer >= this.delayBufferSamples.length-1) {
-      this.delayOutputPointer = 0; 
-    } 
+      this.delayOutputPointer = 0;
+    }
   }
- 
+
   return outputSamples;
 };
 
+module.exports = SingleDelay;
+
+},{}],17:[function(require,module,exports){
 /**
- * Reverb effect by Almer Thie (http://code.almeros.com).
- * Copyright 2010 Almer Thie. All rights reserved.
- * Example: http://code.almeros.com/code-examples/reverb-firefox-audio-api/
+ * Returns the hyperbolic sine of the number
  *
- * This reverb consists of 6 SingleDelays, 6 MultiDelays and an IIRFilter2
- * for each of the two stereo channels.
- *
- * Compatible with interleaved stereo buffers only!
+ * @meta version: 1004.2314
+ * @meta discuss at: http://phpjs.org/functions/sinh
+ * @meta original by: Onno Marsman
  *
- * @param {Number} maxDelayInSamplesSize Maximum possible delay in samples (size of circular buffers)
- * @param {Number} delayInSamples Initial delay in samples for internal (Single/Multi)delays
- * @param {Number} masterVolume Initial master volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- * @param {Number} mixVolume Initial reverb signal mix volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- * @param {Number} delayVolume Initial feedback delay volume for internal (Single/Multi)delays. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- * @param {Number} dampFrequency Initial low pass filter frequency. 0 to 44100 (depending on your maximum sampling frequency)
+ * @param {Number} num
+ * @example
+ * sinh(-0.9834330348825909); // => -1.1497971402636502
+ */
+function sinh (arg) {
+  return (Math.exp(arg) - Math.exp(-arg))/2;
+}
+
+module.exports = sinh;
+
+},{}],18:[function(require,module,exports){
+var DSP = require("./dsp");
+
+/**
+ * WindowFunction
  *
  * @constructor
+ * @param {Number} type
+ * @param {Number} alpha
  */
-function Reverb(maxDelayInSamplesSize, delayInSamples, masterVolume, mixVolume, delayVolume, dampFrequency) {
-  this.delayInSamples   = delayInSamples;
-  this.masterVolume     = masterVolume;
-  this.mixVolume       = mixVolume;
-  this.delayVolume     = delayVolume;
-  this.dampFrequency     = dampFrequency;
- 
-  this.NR_OF_MULTIDELAYS = 6;
-  this.NR_OF_SINGLEDELAYS = 6;
- 
-  this.LOWPASSL = new IIRFilter2(DSP.LOWPASS, dampFrequency, 0, 44100);
-  this.LOWPASSR = new IIRFilter2(DSP.LOWPASS, dampFrequency, 0, 44100);
- 
-  this.singleDelays = [];
-  
-  var i, delayMultiply;
+function WindowFunction(type, alpha) {
+  this.alpha = alpha;
 
-  for (i = 0; i < this.NR_OF_SINGLEDELAYS; i++) {
-    delayMultiply = 1.0 + (i/7.0); // 1.0, 1.1, 1.2...
-    this.singleDelays[i] = new SingleDelay(maxDelayInSamplesSize, Math.round(this.delayInSamples * delayMultiply), this.delayVolume);
-  }
- 
-  this.multiDelays = [];
+  switch(type) {
+    case DSP.BARTLETT:
+      this.func = WindowFunction.Bartlett;
+      break;
 
-  for (i = 0; i < this.NR_OF_MULTIDELAYS; i++) {
-    delayMultiply = 1.0 + (i/10.0); // 1.0, 1.1, 1.2... 
-    this.multiDelays[i] = new MultiDelay(maxDelayInSamplesSize, Math.round(this.delayInSamples * delayMultiply), this.masterVolume, this.delayVolume);
+    case DSP.BARTLETTHANN:
+      this.func = WindowFunction.BartlettHann;
+      break;
+
+    case DSP.BLACKMAN:
+      this.func = WindowFunction.Blackman;
+      this.alpha = this.alpha || 0.16;
+      break;
+
+    case DSP.COSINE:
+      this.func = WindowFunction.Cosine;
+      break;
+
+    case DSP.GAUSS:
+      this.func = WindowFunction.Gauss;
+      this.alpha = this.alpha || 0.25;
+      break;
+
+    case DSP.HAMMING:
+      this.func = WindowFunction.Hamming;
+      break;
+
+    case DSP.HANN:
+      this.func = WindowFunction.Hann;
+      break;
+
+    case DSP.LANCZOS:
+      this.func = WindowFunction.Lanczoz;
+      break;
+
+    case DSP.RECTANGULAR:
+      this.func = WindowFunction.Rectangular;
+      break;
+
+    case DSP.TRIANGULAR:
+      this.func = WindowFunction.Triangular;
+      break;
   }
 }
 
 /**
- * Change the delay time in samples as a base for all delays.
- *
- * @param {Number} delayInSamples Delay in samples
+ * Process a buffer
+ * @param {Array} buffer
  */
-Reverb.prototype.setDelayInSamples = function (delayInSamples){
-  this.delayInSamples = delayInSamples;
-
-  var i, delayMultiply;
- 
-  for (i = 0; i < this.NR_OF_SINGLEDELAYS; i++) {
-    delayMultiply = 1.0 + (i/7.0); // 1.0, 1.1, 1.2...
-    this.singleDelays[i].setDelayInSamples( Math.round(this.delayInSamples * delayMultiply) );
-  }
-   
-  for (i = 0; i < this.NR_OF_MULTIDELAYS; i++) {
-    delayMultiply = 1.0 + (i/10.0); // 1.0, 1.1, 1.2...
-    this.multiDelays[i].setDelayInSamples( Math.round(this.delayInSamples * delayMultiply) );
+WindowFunction.prototype.process = function(buffer) {
+  var length = buffer.length;
+  for ( var i = 0; i < length; i++ ) {
+    buffer[i] *= this.func(length, i, this.alpha);
   }
+  return buffer;
 };
 
-/**
- * Change the master volume.
- *
- * @param {Number} masterVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- */
-Reverb.prototype.setMasterVolume = function (masterVolume){
-  this.masterVolume = masterVolume;
+WindowFunction.Bartlett = function(length, index) {
+  return 2 / (length - 1) * ((length - 1) / 2 - Math.abs(index - (length - 1) / 2));
 };
 
-/**
- * Change the reverb signal mix level.
- *
- * @param {Number} mixVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- */
-Reverb.prototype.setMixVolume = function (mixVolume){
-  this.mixVolume = mixVolume;
+WindowFunction.BartlettHann = function(length, index) {
+  return 0.62 - 0.48 * Math.abs(index / (length - 1) - 0.5) - 0.38 * Math.cos(DSP.TWO_PI * index / (length - 1));
 };
 
-/**
- * Change all delays feedback volume.
- *
- * @param {Number} delayVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- */
-Reverb.prototype.setDelayVolume = function (delayVolume){
-  this.delayVolume = delayVolume;
- 
-  var i;
+WindowFunction.Blackman = function(length, index, alpha) {
+  var a0 = (1 - alpha) / 2;
+  var a1 = 0.5;
+  var a2 = alpha / 2;
 
-  for (i = 0; i<this.NR_OF_SINGLEDELAYS; i++) {
-    this.singleDelays[i].setDelayVolume(this.delayVolume);
-  } 
- 
-  for (i = 0; i<this.NR_OF_MULTIDELAYS; i++) {
-    this.multiDelays[i].setDelayVolume(this.delayVolume);
-  } 
+  return a0 - a1 * Math.cos(DSP.TWO_PI * index / (length - 1)) + a2 * Math.cos(4 * Math.PI * index / (length - 1));
 };
 
-/**
- * Change the Low Pass filter frequency.
- *
- * @param {Number} dampFrequency low pass filter frequency. 0 to 44100 (depending on your maximum sampling frequency)
- */
-Reverb.prototype.setDampFrequency = function (dampFrequency){
-  this.dampFrequency = dampFrequency;
- 
-  this.LOWPASSL.set(dampFrequency, 0);
-  this.LOWPASSR.set(dampFrequency, 0); 
+WindowFunction.Cosine = function(length, index) {
+  return Math.cos(Math.PI * index / (length - 1) - Math.PI / 2);
 };
 
-/**
- * Process a given interleaved float value Array and copies and adds the reverb signal.
- *
- * @param {Array} samples Array containing Float values or a Float64Array
- *
- * @returns A new Float64Array interleaved buffer.
- */
-Reverb.prototype.process = function (interleavedSamples){ 
-  // NB. Make a copy to put in the output samples to return.
-  var outputSamples = new Float64Array(interleavedSamples.length);
- 
-  // Perform low pass on the input samples to mimick damp
-  var leftRightMix = DSP.deinterleave(interleavedSamples);
-  this.LOWPASSL.process( leftRightMix[DSP.LEFT] );
-  this.LOWPASSR.process( leftRightMix[DSP.RIGHT] ); 
-  var filteredSamples = DSP.interleave(leftRightMix[DSP.LEFT], leftRightMix[DSP.RIGHT]);
+WindowFunction.Gauss = function(length, index, alpha) {
+  return Math.pow(Math.E, -0.5 * Math.pow((index - (length - 1) / 2) / (alpha * (length - 1) / 2), 2));
+};
 
-  var i;
+WindowFunction.Hamming = function(length, index) {
+  return 0.54 - 0.46 * Math.cos(DSP.TWO_PI * index / (length - 1));
+};
 
-  // Process MultiDelays in parallel
-  for (i = 0; i<this.NR_OF_MULTIDELAYS; i++) {
-    // Invert the signal of every even multiDelay
-    outputSamples = DSP.mixSampleBuffers(outputSamples, this.multiDelays[i].process(filteredSamples), 2%i === 0, this.NR_OF_MULTIDELAYS);
-  }
- 
-  // Process SingleDelays in series
-  var singleDelaySamples = new Float64Array(outputSamples.length);
-  for (i = 0; i<this.NR_OF_SINGLEDELAYS; i++) {
-    // Invert the signal of every even singleDelay
-    singleDelaySamples = DSP.mixSampleBuffers(singleDelaySamples, this.singleDelays[i].process(outputSamples), 2%i === 0, 1);
-  }
+WindowFunction.Hann = function(length, index) {
+  return 0.5 * (1 - Math.cos(DSP.TWO_PI * index / (length - 1)));
+};
 
-  // Apply the volume of the reverb signal
-  for (i = 0; i<singleDelaySamples.length; i++) {
-    singleDelaySamples[i] *= this.mixVolume;
-  }
- 
-  // Mix the original signal with the reverb signal
-  outputSamples = DSP.mixSampleBuffers(singleDelaySamples, interleavedSamples, 0, 1);
+WindowFunction.Lanczos = function(length, index) {
+  var x = 2 * index / (length - 1) - 1;
+  return Math.sin(Math.PI * x) / (Math.PI * x);
+};
 
-  // Apply the master volume to the complete signal
-  for (i = 0; i<outputSamples.length; i++) {
-    outputSamples[i] *= this.masterVolume;
-  }
-   
-  return outputSamples;
+WindowFunction.Rectangular = function() {
+  return 1;
 };
 
-if (module && typeof module.exports !== 'undefined') {
-  module.exports = {
-    DSP: DSP,
-    DFT: DFT,
-    FFT: FFT,
-    RFFT: RFFT,
-    Sampler: Sampler,
-    Oscillator: Oscillator,
-    ADSR: ADSR,
-    IIRFilter: IIRFilter,
-    IIRFilter2: IIRFilter2,
-    WindowFunction: WindowFunction,
-    sinh: sinh,
-    Biquad: Biquad,
-    GraphicalEq: GraphicalEq,
-    MultiDelay: MultiDelay,
-    SingleDelay: SingleDelay,
-    Reverb: Reverb
-  };
-}
\ No newline at end of file
+WindowFunction.Triangular = function(length, index) {
+  return 2 / length * (length / 2 - Math.abs(index - (length - 1) / 2));
+};
+
+module.exports = WindowFunction;
+
+},{"./dsp":4}]},{},[10]);
diff --git a/dist/dsp.min.js b/dist/dsp.min.js
new file mode 100644
index 0000000..f8c950b
--- /dev/null
+++ b/dist/dsp.min.js
@@ -0,0 +1,3 @@
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){function ADSR(attackLength,decayLength,sustainLevel,sustainLength,releaseLength,sampleRate){this.sampleRate=sampleRate;this.attackLength=attackLength;this.decayLength=decayLength;this.sustainLevel=sustainLevel;this.sustainLength=sustainLength;this.releaseLength=releaseLength;this.sampleRate=sampleRate;this.attackSamples=attackLength*sampleRate;this.decaySamples=decayLength*sampleRate;this.sustainSamples=sustainLength*sampleRate;this.releaseSamples=releaseLength*sampleRate;this.update=function(){this.attack=this.attackSamples;this.decay=this.attack+this.decaySamples;this.sustain=this.decay+this.sustainSamples;this.release=this.sustain+this.releaseSamples};this.update();this.samplesProcessed=0}ADSR.prototype.noteOn=function(){this.samplesProcessed=0;this.sustainSamples=this.sustainLength*this.sampleRate;this.update()};ADSR.prototype.noteOff=function(){this.sustainSamples=this.samplesProcessed-this.decaySamples;this.update()};ADSR.prototype.processSample=function(sample){var amplitude=0;if(this.samplesProcessed<=this.attack){amplitude=0+(1-0)*((this.samplesProcessed-0)/(this.attack-0))}else if(this.samplesProcessed>this.attack&&this.samplesProcessed<=this.decay){amplitude=1+(this.sustainLevel-1)*((this.samplesProcessed-this.attack)/(this.decay-this.attack))}else if(this.samplesProcessed>this.decay&&this.samplesProcessed<=this.sustain){amplitude=this.sustainLevel}else if(this.samplesProcessed>this.sustain&&this.samplesProcessed<=this.release){amplitude=this.sustainLevel+(0-this.sustainLevel)*((this.samplesProcessed-this.sustain)/(this.release-this.sustain))}return sample*amplitude};ADSR.prototype.value=function(){var amplitude=0;if(this.samplesProcessed<=this.attack){amplitude=0+(1-0)*((this.samplesProcessed-0)/(this.attack-0))}else if(this.samplesProcessed>this.attack&&this.samplesProcessed<=this.decay){amplitude=1+(this.sustainLevel-1)*((this.samplesProcessed-this.attack)/(this.decay-this.attack))}else if(this.samplesProcessed>this.decay&&this.samplesProcessed<=this.sustain){amplitude=this.sustainLevel}else if(this.samplesProcessed>this.sustain&&this.samplesProcessed<=this.release){amplitude=this.sustainLevel+(0-this.sustainLevel)*((this.samplesProcessed-this.sustain)/(this.release-this.sustain))}return amplitude};ADSR.prototype.process=function(buffer){for(var i=0;i<buffer.length;i++){buffer[i]*=this.value();this.samplesProcessed++}return buffer};ADSR.prototype.isActive=function(){if(this.samplesProcessed>this.release||this.samplesProcessed===-1){return false}else{return true}};ADSR.prototype.disable=function(){this.samplesProcessed=-1};module.exports=ADSR},{}],2:[function(require,module,exports){var DSP=require("./dsp");var sinh=require("./sinh");function Biquad(type,sampleRate){this.Fs=sampleRate;this.type=type;this.parameterType=DSP.Q;this.x_1_l=0;this.x_2_l=0;this.y_1_l=0;this.y_2_l=0;this.x_1_r=0;this.x_2_r=0;this.y_1_r=0;this.y_2_r=0;this.b0=1;this.a0=1;this.b1=0;this.a1=0;this.b2=0;this.a2=0;this.b0a0=this.b0/this.a0;this.b1a0=this.b1/this.a0;this.b2a0=this.b2/this.a0;this.a1a0=this.a1/this.a0;this.a2a0=this.a2/this.a0;this.f0=3e3;this.dBgain=12;this.Q=1;this.BW=-3;this.S=1;this.coefficients=function(){var b=[this.b0,this.b1,this.b2];var a=[this.a0,this.a1,this.a2];return{b:b,a:a}};this.setFilterType=function(type){this.type=type;this.recalculateCoefficients()};this.setSampleRate=function(rate){this.Fs=rate;this.recalculateCoefficients()};this.setQ=function(q){this.parameterType=DSP.Q;this.Q=Math.max(Math.min(q,115),.001);this.recalculateCoefficients()};this.setBW=function(bw){this.parameterType=DSP.BW;this.BW=bw;this.recalculateCoefficients()};this.setS=function(s){this.parameterType=DSP.S;this.S=Math.max(Math.min(s,5),1e-4);this.recalculateCoefficients()};this.setF0=function(freq){this.f0=freq;this.recalculateCoefficients()};this.setDbGain=function(g){this.dBgain=g;this.recalculateCoefficients()};this.recalculateCoefficients=function(){var A;if(type===DSP.PEAKING_EQ||type===DSP.LOW_SHELF||type===DSP.HIGH_SHELF){A=Math.pow(10,this.dBgain/40)}else{A=Math.sqrt(Math.pow(10,this.dBgain/20))}var w0=DSP.TWO_PI*this.f0/this.Fs;var cosw0=Math.cos(w0);var sinw0=Math.sin(w0);var alpha=0;switch(this.parameterType){case DSP.Q:alpha=sinw0/(2*this.Q);break;case DSP.BW:alpha=sinw0*sinh(Math.LN2/2*this.BW*w0/sinw0);break;case DSP.S:alpha=sinw0/2*Math.sqrt((A+1/A)*(1/this.S-1)+2);break}var coeff;switch(this.type){case DSP.LPF:this.b0=(1-cosw0)/2;this.b1=1-cosw0;this.b2=(1-cosw0)/2;this.a0=1+alpha;this.a1=-2*cosw0;this.a2=1-alpha;break;case DSP.HPF:this.b0=(1+cosw0)/2;this.b1=-(1+cosw0);this.b2=(1+cosw0)/2;this.a0=1+alpha;this.a1=-2*cosw0;this.a2=1-alpha;break;case DSP.BPF_CONSTANT_SKIRT:this.b0=sinw0/2;this.b1=0;this.b2=-sinw0/2;this.a0=1+alpha;this.a1=-2*cosw0;this.a2=1-alpha;break;case DSP.BPF_CONSTANT_PEAK:this.b0=alpha;this.b1=0;this.b2=-alpha;this.a0=1+alpha;this.a1=-2*cosw0;this.a2=1-alpha;break;case DSP.NOTCH:this.b0=1;this.b1=-2*cosw0;this.b2=1;this.a0=1+alpha;this.a1=-2*cosw0;this.a2=1-alpha;break;case DSP.APF:this.b0=1-alpha;this.b1=-2*cosw0;this.b2=1+alpha;this.a0=1+alpha;this.a1=-2*cosw0;this.a2=1-alpha;break;case DSP.PEAKING_EQ:this.b0=1+alpha*A;this.b1=-2*cosw0;this.b2=1-alpha*A;this.a0=1+alpha/A;this.a1=-2*cosw0;this.a2=1-alpha/A;break;case DSP.LOW_SHELF:coeff=sinw0*Math.sqrt((A^2+1)*(1/this.S-1)+2*A);this.b0=A*(A+1-(A-1)*cosw0+coeff);this.b1=2*A*(A-1-(A+1)*cosw0);this.b2=A*(A+1-(A-1)*cosw0-coeff);this.a0=A+1+(A-1)*cosw0+coeff;this.a1=-2*(A-1+(A+1)*cosw0);this.a2=A+1+(A-1)*cosw0-coeff;break;case DSP.HIGH_SHELF:coeff=sinw0*Math.sqrt((A^2+1)*(1/this.S-1)+2*A);this.b0=A*(A+1+(A-1)*cosw0+coeff);this.b1=-2*A*(A-1+(A+1)*cosw0);this.b2=A*(A+1+(A-1)*cosw0-coeff);this.a0=A+1-(A-1)*cosw0+coeff;this.a1=2*(A-1-(A+1)*cosw0);this.a2=A+1-(A-1)*cosw0-coeff;break}this.b0a0=this.b0/this.a0;this.b1a0=this.b1/this.a0;this.b2a0=this.b2/this.a0;this.a1a0=this.a1/this.a0;this.a2a0=this.a2/this.a0};this.process=function(buffer){var len=buffer.length;var output=new Float64Array(len);for(var i=0;i<buffer.length;i++){output[i]=this.b0a0*buffer[i]+this.b1a0*this.x_1_l+this.b2a0*this.x_2_l-this.a1a0*this.y_1_l-this.a2a0*this.y_2_l;this.y_2_l=this.y_1_l;this.y_1_l=output[i];this.x_2_l=this.x_1_l;this.x_1_l=buffer[i]}return output};this.processStereo=function(buffer){var len=buffer.length;var output=new Float64Array(len);for(var i=0;i<len/2;i++){output[2*i]=this.b0a0*buffer[2*i]+this.b1a0*this.x_1_l+this.b2a0*this.x_2_l-this.a1a0*this.y_1_l-this.a2a0*this.y_2_l;this.y_2_l=this.y_1_l;this.y_1_l=output[2*i];this.x_2_l=this.x_1_l;this.x_1_l=buffer[2*i];output[2*i+1]=this.b0a0*buffer[2*i+1]+this.b1a0*this.x_1_r+this.b2a0*this.x_2_r-this.a1a0*this.y_1_r-this.a2a0*this.y_2_r;this.y_2_r=this.y_1_r;this.y_1_r=output[2*i+1];this.x_2_r=this.x_1_r;this.x_1_r=buffer[2*i+1]}return output}}module.exports=Biquad},{"./dsp":4,"./sinh":17}],3:[function(require,module,exports){var FourierTransform=require("./fourier");function DFT(bufferSize,sampleRate){FourierTransform.call(this,bufferSize,sampleRate);var N=bufferSize/2*bufferSize;var TWO_PI=2*Math.PI;this.sinTable=new Float64Array(N);this.cosTable=new Float64Array(N);for(var i=0;i<N;i++){this.sinTable[i]=Math.sin(i*TWO_PI/bufferSize);this.cosTable[i]=Math.cos(i*TWO_PI/bufferSize)}}DFT.prototype.forward=function(buffer){var real=this.real,imag=this.imag,rval,ival;for(var k=0;k<this.bufferSize/2;k++){rval=0;ival=0;for(var n=0;n<buffer.length;n++){rval+=this.cosTable[k*n]*buffer[n];ival+=this.sinTable[k*n]*buffer[n]}real[k]=rval;imag[k]=ival}return this.calculateSpectrum()};module.exports=DFT},{"./fourier":6}],4:[function(require,module,exports){var DSP={LEFT:0,RIGHT:1,MIX:2,SINE:1,TRIANGLE:2,SAW:3,SQUARE:4,LOWPASS:0,HIGHPASS:1,BANDPASS:2,NOTCH:3,BARTLETT:1,BARTLETTHANN:2,BLACKMAN:3,COSINE:4,GAUSS:5,HAMMING:6,HANN:7,LANCZOS:8,RECTANGULAR:9,TRIANGULAR:10,OFF:0,FW:1,BW:2,FWBW:3,TWO_PI:2*Math.PI};function setupTypedArray(name,fallback){if(typeof this[name]!=="function"&&typeof this[name]!=="object"){if(typeof this[fallback]==="function"&&typeof this[fallback]!=="object"){this[name]=this[fallback]}else{this[name]=function(obj){if(obj instanceof Array){return obj}else if(typeof obj==="number"){return new Array(obj)}}}}}setupTypedArray("Float64Array","WebGLFloatArray");setupTypedArray("Int32Array","WebGLIntArray");setupTypedArray("Uint16Array","WebGLUnsignedShortArray");setupTypedArray("Uint8Array","WebGLUnsignedByteArray");DSP.invert=function(buffer){for(var i=0,len=buffer.length;i<len;i++){buffer[i]*=-1}return buffer};DSP.interleave=function(left,right){if(left.length!==right.length){throw"Can not interleave. Channel lengths differ."}var stereoInterleaved=new Float64Array(left.length*2);for(var i=0,len=left.length;i<len;i++){stereoInterleaved[2*i]=left[i];stereoInterleaved[2*i+1]=right[i]}return stereoInterleaved};DSP.deinterleave=function(){var left,right,mix,deinterleaveChannel=[];deinterleaveChannel[DSP.MIX]=function(buffer){for(var i=0,len=buffer.length/2;i<len;i++){mix[i]=(buffer[2*i]+buffer[2*i+1])/2}return mix};deinterleaveChannel[DSP.LEFT]=function(buffer){for(var i=0,len=buffer.length/2;i<len;i++){left[i]=buffer[2*i]}return left};deinterleaveChannel[DSP.RIGHT]=function(buffer){for(var i=0,len=buffer.length/2;i<len;i++){right[i]=buffer[2*i+1]}return right};return function(channel,buffer){left=left||new Float64Array(buffer.length/2);right=right||new Float64Array(buffer.length/2);mix=mix||new Float64Array(buffer.length/2);if(buffer.length/2!==left.length){left=new Float64Array(buffer.length/2);right=new Float64Array(buffer.length/2);mix=new Float64Array(buffer.length/2)}return deinterleaveChannel[channel](buffer)}}();DSP.getChannel=DSP.deinterleave;DSP.mixSampleBuffers=function(sampleBuffer1,sampleBuffer2,negate,volumeCorrection){var outputSamples=new Float64Array(sampleBuffer1);for(var i=0;i<sampleBuffer1.length;i++){outputSamples[i]+=(negate?-sampleBuffer2[i]:sampleBuffer2[i])/volumeCorrection}return outputSamples};DSP.LPF=0;DSP.HPF=1;DSP.BPF_CONSTANT_SKIRT=2;DSP.BPF_CONSTANT_PEAK=3;DSP.NOTCH=4;DSP.APF=5;DSP.PEAKING_EQ=6;DSP.LOW_SHELF=7;DSP.HIGH_SHELF=8;DSP.Q=1;DSP.BW=2;DSP.S=3;DSP.RMS=function(buffer){var total=0;for(var i=0,n=buffer.length;i<n;i++){total+=buffer[i]*buffer[i]}return Math.sqrt(total/n)};DSP.Peak=function(buffer){var peak=0;for(var i=0,n=buffer.length;i<n;i++){peak=Math.abs(buffer[i])>peak?Math.abs(buffer[i]):peak}return peak};DSP.mag2db=function(buffer){var minDb=-120;var minMag=Math.pow(10,minDb/20);var log=Math.log;var max=Math.max;var result=new Float64Array(buffer.length);for(var i=0;i<buffer.length;i++){result[i]=20*log(max(buffer[i],minMag))}return result};DSP.freqz=function(b,a,w){var i,j;if(!w){w=new Float64Array(200);for(i=0;i<w.length;i++){w[i]=DSP.TWO_PI/w.length*i-Math.PI}}var result=new Float64Array(w.length);var sqrt=Math.sqrt;var cos=Math.cos;var sin=Math.sin;for(i=0;i<w.length;i++){var numerator={real:0,imag:0};for(j=0;j<b.length;j++){numerator.real+=b[j]*cos(-j*w[i]);numerator.imag+=b[j]*sin(-j*w[i])}var denominator={real:0,imag:0};for(j=0;j<a.length;j++){denominator.real+=a[j]*cos(-j*w[i]);denominator.imag+=a[j]*sin(-j*w[i])}result[i]=sqrt(numerator.real*numerator.real+numerator.imag*numerator.imag)/sqrt(denominator.real*denominator.real+denominator.imag*denominator.imag)}return result};module.exports=DSP},{}],5:[function(require,module,exports){var FourierTransform=require("./fourier");function FFT(bufferSize,sampleRate){FourierTransform.call(this,bufferSize,sampleRate);this.reverseTable=new Uint32Array(bufferSize);var limit=1;var bit=bufferSize>>1;var i;while(limit<bufferSize){for(i=0;i<limit;i++){this.reverseTable[i+limit]=this.reverseTable[i]+bit}limit=limit<<1;bit=bit>>1}this.sinTable=new Float64Array(bufferSize);this.cosTable=new Float64Array(bufferSize);for(i=0;i<bufferSize;i++){this.sinTable[i]=Math.sin(-Math.PI/i);this.cosTable[i]=Math.cos(-Math.PI/i)}}FFT.prototype.forward=function(buffer){var bufferSize=this.bufferSize,cosTable=this.cosTable,sinTable=this.sinTable,reverseTable=this.reverseTable,real=this.real,imag=this.imag;var k=Math.floor(Math.log(bufferSize)/Math.LN2);if(Math.pow(2,k)!==bufferSize){throw"Invalid buffer size, must be a power of 2."}if(bufferSize!==buffer.length){throw"Supplied buffer is not the same size as defined FFT. FFT Size: "+bufferSize+" Buffer Size: "+buffer.length}var halfSize=1,phaseShiftStepReal,phaseShiftStepImag,currentPhaseShiftReal,currentPhaseShiftImag,off,tr,ti,tmpReal,i;for(i=0;i<bufferSize;i++){real[i]=buffer[reverseTable[i]];imag[i]=0}while(halfSize<bufferSize){phaseShiftStepReal=cosTable[halfSize];phaseShiftStepImag=sinTable[halfSize];currentPhaseShiftReal=1;currentPhaseShiftImag=0;for(var fftStep=0;fftStep<halfSize;fftStep++){i=fftStep;while(i<bufferSize){off=i+halfSize;tr=currentPhaseShiftReal*real[off]-currentPhaseShiftImag*imag[off];ti=currentPhaseShiftReal*imag[off]+currentPhaseShiftImag*real[off];real[off]=real[i]-tr;imag[off]=imag[i]-ti;real[i]+=tr;imag[i]+=ti;i+=halfSize<<1}tmpReal=currentPhaseShiftReal;currentPhaseShiftReal=tmpReal*phaseShiftStepReal-currentPhaseShiftImag*phaseShiftStepImag;currentPhaseShiftImag=tmpReal*phaseShiftStepImag+currentPhaseShiftImag*phaseShiftStepReal}halfSize=halfSize<<1}return this.calculateSpectrum()};FFT.prototype.inverse=function(real,imag){var bufferSize=this.bufferSize,cosTable=this.cosTable,sinTable=this.sinTable,reverseTable=this.reverseTable;real=real||this.real;imag=imag||this.imag;var halfSize=1,phaseShiftStepReal,phaseShiftStepImag,currentPhaseShiftReal,currentPhaseShiftImag,off,tr,ti,tmpReal,i;for(i=0;i<bufferSize;i++){imag[i]*=-1}var revReal=new Float64Array(bufferSize);var revImag=new Float64Array(bufferSize);for(i=0;i<real.length;i++){revReal[i]=real[reverseTable[i]];revImag[i]=imag[reverseTable[i]]}real=revReal;imag=revImag;while(halfSize<bufferSize){phaseShiftStepReal=cosTable[halfSize];phaseShiftStepImag=sinTable[halfSize];currentPhaseShiftReal=1;currentPhaseShiftImag=0;for(var fftStep=0;fftStep<halfSize;fftStep++){i=fftStep;while(i<bufferSize){off=i+halfSize;tr=currentPhaseShiftReal*real[off]-currentPhaseShiftImag*imag[off];ti=currentPhaseShiftReal*imag[off]+currentPhaseShiftImag*real[off];real[off]=real[i]-tr;imag[off]=imag[i]-ti;real[i]+=tr;imag[i]+=ti;i+=halfSize<<1}tmpReal=currentPhaseShiftReal;currentPhaseShiftReal=tmpReal*phaseShiftStepReal-currentPhaseShiftImag*phaseShiftStepImag;currentPhaseShiftImag=tmpReal*phaseShiftStepImag+currentPhaseShiftImag*phaseShiftStepReal}halfSize=halfSize<<1}var buffer=new Float64Array(bufferSize);for(i=0;i<bufferSize;i++){buffer[i]=real[i]/bufferSize}return buffer};module.exports=FFT},{"./fourier":6}],6:[function(require,module,exports){module.exports=function FourierTransform(bufferSize,sampleRate){this.bufferSize=bufferSize;this.sampleRate=sampleRate;this.bandwidth=2/bufferSize*sampleRate/2;this.spectrum=new Float64Array(bufferSize/2);this.real=new Float64Array(bufferSize);this.imag=new Float64Array(bufferSize);this.peakBand=0;this.peak=0;this.getBandFrequency=function(index){return this.bandwidth*index+this.bandwidth/2};this.calculateSpectrum=function(){var spectrum=this.spectrum,real=this.real,imag=this.imag,bSi=2/this.bufferSize,sqrt=Math.sqrt,rval,ival,mag;for(var i=0,N=bufferSize/2;i<N;i++){rval=real[i];ival=imag[i];mag=bSi*sqrt(rval*rval+ival*ival);if(mag>this.peak){this.peakBand=i;this.peak=mag}spectrum[i]=mag}}}},{}],7:[function(require,module,exports){var DSP=require("./dsp");var Biquad=require("./biquad");function GraphicalEq(sampleRate){this.FS=sampleRate;this.minFreq=40;this.maxFreq=16e3;this.bandsPerOctave=1;this.filters=[];this.freqzs=[];this.calculateFreqzs=true;this.recalculateFilters=function(){var bandCount=Math.round(Math.log(this.maxFreq/this.minFreq)*this.bandsPerOctave/Math.LN2);this.filters=[];for(var i=0;i<bandCount;i++){var freq=this.minFreq*Math.pow(2,i/this.bandsPerOctave);var newFilter=new Biquad(DSP.PEAKING_EQ,this.FS);newFilter.setDbGain(0);newFilter.setBW(1/this.bandsPerOctave);newFilter.setF0(freq);this.filters[i]=newFilter;this.recalculateFreqz(i)}};this.setMinimumFrequency=function(freq){this.minFreq=freq;this.recalculateFilters()};this.setMaximumFrequency=function(freq){this.maxFreq=freq;this.recalculateFilters()};this.setBandsPerOctave=function(bands){this.bandsPerOctave=bands;this.recalculateFilters()};this.setBandGain=function(bandIndex,gain){if(bandIndex<0||bandIndex>this.filters.length-1){throw"The band index of the graphical equalizer is out of bounds."}if(!gain){throw"A gain must be passed."}this.filters[bandIndex].setDbGain(gain);this.recalculateFreqz(bandIndex)};this.recalculateFreqz=function(bandIndex){if(!this.calculateFreqzs){return}if(bandIndex<0||bandIndex>this.filters.length-1){throw"The band index of the graphical equalizer is out of bounds. "+bandIndex+" is out of ["+0+", "+this.filters.length-1+"]"}if(!this.w){this.w=new Float64Array(400);for(var i=0;i<this.w.length;i++){this.w[i]=Math.PI/this.w.length*i}}var b=[this.filters[bandIndex].b0,this.filters[bandIndex].b1,this.filters[bandIndex].b2];var a=[this.filters[bandIndex].a0,this.filters[bandIndex].a1,this.filters[bandIndex].a2];this.freqzs[bandIndex]=DSP.mag2db(DSP.freqz(b,a,this.w))};this.process=function(buffer){var output=buffer;for(var i=0;i<this.filters.length;i++){output=this.filters[i].process(output)}return output};this.processStereo=function(buffer){var output=buffer;for(var i=0;i<this.filters.length;i++){output=this.filters[i].processStereo(output)}return output}}module.exports=GraphicalEq},{"./biquad":2,"./dsp":4}],8:[function(require,module,exports){var DSP=require("./dsp");var ADSR=require("./adsr");function IIRFilter(type,cutoff,resonance,sampleRate){this.sampleRate=sampleRate;switch(type){case DSP.LOWPASS:case DSP.LP12:this.func=new IIRFilter.LP12(cutoff,resonance,sampleRate);break}}IIRFilter.prototype.__defineGetter__("cutoff",function(){return this.func.cutoff});IIRFilter.prototype.__defineGetter__("resonance",function(){return this.func.resonance});IIRFilter.prototype.set=function(cutoff,resonance){this.func.calcCoeff(cutoff,resonance)};IIRFilter.prototype.process=function(buffer){this.func.process(buffer)};IIRFilter.prototype.addEnvelope=function(envelope){if(envelope instanceof ADSR){this.func.addEnvelope(envelope)}else{throw"Not an envelope."}};IIRFilter.LP12=function(cutoff,resonance,sampleRate){this.sampleRate=sampleRate;this.vibraPos=0;this.vibraSpeed=0;this.envelope=false;this.calcCoeff=function(cutoff,resonance){this.w=2*Math.PI*cutoff/this.sampleRate;this.q=1-this.w/(2*(resonance+.5/(1+this.w))+this.w-2);this.r=this.q*this.q;this.c=this.r+1-2*Math.cos(this.w)*this.q;this.cutoff=cutoff;this.resonance=resonance};this.calcCoeff(cutoff,resonance);this.process=function(buffer){for(var i=0;i<buffer.length;i++){this.vibraSpeed+=(buffer[i]-this.vibraPos)*this.c;this.vibraPos+=this.vibraSpeed;this.vibraSpeed*=this.r;if(this.envelope){buffer[i]=buffer[i]*(1-this.envelope.value())+this.vibraPos*this.envelope.value();this.envelope.samplesProcessed++}else{buffer[i]=this.vibraPos}}}};IIRFilter.LP12.prototype.addEnvelope=function(envelope){this.envelope=envelope};module.exports=IIRFilter},{"./adsr":1,"./dsp":4}],9:[function(require,module,exports){var ADSR=require("./adsr");function IIRFilter2(type,cutoff,resonance,sampleRate){this.type=type;this.cutoff=cutoff;this.resonance=resonance;this.sampleRate=sampleRate;this.f=new Float64Array(4);this.f[0]=0;this.f[1]=0;this.f[2]=0;this.f[3]=0;this.calcCoeff=function(cutoff,resonance){this.freq=2*Math.sin(Math.PI*Math.min(.25,cutoff/(this.sampleRate*2)));this.damp=Math.min(2*(1-Math.pow(resonance,.25)),Math.min(2,2/this.freq-this.freq*.5))};this.calcCoeff(cutoff,resonance)}IIRFilter2.prototype.process=function(buffer){var input,output;var f=this.f;for(var i=0;i<buffer.length;i++){input=buffer[i];f[3]=input-this.damp*f[2];f[0]=f[0]+this.freq*f[2];f[1]=f[3]-f[0];f[2]=this.freq*f[1]+f[2];output=.5*f[this.type];f[3]=input-this.damp*f[2];f[0]=f[0]+this.freq*f[2];f[1]=f[3]-f[0];f[2]=this.freq*f[1]+f[2];output+=.5*f[this.type];if(this.envelope){buffer[i]=buffer[i]*(1-this.envelope.value())+output*this.envelope.value();this.envelope.samplesProcessed++}else{buffer[i]=output}}};IIRFilter2.prototype.addEnvelope=function(envelope){if(envelope instanceof ADSR){this.envelope=envelope}else{throw"This is not an envelope."}};IIRFilter2.prototype.set=function(cutoff,resonance){this.calcCoeff(cutoff,resonance)};module.exports=IIRFilter2},{"./adsr":1}],10:[function(require,module,exports){"use strict";var DSPJS={DSP:require("./dsp"),DFT:require("./dft"),FFT:require("./fft"),RFFT:require("./rfft"),Sampler:require("./sampler"),Oscillator:require("./oscillator"),ADSR:require("./adsr"),IIRFilter:require("./iir-filter"),IIRFilter2:require("./iir-filter2"),WindowFunction:require("./window-function"),sinh:require("./sinh"),Biquad:require("./biquad"),GraphicalEq:require("./graphical-eq"),MultiDelay:require("./multi-delay"),SingleDelay:require("./single-delay"),Reverb:require("./reverb")};if(typeof module==="object"&&module.exports)module.exports=DSPJS;if(typeof window!=="undefined"){Object.keys(DSPJS).forEach(function(k){window[k]=DSPJS[k]})}},{"./adsr":1,"./biquad":2,"./dft":3,"./dsp":4,"./fft":5,"./graphical-eq":7,"./iir-filter":8,"./iir-filter2":9,"./multi-delay":11,"./oscillator":12,"./reverb":13,"./rfft":14,"./sampler":15,"./single-delay":16,"./sinh":17,"./window-function":18}],11:[function(require,module,exports){function MultiDelay(maxDelayInSamplesSize,delayInSamples,masterVolume,delayVolume){this.delayBufferSamples=new Float64Array(maxDelayInSamplesSize);this.delayInputPointer=delayInSamples;this.delayOutputPointer=0;this.delayInSamples=delayInSamples;this.masterVolume=masterVolume;this.delayVolume=delayVolume}MultiDelay.prototype.setDelayInSamples=function(delayInSamples){this.delayInSamples=delayInSamples;this.delayInputPointer=this.delayOutputPointer+delayInSamples;if(this.delayInputPointer>=this.delayBufferSamples.length-1){this.delayInputPointer=this.delayInputPointer-this.delayBufferSamples.length}};MultiDelay.prototype.setMasterVolume=function(masterVolume){this.masterVolume=masterVolume};MultiDelay.prototype.setDelayVolume=function(delayVolume){this.delayVolume=delayVolume};MultiDelay.prototype.process=function(samples){var outputSamples=new Float64Array(samples.length);for(var i=0;i<samples.length;i++){var delaySample=this.delayBufferSamples[this.delayOutputPointer]===null?0:this.delayBufferSamples[this.delayOutputPointer];var sample=delaySample*this.delayVolume+samples[i];this.delayBufferSamples[this.delayInputPointer]=sample;outputSamples[i]=sample*this.masterVolume;this.delayInputPointer++;if(this.delayInputPointer>=this.delayBufferSamples.length-1){this.delayInputPointer=0}this.delayOutputPointer++;if(this.delayOutputPointer>=this.delayBufferSamples.length-1){this.delayOutputPointer=0}}return outputSamples};module.exports=MultiDelay},{}],12:[function(require,module,exports){var DSP=require("./dsp");function Oscillator(type,frequency,amplitude,bufferSize,sampleRate){this.frequency=frequency;this.amplitude=amplitude;this.bufferSize=bufferSize;this.sampleRate=sampleRate;this.frameCount=0;this.waveTableLength=2048;this.cyclesPerSample=frequency/sampleRate;this.signal=new Float64Array(bufferSize);this.envelope=null;switch(parseInt(type,10)){case DSP.TRIANGLE:this.func=Oscillator.Triangle;break;case DSP.SAW:this.func=Oscillator.Saw;break;case DSP.SQUARE:this.func=Oscillator.Square;break;default:case DSP.SINE:this.func=Oscillator.Sine;break}this.generateWaveTable=function(){Oscillator.waveTable[this.func]=new Float64Array(2048);var waveTableTime=this.waveTableLength/this.sampleRate;var waveTableHz=1/waveTableTime;for(var i=0;i<this.waveTableLength;i++){Oscillator.waveTable[this.func][i]=this.func(i*waveTableHz/this.sampleRate)}};if(typeof Oscillator.waveTable==="undefined"){Oscillator.waveTable={}}if(typeof Oscillator.waveTable[this.func]==="undefined"){this.generateWaveTable()}this.waveTable=Oscillator.waveTable[this.func]}Oscillator.prototype.setAmp=function(amplitude){if(amplitude>=0&&amplitude<=1){this.amplitude=amplitude}else{throw"Amplitude out of range (0..1)."}};Oscillator.prototype.setFreq=function(frequency){this.frequency=frequency;this.cyclesPerSample=frequency/this.sampleRate};Oscillator.prototype.add=function(oscillator){for(var i=0;i<this.bufferSize;i++){this.signal[i]+=oscillator.signal[i]}return this.signal};Oscillator.prototype.addSignal=function(signal){for(var i=0;i<signal.length;i++){if(i>=this.bufferSize){break}this.signal[i]+=signal[i]}return this.signal};Oscillator.prototype.addEnvelope=function(envelope){this.envelope=envelope};Oscillator.prototype.applyEnvelope=function(){this.envelope.process(this.signal)};Oscillator.prototype.valueAt=function(offset){return this.waveTable[offset%this.waveTableLength]};Oscillator.prototype.generate=function(){var frameOffset=this.frameCount*this.bufferSize;var step=this.waveTableLength*this.frequency/this.sampleRate;var offset;for(var i=0;i<this.bufferSize;i++){offset=Math.round((frameOffset+i)*step);this.signal[i]=this.waveTable[offset%this.waveTableLength]*this.amplitude}this.frameCount++;return this.signal};Oscillator.Sine=function(step){return Math.sin(DSP.TWO_PI*step)};Oscillator.Square=function(step){return step<.5?1:-1};Oscillator.Saw=function(step){return 2*(step-Math.round(step))};Oscillator.Triangle=function(step){return 1-4*Math.abs(Math.round(step)-step)};Oscillator.Pulse=function(){};module.exports=Oscillator},{"./dsp":4}],13:[function(require,module,exports){var DSP=require("./dsp");var IIRFilter2=require("./iir-filter2");var SingleDelay=require("./single-delay");var MultiDelay=require("./multi-delay");function Reverb(maxDelayInSamplesSize,delayInSamples,masterVolume,mixVolume,delayVolume,dampFrequency){this.delayInSamples=delayInSamples;this.masterVolume=masterVolume;this.mixVolume=mixVolume;this.delayVolume=delayVolume;this.dampFrequency=dampFrequency;this.NR_OF_MULTIDELAYS=6;this.NR_OF_SINGLEDELAYS=6;this.LOWPASSL=new IIRFilter2(DSP.LOWPASS,dampFrequency,0,44100);this.LOWPASSR=new IIRFilter2(DSP.LOWPASS,dampFrequency,0,44100);this.singleDelays=[];var i,delayMultiply;for(i=0;i<this.NR_OF_SINGLEDELAYS;i++){delayMultiply=1+i/7;this.singleDelays[i]=new SingleDelay(maxDelayInSamplesSize,Math.round(this.delayInSamples*delayMultiply),this.delayVolume)}this.multiDelays=[];for(i=0;i<this.NR_OF_MULTIDELAYS;i++){delayMultiply=1+i/10;this.multiDelays[i]=new MultiDelay(maxDelayInSamplesSize,Math.round(this.delayInSamples*delayMultiply),this.masterVolume,this.delayVolume)}}Reverb.prototype.setDelayInSamples=function(delayInSamples){this.delayInSamples=delayInSamples;var i,delayMultiply;for(i=0;i<this.NR_OF_SINGLEDELAYS;i++){delayMultiply=1+i/7;this.singleDelays[i].setDelayInSamples(Math.round(this.delayInSamples*delayMultiply))}for(i=0;i<this.NR_OF_MULTIDELAYS;i++){delayMultiply=1+i/10;this.multiDelays[i].setDelayInSamples(Math.round(this.delayInSamples*delayMultiply))}};Reverb.prototype.setMasterVolume=function(masterVolume){this.masterVolume=masterVolume};Reverb.prototype.setMixVolume=function(mixVolume){this.mixVolume=mixVolume};Reverb.prototype.setDelayVolume=function(delayVolume){this.delayVolume=delayVolume;var i;for(i=0;i<this.NR_OF_SINGLEDELAYS;i++){this.singleDelays[i].setDelayVolume(this.delayVolume)}for(i=0;i<this.NR_OF_MULTIDELAYS;i++){this.multiDelays[i].setDelayVolume(this.delayVolume)}};Reverb.prototype.setDampFrequency=function(dampFrequency){this.dampFrequency=dampFrequency;this.LOWPASSL.set(dampFrequency,0);this.LOWPASSR.set(dampFrequency,0)};Reverb.prototype.process=function(interleavedSamples){var outputSamples=new Float64Array(interleavedSamples.length);var leftRightMix=DSP.deinterleave(interleavedSamples);this.LOWPASSL.process(leftRightMix[DSP.LEFT]);this.LOWPASSR.process(leftRightMix[DSP.RIGHT]);var filteredSamples=DSP.interleave(leftRightMix[DSP.LEFT],leftRightMix[DSP.RIGHT]);var i;for(i=0;i<this.NR_OF_MULTIDELAYS;i++){outputSamples=DSP.mixSampleBuffers(outputSamples,this.multiDelays[i].process(filteredSamples),2%i===0,this.NR_OF_MULTIDELAYS)}var singleDelaySamples=new Float64Array(outputSamples.length);for(i=0;i<this.NR_OF_SINGLEDELAYS;i++){singleDelaySamples=DSP.mixSampleBuffers(singleDelaySamples,this.singleDelays[i].process(outputSamples),2%i===0,1)}for(i=0;i<singleDelaySamples.length;i++){singleDelaySamples[i]*=this.mixVolume}outputSamples=DSP.mixSampleBuffers(singleDelaySamples,interleavedSamples,0,1);for(i=0;i<outputSamples.length;i++){outputSamples[i]*=this.masterVolume}return outputSamples};module.exports=Reverb},{"./dsp":4,"./iir-filter2":9,"./multi-delay":11,"./single-delay":16}],14:[function(require,module,exports){var FourierTransform=require("./fourier");function RFFT(bufferSize,sampleRate){FourierTransform.call(this,bufferSize,sampleRate);this.trans=new Float64Array(bufferSize);this.reverseTable=new Uint32Array(bufferSize);this.reverseBinPermute=function(dest,source){var bufferSize=this.bufferSize,halfSize=bufferSize>>>1,nm1=bufferSize-1,i=1,r=0,h;dest[0]=source[0];do{r+=halfSize;dest[i]=source[r];dest[r]=source[i];i++;h=halfSize<<1;while(h=h>>1,!((r^=h)&h));if(r>=i){dest[i]=source[r];dest[r]=source[i];dest[nm1-i]=source[nm1-r];dest[nm1-r]=source[nm1-i]}i++}while(i<halfSize);dest[nm1]=source[nm1]};this.generateReverseTable=function(){var bufferSize=this.bufferSize,halfSize=bufferSize>>>1,nm1=bufferSize-1,i=1,r=0,h;this.reverseTable[0]=0;do{r+=halfSize;this.reverseTable[i]=r;this.reverseTable[r]=i;i++;h=halfSize<<1;while(h=h>>1,!((r^=h)&h));if(r>=i){this.reverseTable[i]=r;this.reverseTable[r]=i;this.reverseTable[nm1-i]=nm1-r;this.reverseTable[nm1-r]=nm1-i}i++}while(i<halfSize);this.reverseTable[nm1]=nm1};this.generateReverseTable()}RFFT.prototype.forward=function(buffer){var n=this.bufferSize,spectrum=this.spectrum,x=this.trans,TWO_PI=2*Math.PI,sqrt=Math.sqrt,i=n>>>1,bSi=2/n,n2,n4,n8,nn,t1,t2,t3,t4,i1,i2,i3,i4,i5,i6,i7,i8,st1,cc1,ss1,cc3,ss3,e,a,rval,ival,mag;this.reverseBinPermute(x,buffer);for(var ix=0,id=4;ix<n;id*=4){for(var i0=ix;i0<n;i0+=id){st1=x[i0]-x[i0+1];x[i0]+=x[i0+1];x[i0+1]=st1}ix=2*(id-1)}n2=2;nn=n>>>1;while(nn=nn>>>1){ix=0;n2=n2<<1;id=n2<<1;n4=n2>>>2;n8=n2>>>3;do{if(n4!==1){for(i0=ix;i0<n;i0+=id){i1=i0;i2=i1+n4;i3=i2+n4;i4=i3+n4;t1=x[i3]+x[i4];x[i4]-=x[i3];x[i3]=x[i1]-t1;x[i1]+=t1;i1+=n8;i2+=n8;i3+=n8;i4+=n8;t1=x[i3]+x[i4];t2=x[i3]-x[i4];t1=-t1*Math.SQRT1_2;t2*=Math.SQRT1_2;st1=x[i2];x[i4]=t1+st1;x[i3]=t1-st1;x[i2]=x[i1]-t2;x[i1]+=t2}}else{for(i0=ix;i0<n;i0+=id){i1=i0;i2=i1+n4;i3=i2+n4;i4=i3+n4;t1=x[i3]+x[i4];x[i4]-=x[i3];x[i3]=x[i1]-t1;x[i1]+=t1}}ix=(id<<1)-n2;id=id<<2}while(ix<n);e=TWO_PI/n2;for(var j=1;j<n8;j++){a=j*e;ss1=Math.sin(a);cc1=Math.cos(a);cc3=4*cc1*(cc1*cc1-.75);ss3=4*ss1*(.75-ss1*ss1);ix=0;id=n2<<1;do{for(i0=ix;i0<n;i0+=id){i1=i0+j;i2=i1+n4;i3=i2+n4;i4=i3+n4;i5=i0+n4-j;i6=i5+n4;i7=i6+n4;i8=i7+n4;t2=x[i7]*cc1-x[i3]*ss1;t1=x[i7]*ss1+x[i3]*cc1;t4=x[i8]*cc3-x[i4]*ss3;t3=x[i8]*ss3+x[i4]*cc3;st1=t2-t4;t2+=t4;t4=st1;x[i8]=t2+x[i6];x[i3]=t2-x[i6];st1=t3-t1;t1+=t3;t3=st1;x[i4]=t3+x[i2];x[i7]=t3-x[i2];x[i6]=x[i1]-t1;x[i1]+=t1;x[i2]=t4+x[i5];x[i5]-=t4}ix=(id<<1)-n2;id=id<<2}while(ix<n)}}while(--i){rval=x[i];ival=x[n-i-1];mag=bSi*sqrt(rval*rval+ival*ival);if(mag>this.peak){this.peakBand=i;this.peak=mag}spectrum[i]=mag}spectrum[0]=bSi*x[0];return spectrum};module.exports=RFFT},{"./fourier":6}],15:[function(require,module,exports){var DSP=require("./dsp");function Sampler(file,bufferSize,sampleRate,playStart,playEnd,loopStart,loopEnd,loopMode){this.file=file;this.bufferSize=bufferSize;this.sampleRate=sampleRate;this.playStart=playStart||0;this.playEnd=playEnd||1;
+
+this.loopStart=loopStart||0;this.loopEnd=loopEnd||1;this.loopMode=loopMode||DSP.OFF;this.loaded=false;this.samples=[];this.signal=new Float64Array(bufferSize);this.frameCount=0;this.envelope=null;this.amplitude=1;this.rootFrequency=110;this.frequency=550;this.step=this.frequency/this.rootFrequency;this.duration=0;this.samplesProcessed=0;this.playhead=0;var audio=document.createElement("AUDIO");var self=this;this.loadSamples=function(event){var buffer=DSP.getChannel(DSP.MIX,event.frameBuffer);for(var i=0;i<buffer.length;i++){self.samples.push(buffer[i])}};this.loadComplete=function(){self.samples=new Float64Array(self.samples);self.loaded=true};this.loadMetaData=function(){self.duration=audio.duration};audio.addEventListener("MozAudioAvailable",this.loadSamples,false);audio.addEventListener("loadedmetadata",this.loadMetaData,false);audio.addEventListener("ended",this.loadComplete,false);audio.muted=true;audio.src=file;audio.play()}Sampler.prototype.applyEnvelope=function(){this.envelope.process(this.signal);return this.signal};Sampler.prototype.generate=function(){var loopWidth=this.playEnd*this.samples.length-this.playStart*this.samples.length;var playStartSamples=this.playStart*this.samples.length;var playEndSamples=this.playEnd*this.samples.length;for(var i=0;i<this.bufferSize;i++){switch(this.loopMode){case DSP.OFF:this.playhead=Math.round(this.samplesProcessed*this.step+playStartSamples);if(this.playhead<this.playEnd*this.samples.length){this.signal[i]=this.samples[this.playhead]*this.amplitude}else{this.signal[i]=0}break;case DSP.FW:this.playhead=Math.round(this.samplesProcessed*this.step%loopWidth+playStartSamples);if(this.playhead<this.playEnd*this.samples.length){this.signal[i]=this.samples[this.playhead]*this.amplitude}break;case DSP.BW:this.playhead=playEndSamples-Math.round(this.samplesProcessed*this.step%loopWidth);if(this.playhead<this.playEnd*this.samples.length){this.signal[i]=this.samples[this.playhead]*this.amplitude}break;case DSP.FWBW:if(Math.floor(this.samplesProcessed*this.step/loopWidth)%2===0){this.playhead=Math.round(this.samplesProcessed*this.step%loopWidth+playStartSamples)}else{this.playhead=playEndSamples-Math.round(this.samplesProcessed*this.step%loopWidth)}if(this.playhead<this.playEnd*this.samples.length){this.signal[i]=this.samples[this.playhead]*this.amplitude}break}this.samplesProcessed++}this.frameCount++;return this.signal};Sampler.prototype.setFreq=function(frequency){var totalProcessed=this.samplesProcessed*this.step;this.frequency=frequency;this.step=this.frequency/this.rootFrequency;this.samplesProcessed=Math.round(totalProcessed/this.step)};Sampler.prototype.reset=function(){this.samplesProcessed=0;this.playhead=0};module.exports=Sampler},{"./dsp":4}],16:[function(require,module,exports){function SingleDelay(maxDelayInSamplesSize,delayInSamples,delayVolume){this.delayBufferSamples=new Float64Array(maxDelayInSamplesSize);this.delayInputPointer=delayInSamples;this.delayOutputPointer=0;this.delayInSamples=delayInSamples;this.delayVolume=delayVolume}SingleDelay.prototype.setDelayInSamples=function(delayInSamples){this.delayInSamples=delayInSamples;this.delayInputPointer=this.delayOutputPointer+delayInSamples;if(this.delayInputPointer>=this.delayBufferSamples.length-1){this.delayInputPointer=this.delayInputPointer-this.delayBufferSamples.length}};SingleDelay.prototype.setDelayVolume=function(delayVolume){this.delayVolume=delayVolume};SingleDelay.prototype.process=function(samples){var outputSamples=new Float64Array(samples.length);for(var i=0;i<samples.length;i++){this.delayBufferSamples[this.delayInputPointer]=samples[i];var delaySample=this.delayBufferSamples[this.delayOutputPointer];outputSamples[i]=delaySample*this.delayVolume;this.delayInputPointer++;if(this.delayInputPointer>=this.delayBufferSamples.length-1){this.delayInputPointer=0}this.delayOutputPointer++;if(this.delayOutputPointer>=this.delayBufferSamples.length-1){this.delayOutputPointer=0}}return outputSamples};module.exports=SingleDelay},{}],17:[function(require,module,exports){function sinh(arg){return(Math.exp(arg)-Math.exp(-arg))/2}module.exports=sinh},{}],18:[function(require,module,exports){var DSP=require("./dsp");function WindowFunction(type,alpha){this.alpha=alpha;switch(type){case DSP.BARTLETT:this.func=WindowFunction.Bartlett;break;case DSP.BARTLETTHANN:this.func=WindowFunction.BartlettHann;break;case DSP.BLACKMAN:this.func=WindowFunction.Blackman;this.alpha=this.alpha||.16;break;case DSP.COSINE:this.func=WindowFunction.Cosine;break;case DSP.GAUSS:this.func=WindowFunction.Gauss;this.alpha=this.alpha||.25;break;case DSP.HAMMING:this.func=WindowFunction.Hamming;break;case DSP.HANN:this.func=WindowFunction.Hann;break;case DSP.LANCZOS:this.func=WindowFunction.Lanczoz;break;case DSP.RECTANGULAR:this.func=WindowFunction.Rectangular;break;case DSP.TRIANGULAR:this.func=WindowFunction.Triangular;break}}WindowFunction.prototype.process=function(buffer){var length=buffer.length;for(var i=0;i<length;i++){buffer[i]*=this.func(length,i,this.alpha)}return buffer};WindowFunction.Bartlett=function(length,index){return 2/(length-1)*((length-1)/2-Math.abs(index-(length-1)/2))};WindowFunction.BartlettHann=function(length,index){return.62-.48*Math.abs(index/(length-1)-.5)-.38*Math.cos(DSP.TWO_PI*index/(length-1))};WindowFunction.Blackman=function(length,index,alpha){var a0=(1-alpha)/2;var a1=.5;var a2=alpha/2;return a0-a1*Math.cos(DSP.TWO_PI*index/(length-1))+a2*Math.cos(4*Math.PI*index/(length-1))};WindowFunction.Cosine=function(length,index){return Math.cos(Math.PI*index/(length-1)-Math.PI/2)};WindowFunction.Gauss=function(length,index,alpha){return Math.pow(Math.E,-.5*Math.pow((index-(length-1)/2)/(alpha*(length-1)/2),2))};WindowFunction.Hamming=function(length,index){return.54-.46*Math.cos(DSP.TWO_PI*index/(length-1))};WindowFunction.Hann=function(length,index){return.5*(1-Math.cos(DSP.TWO_PI*index/(length-1)))};WindowFunction.Lanczos=function(length,index){var x=2*index/(length-1)-1;return Math.sin(Math.PI*x)/(Math.PI*x)};WindowFunction.Rectangular=function(){return 1};WindowFunction.Triangular=function(length,index){return 2/length*(length/2-Math.abs(index-(length-1)/2))};module.exports=WindowFunction},{"./dsp":4}]},{},[10]);
\ No newline at end of file
diff --git a/docs/API.md b/docs/API.md
new file mode 100644
index 0000000..63f0940
--- /dev/null
+++ b/docs/API.md
@@ -0,0 +1,999 @@
+## Classes
+
+<dl>
+<dt><a href="#ADSR">ADSR</a></dt>
+<dd></dd>
+<dt><a href="#Biquad">Biquad</a></dt>
+<dd></dd>
+<dt><a href="#DFT">DFT</a></dt>
+<dd></dd>
+<dt><a href="#FFT">FFT</a></dt>
+<dd></dd>
+<dt><a href="#GraphicalEq">GraphicalEq</a></dt>
+<dd></dd>
+<dt><a href="#IIRFilter">IIRFilter</a></dt>
+<dd></dd>
+<dt><a href="#IIRFilter2">IIRFilter2</a></dt>
+<dd></dd>
+<dt><a href="#MultiDelay">MultiDelay</a></dt>
+<dd></dd>
+<dt><a href="#Oscillator">Oscillator</a></dt>
+<dd></dd>
+<dt><a href="#Reverb">Reverb</a></dt>
+<dd></dd>
+<dt><a href="#RFFT">RFFT</a></dt>
+<dd></dd>
+<dt><a href="#Sampler">Sampler</a></dt>
+<dd></dd>
+<dt><a href="#SingleDelay">SingleDelay</a></dt>
+<dd></dd>
+<dt><a href="#WindowFunction">WindowFunction</a></dt>
+<dd></dd>
+</dl>
+
+## Members
+
+<dl>
+<dt><a href="#DSP">DSP</a></dt>
+<dd><p>DSP is an object which contains general purpose utility functions and constants</p>
+</dd>
+</dl>
+
+## Functions
+
+<dl>
+<dt><a href="#sinh">sinh(num)</a></dt>
+<dd><p>Returns the hyperbolic sine of the number</p>
+</dd>
+</dl>
+
+<a name="ADSR"></a>
+
+## ADSR
+**Kind**: global class  
+
+* [ADSR](#ADSR)
+    * [new ADSR(attack, decay, sustain, release, sampleRate)](#new_ADSR_new)
+    * [.noteOn()](#ADSR+noteOn)
+    * [.noteOff()](#ADSR+noteOff)
+    * [.processSample()](#ADSR+processSample)
+    * [.value()](#ADSR+value) ⇒ <code>Number</code>
+    * [.process(buffer)](#ADSR+process)
+    * [.isActive()](#ADSR+isActive) ⇒ <code>Boolean</code>
+    * [.disable()](#ADSR+disable)
+
+<a name="new_ADSR_new"></a>
+
+### new ADSR(attack, decay, sustain, release, sampleRate)
+ADSR Envelope
+
+
+| Param | Type | Description |
+| --- | --- | --- |
+| attack | <code>Number</code> | The attack length in seconds |
+| decay | <code>Number</code> | The decay length in seconds |
+| sustain | <code>Number</code> | The sustain level |
+| release | <code>Number</code> | The release length in seconds |
+| sampleRate | <code>Number</code> | The the sample rate |
+
+<a name="ADSR+noteOn"></a>
+
+### adsR.noteOn()
+Start the envelope
+
+**Kind**: instance method of <code>[ADSR](#ADSR)</code>  
+<a name="ADSR+noteOff"></a>
+
+### adsR.noteOff()
+Stop the envelope
+
+Send a note off when using a sustain of infinity to let the envelope enter
+the release phase
+
+**Kind**: instance method of <code>[ADSR](#ADSR)</code>  
+<a name="ADSR+processSample"></a>
+
+### adsR.processSample()
+Process sample
+
+**Kind**: instance method of <code>[ADSR](#ADSR)</code>  
+<a name="ADSR+value"></a>
+
+### adsR.value() ⇒ <code>Number</code>
+Get current value
+
+**Kind**: instance method of <code>[ADSR](#ADSR)</code>  
+**Returns**: <code>Number</code> - amplitude  
+<a name="ADSR+process"></a>
+
+### adsR.process(buffer)
+Process a buffer
+
+**Kind**: instance method of <code>[ADSR](#ADSR)</code>  
+
+| Param | Type |
+| --- | --- |
+| buffer | <code>Array</code> | 
+
+<a name="ADSR+isActive"></a>
+
+### adsR.isActive() ⇒ <code>Boolean</code>
+Test if the envelope is active
+
+**Kind**: instance method of <code>[ADSR](#ADSR)</code>  
+<a name="ADSR+disable"></a>
+
+### adsR.disable()
+Disable the envelope
+
+**Kind**: instance method of <code>[ADSR](#ADSR)</code>  
+<a name="Biquad"></a>
+
+## Biquad
+**Kind**: global class  
+<a name="new_Biquad_new"></a>
+
+### new Biquad(type, sampleRate)
+Biquad filter
+
+Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
+ Copyright 2010 Ricard Marxer. All rights reserved.
+
+Implementation based on:
+http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
+
+
+| Param | Type |
+| --- | --- |
+| type | <code>Number</code> | 
+| sampleRate | <code>Number</code> | 
+
+<a name="DFT"></a>
+
+## DFT
+**Kind**: global class  
+
+* [DFT](#DFT)
+    * [new DFT(bufferSize, sampleRate)](#new_DFT_new)
+    * [.forward(buffer)](#DFT+forward) ⇒
+
+<a name="new_DFT_new"></a>
+
+### new DFT(bufferSize, sampleRate)
+DFT is a class for calculating the Discrete Fourier Transform of a signal.
+
+
+| Param | Type | Description |
+| --- | --- | --- |
+| bufferSize | <code>Number</code> | The size of the sample buffer to be computed |
+| sampleRate | <code>Number</code> | The sampleRate of the buffer (eg. 44100) |
+
+<a name="DFT+forward"></a>
+
+### dfT.forward(buffer) ⇒
+Performs a forward transform on the sample buffer.
+Converts a time domain signal to frequency domain spectra.
+
+**Kind**: instance method of <code>[DFT](#DFT)</code>  
+**Returns**: The frequency spectrum array  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| buffer | <code>Array</code> | The sample buffer |
+
+<a name="FFT"></a>
+
+## FFT
+**Kind**: global class  
+
+* [FFT](#FFT)
+    * [new FFT(bufferSize, sampleRate)](#new_FFT_new)
+    * [.forward(buffer)](#FFT+forward) ⇒
+    * [.inverse(real, imag)](#FFT+inverse) ⇒
+
+<a name="new_FFT_new"></a>
+
+### new FFT(bufferSize, sampleRate)
+FFT is a class for calculating the Discrete Fourier Transform of a signal
+with the Fast Fourier Transform algorithm.
+
+
+| Param | Type | Description |
+| --- | --- | --- |
+| bufferSize | <code>Number</code> | The size of the sample buffer to be computed. Must be power of 2 |
+| sampleRate | <code>Number</code> | The sampleRate of the buffer (eg. 44100) |
+
+<a name="FFT+forward"></a>
+
+### ffT.forward(buffer) ⇒
+Performs a forward transform on the sample buffer.
+Converts a time domain signal to frequency domain spectra.
+
+**Kind**: instance method of <code>[FFT](#FFT)</code>  
+**Returns**: The frequency spectrum array  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| buffer | <code>Array</code> | The sample buffer. Buffer Length must be power of 2 |
+
+<a name="FFT+inverse"></a>
+
+### ffT.inverse(real, imag) ⇒
+Performs a inverse FFT transformation
+Converts a frequency domain spectra to a time domain signal
+
+**Kind**: instance method of <code>[FFT](#FFT)</code>  
+**Returns**: The time domain signal  
+
+| Param | Type |
+| --- | --- |
+| real | <code>Array</code> | 
+| imag | <code>Array</code> | 
+
+<a name="GraphicalEq"></a>
+
+## GraphicalEq
+**Kind**: global class  
+<a name="new_GraphicalEq_new"></a>
+
+### new GraphicalEq(sampleRate)
+Create a Graphical Equalizer
+
+ Implementation of a graphic equalizer with a configurable bands-per-octave
+ and minimum and maximum frequencies
+
+ Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
+ Copyright 2010 Ricard Marxer. All rights reserved.
+
+
+| Param | Type |
+| --- | --- |
+| sampleRate | <code>SampleRate</code> | 
+
+**Example**  
+```js
+var eq = new GraphicalEq(44100)
+```
+<a name="IIRFilter"></a>
+
+## IIRFilter
+**Kind**: global class  
+
+* [IIRFilter](#IIRFilter)
+    * [new IIRFilter()](#new_IIRFilter_new)
+    * _instance_
+        * [.set(cutoff, resonance)](#IIRFilter+set)
+        * [.process(buffer)](#IIRFilter+process)
+        * [.addEnvelope(envelope)](#IIRFilter+addEnvelope)
+    * _static_
+        * [.LP12](#IIRFilter.LP12)
+            * [new IIRFilter.LP12()](#new_IIRFilter.LP12_new)
+            * [.addEnvelope(envelope)](#IIRFilter.LP12+addEnvelope)
+
+<a name="new_IIRFilter_new"></a>
+
+### new IIRFilter()
+IIRFilter
+
+<a name="IIRFilter+set"></a>
+
+### iirFilter.set(cutoff, resonance)
+Set filter parameters
+
+**Kind**: instance method of <code>[IIRFilter](#IIRFilter)</code>  
+
+| Param | Type |
+| --- | --- |
+| cutoff | <code>Number</code> | 
+| resonance | <code>Number</code> | 
+
+<a name="IIRFilter+process"></a>
+
+### iirFilter.process(buffer)
+Process a buffer
+
+**Kind**: instance method of <code>[IIRFilter](#IIRFilter)</code>  
+
+| Param | Type |
+| --- | --- |
+| buffer | <code>Array</code> | 
+
+<a name="IIRFilter+addEnvelope"></a>
+
+### iirFilter.addEnvelope(envelope)
+Add an envelope to the filter
+
+**Kind**: instance method of <code>[IIRFilter](#IIRFilter)</code>  
+
+| Param | Type |
+| --- | --- |
+| envelope | <code>[ADSR](#ADSR)</code> | 
+
+<a name="IIRFilter.LP12"></a>
+
+### IIRFilter.LP12
+**Kind**: static class of <code>[IIRFilter](#IIRFilter)</code>  
+
+* [.LP12](#IIRFilter.LP12)
+    * [new IIRFilter.LP12()](#new_IIRFilter.LP12_new)
+    * [.addEnvelope(envelope)](#IIRFilter.LP12+addEnvelope)
+
+<a name="new_IIRFilter.LP12_new"></a>
+
+#### new IIRFilter.LP12()
+LP12 filter
+
+<a name="IIRFilter.LP12+addEnvelope"></a>
+
+#### lP12.addEnvelope(envelope)
+Add an envelope to the filter
+
+**Kind**: instance method of <code>[LP12](#IIRFilter.LP12)</code>  
+
+| Param | Type |
+| --- | --- |
+| envelope | <code>[ADSR](#ADSR)</code> | 
+
+<a name="IIRFilter2"></a>
+
+## IIRFilter2
+**Kind**: global class  
+
+* [IIRFilter2](#IIRFilter2)
+    * [new IIRFilter2(type, cutoff, resonance, sampleRate)](#new_IIRFilter2_new)
+    * [.process(buffer)](#IIRFilter2+process)
+    * [.addEnvelope(envelope)](#IIRFilter2+addEnvelope)
+    * [.set(cutoff, resonance)](#IIRFilter2+set)
+
+<a name="new_IIRFilter2_new"></a>
+
+### new IIRFilter2(type, cutoff, resonance, sampleRate)
+IIRFilter2
+
+
+| Param | Type |
+| --- | --- |
+| type | <code>Number</code> | 
+| cutoff | <code>Number</code> | 
+| resonance | <code>Number</code> | 
+| sampleRate | <code>Number</code> | 
+
+<a name="IIRFilter2+process"></a>
+
+### iirFilter2.process(buffer)
+Process a buffer
+
+**Kind**: instance method of <code>[IIRFilter2](#IIRFilter2)</code>  
+
+| Param | Type |
+| --- | --- |
+| buffer | <code>Array</code> | 
+
+<a name="IIRFilter2+addEnvelope"></a>
+
+### iirFilter2.addEnvelope(envelope)
+Add an envelope to the filter
+
+**Kind**: instance method of <code>[IIRFilter2](#IIRFilter2)</code>  
+
+| Param | Type |
+| --- | --- |
+| envelope | <code>[ADSR](#ADSR)</code> | 
+
+<a name="IIRFilter2+set"></a>
+
+### iirFilter2.set(cutoff, resonance)
+Set filter parameters
+
+**Kind**: instance method of <code>[IIRFilter2](#IIRFilter2)</code>  
+
+| Param | Type |
+| --- | --- |
+| cutoff | <code>Number</code> | 
+| resonance | <code>Number</code> | 
+
+<a name="MultiDelay"></a>
+
+## MultiDelay
+**Kind**: global class  
+
+* [MultiDelay](#MultiDelay)
+    * [new MultiDelay(maxDelayInSamplesSize, delayInSamples, masterVolume, delayVolume)](#new_MultiDelay_new)
+    * [.setDelayInSamples(delayInSamples)](#MultiDelay+setDelayInSamples)
+    * [.setMasterVolume(masterVolume)](#MultiDelay+setMasterVolume)
+    * [.setDelayVolume(delayVolume)](#MultiDelay+setDelayVolume)
+    * [.process(samples)](#MultiDelay+process) ⇒
+
+<a name="new_MultiDelay_new"></a>
+
+### new MultiDelay(maxDelayInSamplesSize, delayInSamples, masterVolume, delayVolume)
+MultiDelay effect by Almer Thie (http://code.almeros.com).
+Copyright 2010 Almer Thie. All rights reserved.
+Example: http://code.almeros.com/code-examples/delay-firefox-audio-api/
+
+This is a delay that feeds it's own delayed signal back into its circular
+buffer. Also known as a CombFilter.
+
+Compatible with interleaved stereo (or more channel) buffers and
+non-interleaved mono buffers.
+
+
+| Param | Type | Description |
+| --- | --- | --- |
+| maxDelayInSamplesSize | <code>Number</code> | Maximum possible delay in samples (size of circular buffer) |
+| delayInSamples | <code>Number</code> | Initial delay in samples |
+| masterVolume | <code>Number</code> | Initial master volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify) |
+| delayVolume | <code>Number</code> | Initial feedback delay volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify) |
+
+<a name="MultiDelay+setDelayInSamples"></a>
+
+### multiDelay.setDelayInSamples(delayInSamples)
+Change the delay time in samples.
+
+**Kind**: instance method of <code>[MultiDelay](#MultiDelay)</code>  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| delayInSamples | <code>Number</code> | Delay in samples |
+
+<a name="MultiDelay+setMasterVolume"></a>
+
+### multiDelay.setMasterVolume(masterVolume)
+Change the master volume.
+
+**Kind**: instance method of <code>[MultiDelay](#MultiDelay)</code>  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| masterVolume | <code>Number</code> | Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify) |
+
+<a name="MultiDelay+setDelayVolume"></a>
+
+### multiDelay.setDelayVolume(delayVolume)
+Change the delay feedback volume.
+
+**Kind**: instance method of <code>[MultiDelay](#MultiDelay)</code>  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| delayVolume | <code>Number</code> | Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify) |
+
+<a name="MultiDelay+process"></a>
+
+### multiDelay.process(samples) ⇒
+Process a given interleaved or mono non-interleaved float value Array and adds the delayed audio.
+
+**Kind**: instance method of <code>[MultiDelay](#MultiDelay)</code>  
+**Returns**: A new Float64Array interleaved or mono non-interleaved as was fed to this function.  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| samples | <code>Array</code> | Array containing Float values or a Float64Array |
+
+<a name="Oscillator"></a>
+
+## Oscillator
+**Kind**: global class  
+
+* [Oscillator](#Oscillator)
+    * [new Oscillator(type, frequency, amplitude, bufferSize, sampleRate)](#new_Oscillator_new)
+    * [.setAmp(amplitude)](#Oscillator+setAmp)
+    * [.setFreq(frequency)](#Oscillator+setFreq)
+    * [.add(oscillator)](#Oscillator+add) ⇒ <code>Array</code>
+    * [.addSignal(signal)](#Oscillator+addSignal)
+    * [.addEnvelope(envelope)](#Oscillator+addEnvelope)
+    * [.applyEnvelope()](#Oscillator+applyEnvelope)
+    * [.valueAt(offset)](#Oscillator+valueAt)
+    * [.generate()](#Oscillator+generate) ⇒ <code>Array</code>
+
+<a name="new_Oscillator_new"></a>
+
+### new Oscillator(type, frequency, amplitude, bufferSize, sampleRate)
+Oscillator class for generating and modifying signals
+
+
+| Param | Type | Description |
+| --- | --- | --- |
+| type | <code>Number</code> | A waveform constant (eg. DSP.SINE) |
+| frequency | <code>Number</code> | Initial frequency of the signal |
+| amplitude | <code>Number</code> | Initial amplitude of the signal |
+| bufferSize | <code>Number</code> | Size of the sample buffer to generate |
+| sampleRate | <code>Number</code> | The sample rate of the signal |
+
+<a name="Oscillator+setAmp"></a>
+
+### oscillator.setAmp(amplitude)
+Set the amplitude of the signal
+
+**Kind**: instance method of <code>[Oscillator](#Oscillator)</code>  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| amplitude | <code>Number</code> | The amplitude of the signal (between 0 and 1) |
+
+<a name="Oscillator+setFreq"></a>
+
+### oscillator.setFreq(frequency)
+Set the frequency of the signal
+
+**Kind**: instance method of <code>[Oscillator](#Oscillator)</code>  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| frequency | <code>Number</code> | The frequency of the signal |
+
+<a name="Oscillator+add"></a>
+
+### oscillator.add(oscillator) ⇒ <code>Array</code>
+Add an oscillator
+
+**Kind**: instance method of <code>[Oscillator](#Oscillator)</code>  
+**Returns**: <code>Array</code> - the current oscillator signal  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| oscillator | <code>[Oscillator](#Oscillator)</code> | The oscillator to be added to |
+
+<a name="Oscillator+addSignal"></a>
+
+### oscillator.addSignal(signal)
+Add a signal to the current generated osc signal
+
+**Kind**: instance method of <code>[Oscillator](#Oscillator)</code>  
+
+| Param | Type |
+| --- | --- |
+| signal | <code>Array</code> | 
+
+<a name="Oscillator+addEnvelope"></a>
+
+### oscillator.addEnvelope(envelope)
+Add an envelope to the oscillator
+
+**Kind**: instance method of <code>[Oscillator](#Oscillator)</code>  
+
+| Param | Type |
+| --- | --- |
+| envelope | <code>[ADSR](#ADSR)</code> | 
+
+<a name="Oscillator+applyEnvelope"></a>
+
+### oscillator.applyEnvelope()
+Apply the oscillator envelope to its signal
+
+**Kind**: instance method of <code>[Oscillator](#Oscillator)</code>  
+<a name="Oscillator+valueAt"></a>
+
+### oscillator.valueAt(offset)
+Get value
+
+**Kind**: instance method of <code>[Oscillator](#Oscillator)</code>  
+
+| Param | Type |
+| --- | --- |
+| offset | <code>Number</code> | 
+
+<a name="Oscillator+generate"></a>
+
+### oscillator.generate() ⇒ <code>Array</code>
+Generate the oscillator signal
+
+**Kind**: instance method of <code>[Oscillator](#Oscillator)</code>  
+**Returns**: <code>Array</code> - the signal  
+<a name="Reverb"></a>
+
+## Reverb
+**Kind**: global class  
+
+* [Reverb](#Reverb)
+    * [new Reverb(maxDelayInSamplesSize, delayInSamples, masterVolume, mixVolume, delayVolume, dampFrequency)](#new_Reverb_new)
+    * [.setDelayInSamples(delayInSamples)](#Reverb+setDelayInSamples)
+    * [.setMasterVolume(masterVolume)](#Reverb+setMasterVolume)
+    * [.setMixVolume(mixVolume)](#Reverb+setMixVolume)
+    * [.setDelayVolume(delayVolume)](#Reverb+setDelayVolume)
+    * [.setDampFrequency(dampFrequency)](#Reverb+setDampFrequency)
+    * [.process(samples)](#Reverb+process) ⇒
+
+<a name="new_Reverb_new"></a>
+
+### new Reverb(maxDelayInSamplesSize, delayInSamples, masterVolume, mixVolume, delayVolume, dampFrequency)
+Reverb effect by Almer Thie (http://code.almeros.com).
+Copyright 2010 Almer Thie. All rights reserved.
+Example: http://code.almeros.com/code-examples/reverb-firefox-audio-api/
+
+This reverb consists of 6 SingleDelays, 6 MultiDelays and an IIRFilter2
+for each of the two stereo channels.
+
+Compatible with interleaved stereo buffers only!
+
+
+| Param | Type | Description |
+| --- | --- | --- |
+| maxDelayInSamplesSize | <code>Number</code> | Maximum possible delay in samples (size of circular buffers) |
+| delayInSamples | <code>Number</code> | Initial delay in samples for internal (Single/Multi)delays |
+| masterVolume | <code>Number</code> | Initial master volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify) |
+| mixVolume | <code>Number</code> | Initial reverb signal mix volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify) |
+| delayVolume | <code>Number</code> | Initial feedback delay volume for internal (Single/Multi)delays. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify) |
+| dampFrequency | <code>Number</code> | Initial low pass filter frequency. 0 to 44100 (depending on your maximum sampling frequency) |
+
+<a name="Reverb+setDelayInSamples"></a>
+
+### reverb.setDelayInSamples(delayInSamples)
+Change the delay time in samples as a base for all delays.
+
+**Kind**: instance method of <code>[Reverb](#Reverb)</code>  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| delayInSamples | <code>Number</code> | Delay in samples |
+
+<a name="Reverb+setMasterVolume"></a>
+
+### reverb.setMasterVolume(masterVolume)
+Change the master volume.
+
+**Kind**: instance method of <code>[Reverb](#Reverb)</code>  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| masterVolume | <code>Number</code> | Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify) |
+
+<a name="Reverb+setMixVolume"></a>
+
+### reverb.setMixVolume(mixVolume)
+Change the reverb signal mix level.
+
+**Kind**: instance method of <code>[Reverb](#Reverb)</code>  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| mixVolume | <code>Number</code> | Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify) |
+
+<a name="Reverb+setDelayVolume"></a>
+
+### reverb.setDelayVolume(delayVolume)
+Change all delays feedback volume.
+
+**Kind**: instance method of <code>[Reverb](#Reverb)</code>  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| delayVolume | <code>Number</code> | Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify) |
+
+<a name="Reverb+setDampFrequency"></a>
+
+### reverb.setDampFrequency(dampFrequency)
+Change the Low Pass filter frequency.
+
+**Kind**: instance method of <code>[Reverb](#Reverb)</code>  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| dampFrequency | <code>Number</code> | low pass filter frequency. 0 to 44100 (depending on your maximum sampling frequency) |
+
+<a name="Reverb+process"></a>
+
+### reverb.process(samples) ⇒
+Process a given interleaved float value Array and copies and adds the reverb signal.
+
+**Kind**: instance method of <code>[Reverb](#Reverb)</code>  
+**Returns**: A new Float64Array interleaved buffer.  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| samples | <code>Array</code> | Array containing Float values or a Float64Array |
+
+<a name="RFFT"></a>
+
+## RFFT
+**Kind**: global class  
+
+* [RFFT](#RFFT)
+    * [new RFFT(bufferSize, sampleRate)](#new_RFFT_new)
+    * [.forward(buffer)](#RFFT+forward) ⇒
+
+<a name="new_RFFT_new"></a>
+
+### new RFFT(bufferSize, sampleRate)
+RFFT is a class for calculating the Discrete Fourier Transform of a signal
+with the Fast Fourier Transform algorithm.
+
+This method currently only contains a forward transform but is highly optimized.
+
+
+| Param | Type | Description |
+| --- | --- | --- |
+| bufferSize | <code>Number</code> | The size of the sample buffer to be computed. Must be power of 2 |
+| sampleRate | <code>Number</code> | The sampleRate of the buffer (eg. 44100) |
+
+<a name="RFFT+forward"></a>
+
+### rffT.forward(buffer) ⇒
+Performs a forward transform on the sample buffer.
+Converts a time domain signal to frequency domain spectra.
+
+**Kind**: instance method of <code>[RFFT](#RFFT)</code>  
+**Returns**: The frequency spectrum array  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| buffer | <code>Array</code> | The sample buffer. Buffer Length must be power of 2 |
+
+<a name="Sampler"></a>
+
+## Sampler
+**Kind**: global class  
+<a name="new_Sampler_new"></a>
+
+### new Sampler(file, bufferSize, sampleRate, playStart, playEnd, loopStart, loopEnd, loopMode)
+Sampler
+
+
+| Param | Type |
+| --- | --- |
+| file |  | 
+| bufferSize | <code>Number</code> | 
+| sampleRate | <code>Number</code> | 
+| playStart | <code>Number</code> | 
+| playEnd | <code>Number</code> | 
+| loopStart | <code>Number</code> | 
+| loopEnd | <code>Number</code> | 
+| loopMode | <code>Number</code> | 
+
+<a name="SingleDelay"></a>
+
+## SingleDelay
+**Kind**: global class  
+
+* [SingleDelay](#SingleDelay)
+    * [new SingleDelay(maxDelayInSamplesSize, delayInSamples, delayVolume)](#new_SingleDelay_new)
+    * [.setDelayInSamples(delayInSamples)](#SingleDelay+setDelayInSamples)
+    * [.setDelayVolume(delayVolume)](#SingleDelay+setDelayVolume)
+    * [.process(samples)](#SingleDelay+process) ⇒
+
+<a name="new_SingleDelay_new"></a>
+
+### new SingleDelay(maxDelayInSamplesSize, delayInSamples, delayVolume)
+SingleDelay effect by Almer Thie (http://code.almeros.com).
+Copyright 2010 Almer Thie. All rights reserved.
+Example: See usage in Reverb class
+
+This is a delay that does NOT feeds it's own delayed signal back into its
+circular buffer, neither does it return the original signal. Also known as
+an AllPassFilter(?).
+
+Compatible with interleaved stereo (or more channel) buffers and
+non-interleaved mono buffers.
+
+
+| Param | Type | Description |
+| --- | --- | --- |
+| maxDelayInSamplesSize | <code>Number</code> | Maximum possible delay in samples (size of circular buffer) |
+| delayInSamples | <code>Number</code> | Initial delay in samples |
+| delayVolume | <code>Number</code> | Initial feedback delay volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify) |
+
+<a name="SingleDelay+setDelayInSamples"></a>
+
+### singleDelay.setDelayInSamples(delayInSamples)
+Change the delay time in samples.
+
+**Kind**: instance method of <code>[SingleDelay](#SingleDelay)</code>  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| delayInSamples | <code>Number</code> | Delay in samples |
+
+<a name="SingleDelay+setDelayVolume"></a>
+
+### singleDelay.setDelayVolume(delayVolume)
+Change the return signal volume.
+
+**Kind**: instance method of <code>[SingleDelay](#SingleDelay)</code>  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| delayVolume | <code>Number</code> | Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify) |
+
+<a name="SingleDelay+process"></a>
+
+### singleDelay.process(samples) ⇒
+Process a given interleaved or mono non-interleaved float value Array and
+returns the delayed audio.
+
+**Kind**: instance method of <code>[SingleDelay](#SingleDelay)</code>  
+**Returns**: A new Float64Array interleaved or mono non-interleaved as was fed to this function.  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| samples | <code>Array</code> | Array containing Float values or a Float64Array |
+
+<a name="WindowFunction"></a>
+
+## WindowFunction
+**Kind**: global class  
+
+* [WindowFunction](#WindowFunction)
+    * [new WindowFunction(type, alpha)](#new_WindowFunction_new)
+    * [.process(buffer)](#WindowFunction+process)
+
+<a name="new_WindowFunction_new"></a>
+
+### new WindowFunction(type, alpha)
+WindowFunction
+
+
+| Param | Type |
+| --- | --- |
+| type | <code>Number</code> | 
+| alpha | <code>Number</code> | 
+
+<a name="WindowFunction+process"></a>
+
+### windowFunction.process(buffer)
+Process a buffer
+
+**Kind**: instance method of <code>[WindowFunction](#WindowFunction)</code>  
+
+| Param | Type |
+| --- | --- |
+| buffer | <code>Array</code> | 
+
+<a name="DSP"></a>
+
+## DSP
+DSP is an object which contains general purpose utility functions and constants
+
+**Kind**: global variable  
+
+* [DSP](#DSP)
+    * [.deinterleave](#DSP.deinterleave) ⇒
+    * [.getChannel](#DSP.getChannel) ⇒
+    * [.invert(buffer)](#DSP.invert) ⇒
+    * [.interleave(left, right)](#DSP.interleave) ⇒
+    * [.mixSampleBuffers(sampleBuffer1, sampleBuffer2, negate, volumeCorrection)](#DSP.mixSampleBuffers) ⇒
+    * [.RMS(buffer)](#DSP.RMS)
+    * [.Peak(buffer)](#DSP.Peak)
+    * [.mag2db(@buffer)](#DSP.mag2db) ⇒
+    * [.freqz(b, a, w)](#DSP.freqz) ⇒
+
+<a name="DSP.deinterleave"></a>
+
+### DSP.deinterleave ⇒
+Converts a stereo-interleaved sample buffer into split-stereo (dual mono) sample buffers
+
+**Kind**: static property of <code>[DSP](#DSP)</code>  
+**Returns**: an Array containing left and right channels  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| buffer | <code>Array</code> | A stereo-interleaved sample buffer |
+
+<a name="DSP.getChannel"></a>
+
+### DSP.getChannel ⇒
+Separates a channel from a stereo-interleaved sample buffer
+
+**Kind**: static property of <code>[DSP](#DSP)</code>  
+**Returns**: an Array containing a signal mono sample buffer  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| buffer | <code>Array</code> | A stereo-interleaved sample buffer |
+| channel | <code>Number</code> | A channel constant (LEFT, RIGHT, MIX) |
+
+<a name="DSP.invert"></a>
+
+### DSP.invert(buffer) ⇒
+Inverts the phase of a signal
+
+**Kind**: static method of <code>[DSP](#DSP)</code>  
+**Returns**: The inverted sample buffer  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| buffer | <code>Array</code> | A sample buffer |
+
+<a name="DSP.interleave"></a>
+
+### DSP.interleave(left, right) ⇒
+Converts split-stereo (dual mono) sample buffers into a stereo interleaved sample buffer
+
+**Kind**: static method of <code>[DSP](#DSP)</code>  
+**Returns**: The stereo interleaved buffer  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| left | <code>Array</code> | A sample buffer |
+| right | <code>Array</code> | A sample buffer |
+
+<a name="DSP.mixSampleBuffers"></a>
+
+### DSP.mixSampleBuffers(sampleBuffer1, sampleBuffer2, negate, volumeCorrection) ⇒
+Helper method (for Reverb) to mix two (interleaved) samplebuffers. It's possible
+to negate the second buffer while mixing and to perform a volume correction
+on the final signal.
+
+**Kind**: static method of <code>[DSP](#DSP)</code>  
+**Returns**: A new Float64Array interleaved buffer.  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| sampleBuffer1 | <code>Array</code> | Array containing Float values or a Float64Array |
+| sampleBuffer2 | <code>Array</code> | Array containing Float values or a Float64Array |
+| negate | <code>Boolean</code> | When true inverts/flips the audio signal |
+| volumeCorrection | <code>Number</code> | When you add multiple sample buffers, use this to tame your signal ;) |
+
+<a name="DSP.RMS"></a>
+
+### DSP.RMS(buffer)
+Find RMS of signal
+
+**Kind**: static method of <code>[DSP](#DSP)</code>  
+
+| Param | Type |
+| --- | --- |
+| buffer | <code>Array</code> | 
+
+<a name="DSP.Peak"></a>
+
+### DSP.Peak(buffer)
+Find Peak of signal
+
+**Kind**: static method of <code>[DSP](#DSP)</code>  
+
+| Param | Type |
+| --- | --- |
+| buffer | <code>Array</code> | 
+
+<a name="DSP.mag2db"></a>
+
+### DSP.mag2db(@buffer) ⇒
+Magnitude to decibels
+
+Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
+Copyright 2010 Ricard Marxer. All rights reserved.
+
+**Kind**: static method of <code>[DSP](#DSP)</code>  
+**Returns**: the array in decibels  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| @buffer | <code>Array</code> | The array of magnitudes to convert to decibels |
+
+<a name="DSP.freqz"></a>
+
+### DSP.freqz(b, a, w) ⇒
+Frequency response
+
+ Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
+ Copyright 2010 Ricard Marxer. All rights reserved.
+
+ Calculates the frequency response at the given points.
+
+**Kind**: static method of <code>[DSP](#DSP)</code>  
+**Returns**: the frequency response in magnitude  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| b | <code>Number</code> | The coefficients of the filter |
+| a | <code>Number</code> | The coefficients of the filter |
+| w | <code>Number</code> | The points (normally between -PI and PI) where to calculate the frequency response |
+
+<a name="sinh"></a>
+
+## sinh(num)
+Returns the hyperbolic sine of the number
+
+**Kind**: global function  
+**Meta**: version: 1004.2314  
+**Meta**: discuss at: http://phpjs.org/functions/sinh  
+**Meta**: original by: Onno Marsman  
+
+| Param | Type |
+| --- | --- |
+| num | <code>Number</code> | 
+
+**Example**  
+```js
+sinh(-0.9834330348825909); // => -1.1497971402636502
+```
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..e3a60ce
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,51 @@
+## DSP.js [![npm](https://img.shields.io/npm/v/dsp.js.svg?style=flat-square)](https://www.npmjs.com/package/dsp.js)
+
+DSP.js is a comprehensive digital signal processing library for javascript.
+It includes many functions for signal analysis and generation, including
+Oscillators(sine, saw, square, triangle), Window functions (Hann, Hamming, etc),
+Envelopes(ADSR), IIR Filters(lowpass, highpass, bandpass, notch), FFT and DFT
+transforms, Delays, Reverb.
+
+## Install
+
+__Node__
+
+Via npm: `npm install --save dsp.js` and require the modules:
+
+```js
+const { DSP, WindowFunction, Oscillator } = require('dsp.js')
+```
+
+You can require individual modules:
+
+```js
+const DSP = require('dsp.js/lib/dsp')
+const WindowFunction = require('dsp.js/lib/window-function')
+```
+
+__Browser__
+
+Or grab the [dsp.js](https://github.com/corbanbrook/dsp.js/blob/master/dist/dsp.min.js) and include it in your html:
+
+```html
+<script src="dsp.min.js"></script>
+```
+
+## Usage
+
+[Read the API documentation](https://github.com/corbanbrook/dsp.js/blob/master/docs/API.md)
+
+## Tests and scripts
+
+To setup you local machine to work with the code, first you have to clone this repository and install dependencies (`node` and `npm` are assumed to be installed): `npm install`
+
+- Run the tests: `npm test`
+- Generate API reference and distribution files: `npm run dist`
+- Run benchmarks: `npm run bench`
+
+
+## License
+
+MIT License
+
+Copyright (c) 2010 Corban Brook @corban                                                    
diff --git a/examples/biquad.html b/examples/biquad.html
index 754f017..f827564 100644
--- a/examples/biquad.html
+++ b/examples/biquad.html
@@ -11,7 +11,7 @@
     <script language="javascript" src="js/init.js"></script>
 
     <!-- Load DSP.js -->
-    <script src="../dsp.js"></script>
+    <script src="../dist/dsp.js"></script>
     <script>
       $(function() {
         $('#freq').slider({ orientation: 'vertical', range: 'min', min: 60, max: 10000, step: 1, value: 880, slide: changeF0 });
diff --git a/examples/fft.html b/examples/fft.html
index 5a35922..a6074cd 100644
--- a/examples/fft.html
+++ b/examples/fft.html
@@ -1,21 +1,21 @@
 <html>
-  <head>      
+  <head>
     <script language="javascript" src="js/processing.js"></script>
     <script language="javascript" src="js/init.js"></script>
-    <script language="javascript" src="../dsp.js"></script>
+    <script language="javascript" src="../dist/dsp.js"></script>
   </head>
   <body>
     <style>
       body { background: black; margin:0; padding:0;}
     </style>
-    
+
     <script>
       var frameBufferSize = 4096;
       var bufferSize = frameBufferSize/2;
-      
+
       var signal = new Float32Array(bufferSize);
       var peak = new Float32Array(bufferSize);
-      
+
       var fft = new FFT(bufferSize, 44100);
 
       function loadedMetaData(event) {
@@ -30,11 +30,11 @@
 
         // perform forward transform
         fft.forward(signal);
-        
+
         // calculate peak values
         for ( var i = 0; i < bufferSize; i++ ) {
           fft.spectrum[i] *= -1 * Math.log((fft.bufferSize/2 - i) * (0.5/fft.bufferSize/2)) * fft.bufferSize; // equalize, attenuates low freqs and boosts highs
-          
+
           if ( peak[i] < fft.spectrum[i] ) {
             peak[i] = fft.spectrum[i];
           } else {
@@ -43,7 +43,7 @@
         }
       }
     </script>
-    
+
     <script target="#fft" type="application/processing">
       void setup() {
         size(1400, 600);
@@ -52,19 +52,19 @@
         frameRate(60);
         //strokeCap(SQUARE);
       }
-      
+
       void draw() {
         background(0);
         //fill(0, 60);
         //rect(0, 0, width, height);
-        for ( int i = 0; i < fft.spectrum.length/2; i += 3 ) {          
+        for ( int i = 0; i < fft.spectrum.length/2; i += 3 ) {
           if (3 * i > width) { break; }
           var magnitude = fft.spectrum[i];
-          
+
           // draw magnitudes
           stroke((i) % 360, 60, constrain(magnitude * 6, 20, 100));
           line(3*i, height, 3*i, height - magnitude * 16);
-          
+
           // draw peak indicators
           stroke((i) % 360, 60, constrain(magnitude * 100, 50, 100));
           line(3*i, height - peak[i] * 16 - 1, 3*i, height - peak[i] * 16);
@@ -74,11 +74,11 @@
           println(FRAME_RATE);
         }
         */
-      } 
+      }
     </script>
-    
+
     <audio id="input" src="audio/corban-peddle.ogg" controls="true" onloadedmetadata="loadedMetaData(event);"></audio><br />
-    
+
     <div><canvas id="fft"></canvas></div>
   </body>
 </html>
diff --git a/examples/filter.html b/examples/filter.html
index 9b175c1..334d142 100644
--- a/examples/filter.html
+++ b/examples/filter.html
@@ -3,13 +3,13 @@
   <head>
     <!-- Load JQuery and JQuery-UI -->
     <script type="text/javascript" src="js/jquery-1.4.2.min.js"></script>
-    
+
     <!-- Load Processing.js -->
     <script language="javascript" src="js/processing.js"></script>
     <script language="javascript" src="js/init.js"></script>
 
     <!-- Load DSP.js -->
-    <script src="../dsp.js"></script>
+    <script src="../dist/dsp.js"></script>
 
     <style type="text/css">
       body, * {
@@ -45,7 +45,7 @@
         margin-bottom: 16px;
         width: 8px;
       }
-      
+
       .ui-slider .ui-slider-handle {
         width: 8px;
         margin-left: 3px;
@@ -57,7 +57,7 @@
       // Setup shared variables
       var sampleRate = 44100;
       var frameSize = 4096;
-      
+
       var signal = new Float32Array(frameSize/2);
       var lp12;
       var buffer = [];
@@ -65,7 +65,7 @@
 
       // Setup experimental audio out
       var output = new Audio();
-      
+
       if ( typeof output.mozSetup === 'function' ) {
         output.mozSetup(1, sampleRate);
       }
@@ -86,7 +86,7 @@
         output.mozWriteAudio(signal);
       }
     </script>
-    
+
     <script type="application/processing" target="#signal">
       Knob cut;
       Knob res;
@@ -94,8 +94,8 @@
       void setup() {
         size(512, 200);
 
-        cut = new Knob("", 60, 6000, 2500, 70, 20, 50, 50);        
-        res = new Knob("", 1, 20, 7, 0.3, 20, 100, 50);        
+        cut = new Knob("", 60, 6000, 2500, 70, 20, 50, 50);
+        res = new Knob("", 1, 20, 7, 0.3, 20, 100, 50);
 
         lp12 = new IIRFilter(DSP.LOWPASS, 22050, 0, sampleRate);
 
@@ -104,7 +104,7 @@
 
         stroke(255, 0, 0);
       }
-      
+
       void draw() {
         background(255);
         lp12.set(cut.value, res.value);
@@ -165,7 +165,7 @@
             this.active = false;
             Knob.active = false;
           }
-          
+
           this.percent =  (this.value - this.min) / (this.max - this.min);
           this.angle = map(this.value, this.min, this.max, radians(-130), radians(130));
           pushMatrix();
@@ -182,7 +182,7 @@
         }
 
         boolean isOver() {
-          if (mouseX > this.x - this.radius && 
+          if (mouseX > this.x - this.radius &&
               mouseX < this.x + this.radius &&
               mouseY > this.y - this.radius &&
               mouseY < this.y + this.radius) {
@@ -193,10 +193,10 @@
       }
 
     </script>
-    
+
     <h1>Audio Filter</h1>
     <p>Applies a Low pass filter to an audio stream. Remember to <b>Mute</b> the audio to hear the pure filtered sound.</p>
-    <p>The low pass filter acts as a gate, only letting frequencies below the cut off value through.</p> 
+    <p>The low pass filter acts as a gate, only letting frequencies below the cut off value through.</p>
     <audio id='input' tabindex="0" src="audio/megaman.ogg" controls="true" onloadedmetadata="loadedMetaData(event);" style="width: 512px;"></audio><br>
     <div><canvas id="signal" width="512px" height="200px"></canvas></div>
 
diff --git a/examples/grapheq.html b/examples/grapheq.html
index ecc7e92..694ff79 100644
--- a/examples/grapheq.html
+++ b/examples/grapheq.html
@@ -11,7 +11,7 @@
     <script language="javascript" src="js/init.js"></script>
 
     <!-- Load DSP.js -->
-    <script src="../dsp.js"></script>
+    <script src="../dist/dsp.js"></script>
     <script>
       $(function() {
         $('#minFreq').slider({ orientation: 'vertical', range: 'min', min: 10, max: 22000, step: 1, value: 40, slide: changeMinFreq });
diff --git a/examples/nowave.html b/examples/nowave.html
index 48f3dca..da4c8d7 100644
--- a/examples/nowave.html
+++ b/examples/nowave.html
@@ -1,8 +1,8 @@
 <html>
-  <head>      
+  <head>
     <script language="javascript" src="js/processing.js"></script>
     <script language="javascript" src="js/init.js"></script>
-    <script language="javascript" src="../dsp.js"></script>
+    <script language="javascript" src="../dist/dsp.js"></script>
   </head>
   <body>
     <script>
@@ -21,13 +21,13 @@
         output.mozSetup(1, sampleRate, 1);
       }
 
-      var audioWriter = function(s) {     
+      var audioWriter = function(s) {
         if ( typeof output.mozWriteAudio === 'function' ) {
           output.mozWriteAudio(s);
         }
       }
     </script>
-    
+
     <script target="#signal" type="application/processing">
       int nthHarmonic = 1;
       float frequency = 200;
@@ -35,7 +35,7 @@
       void setup() {
         size(1024, 600);
         frameRate(60);
-        
+
         sine = new Oscillator(Oscillator.Sine, frequency, 1, bufferSize, sampleRate);
         sine.generate();
         lp12 = new IIRFilter(DSP.LOWPASS, 400, 1, sampleRate);
@@ -52,7 +52,7 @@
         rect(-10, -10, width + 20, height + 20);
 
 
-        if (nthHarmonic > 40 ) 
+        if (nthHarmonic > 40 )
         {
           sizes = [2048, 1024, 512, 256, 128, 512, 256, 128];
           frequency = constrain(random(200), 10, 200);
@@ -65,19 +65,19 @@
 
         frequency = constrain((mouseX/50) * (mouseX/50), 10, 1000);
 
-        
+
         // Add harmonic
         if ( nthHarmonic > 1 ) {
-          harmonic = new Oscillator(Oscillator.Sine, frequency*nthHarmonic, 1/nthHarmonic, bufferSize, sampleRate); 
+          harmonic = new Oscillator(Oscillator.Sine, frequency*nthHarmonic, 1/nthHarmonic, bufferSize, sampleRate);
           harmonic.generate();
-          sine.add(harmonic); 
+          sine.add(harmonic);
         }
-        
+
         nthHarmonic += 2; // 3rd, 5th, 7th, 9th, etc
 
-        
+
         // Draw additive signal
-        
+
         for ( int i = 0; i < width - 1; i+=4 ) {
           stroke((i + frameCount + 50) % 360, 100, 60);
           line(i, height/2 - sine.signal[i % bufferSize] * 100, i+1, height/2 - sine.signal[(i+1) % bufferSize] * 300);
@@ -96,7 +96,7 @@
         audioWriter(sine.signal);
       }
     </script>
-  
+
     <div><canvas id="signal" width="200px" height="200px"></canvas></div>
   </body>
 </html>
diff --git a/examples/rfft.html b/examples/rfft.html
index 9a83091..0a0811b 100644
--- a/examples/rfft.html
+++ b/examples/rfft.html
@@ -2,7 +2,7 @@
   <head>      
     <script language="javascript" src="js/processing.js"></script>
     <script language="javascript" src="js/init.js"></script>
-    <script language="javascript" src="../dsp.js"></script>
+    <script language="javascript" src="../dist/dsp.js"></script>
   </head>
   <body>
     <style>
diff --git a/examples/sampler.html b/examples/sampler.html
index 6efeb3f..d8b8780 100644
--- a/examples/sampler.html
+++ b/examples/sampler.html
@@ -11,7 +11,7 @@
     <script language="javascript" src="js/init.js"></script>
 
     <!-- Load DSP.js -->
-    <script src="../dsp.js"></script>
+    <script src="../dist/dsp.js"></script>
     <script>
     </script>
 
diff --git a/examples/squarewave.html b/examples/squarewave.html
index f6be448..d5fae69 100644
--- a/examples/squarewave.html
+++ b/examples/squarewave.html
@@ -1,8 +1,8 @@
 <html>
-  <head>      
+  <head>
     <script language="javascript" src="js/processing.js"></script>
     <script language="javascript" src="js/init.js"></script>
-    <script language="javascript" src="../dsp.js"></script>
+    <script language="javascript" src="../dist/dsp.min.js"></script>
   </head>
   <body>
     <script>
@@ -20,11 +20,11 @@
         output.mozSetup(1, sampleRate, 1);
       }
 
-      var audioWriter = function(s) {     
+      var audioWriter = function(s) {
         output.mozWriteAudio(s);
       }
     </script>
-    
+
     <script target="#signal" type="application/processing">
       int nthHarmonic = 1;
       float frequency = 344.53;
@@ -33,7 +33,7 @@
       void setup() {
         size(1024, 100);
         frameRate(60);
-        
+
         fft = new FFT(bufferSize, sampleRate);
         sine = new Oscillator(Oscillator.Sine, frequency, 1, bufferSize, sampleRate);
         sine.generate();
@@ -42,28 +42,28 @@
       void draw() {
         background(0);
 
-        if (nthHarmonic > 40 ) 
+        if (nthHarmonic > 40 )
         {
           nthHarmonic = 1;
           sine.generate();
         }
-        
+
         // Add harmonic
         if ( nthHarmonic > 1 ) {
-          harmonic = new Oscillator(Oscillator.Sine, frequency*nthHarmonic, 1/nthHarmonic, bufferSize, sampleRate); 
+          harmonic = new Oscillator(Oscillator.Sine, frequency*nthHarmonic, 1/nthHarmonic, bufferSize, sampleRate);
           harmonic.generate();
-          sine.add(harmonic); 
+          sine.add(harmonic);
         }
-        
+
         nthHarmonic += 2; // 3rd, 5th, 7th, 9th, etc
 
         // Calculate forward transform
         fft.forward(sine.signal);
-        
+
         // Draw additive signal
         stroke(255);
         strokeWeight(1.5);
-        
+
         for ( int i = 0; i < bufferSize - 1; i++ ) {
           line(i, scale + 10 - sine.signal[i] * scale, i+1, scale + 10 - sine.signal[i+1] * scale);
         }
@@ -72,30 +72,30 @@
         audioWriter(sine.signal);
       }
     </script>
-  
+
     <script target="#fft" type="application/processing">
       void setup() {
         size(1024, 300);
       }
-      
+
       void draw() {
         background(0);
-        
+
         // Draw spectrum
         stroke(255);
         strokeWeight(1.5);
-        
+
         for ( int i = 0; i < fft.spectrum.length - 1; i+=4 ) {
           line(2*i/4, height - 10 - fft.spectrum[i] * 512, 2*i/4+1, height - 10 - fft.spectrum[i+1] * 512);
         }
-      } 
+      }
     </script>
-    
+
     <h1>Building a Square wave</h1>
     <p>Any complex waveform can be made up of simple sinusoids.</p>
     <p>A square wave is made by first starting with a sine wave then adding every other nth harmonic at frequency nth * the base frequency and amplitude of 1/n</p>
-    <p>FFT breaks down a waveform into its sinusoidal components to measure the amplitude of the frequencies.</p> 
-    
+    <p>FFT breaks down a waveform into its sinusoidal components to measure the amplitude of the frequencies.</p>
+
     <b>1.</b> Additive sine waves combine to form a complex square wave.
     <div><canvas id="signal" width="200px" height="200px"></canvas></div>
     <p></p>
diff --git a/examples/synthesizer.html b/examples/synthesizer.html
index a888029..fc63f91 100644
--- a/examples/synthesizer.html
+++ b/examples/synthesizer.html
@@ -2,19 +2,19 @@
 <html>
   <head>
     <!-- Load JQuery and JQuery-UI -->
-    <link type="text/css" href="css/hot-sneaks/jquery-ui-1.8.custom.css" rel="stylesheet" />  
+    <link type="text/css" href="css/hot-sneaks/jquery-ui-1.8.custom.css" rel="stylesheet" />
     <script type="text/javascript" src="js/jquery-1.4.2.min.js"></script>
     <script type="text/javascript" src="js/jquery-ui-1.8.custom.min.js"></script>
-    
+
     <!-- Load Processing.js -->
     <script language="javascript" src="js/processing.js"></script>
     <script language="javascript" src="js/init.js"></script>
 
     <!-- Load DSP.js -->
-    <script src="../dsp.js"></script>
+    <script src="../dist/dsp.js"></script>
     <script>
       $(function() {
-        // Amplitude 
+        // Amplitude
         $('#amp'   ).slider({ orientation: 'vertical', range: 'min', value: 0.7,  min: 0,    max: 1,    step: 0.01, slide: changeAmplitude });
 
         // Amp. Envelope
@@ -35,7 +35,7 @@
 
         // Osc Mix
         $('#osc_mix').slider({ orientation: 'vertical', range: 'min', value: 0.5, min: -0.1, max: 1.1,  step: 0.01, slide: changeAmplitude });
-        
+
         changeAmplitude(); // reset amp and mix
 
         // Debug toggle
@@ -61,27 +61,27 @@
         $("input[name='filter_type']").change(changeFilter);
         changeFilter(); // reset filter to slider defaults
 
-        // Oscillator waveform select 
+        // Oscillator waveform select
         $("#osc1_waveform").change(changeOsc1);
         $("#osc2_waveform").change(changeOsc2);
 
-        // Oscillator osctave select 
+        // Oscillator osctave select
         $("#osc1_octave").change(changeOsc1);
         $("#osc2_octave").change(changeOsc2);
-        
-        // Oscillator osctave select 
+
+        // Oscillator osctave select
         $("#osc1_semi").change(changeOsc1);
         $("#osc2_semi").change(changeOsc2);
 
         changeOsc1(); // reset oscillator to select defaults
-        changeOsc2(); 
+        changeOsc2();
       });
     </script>
 
     <style type="text/css">
       body, * {
         font-family: Arial, sans-serif;
-        font-size: 12px; 
+        font-size: 12px;
       }
 
       .control {
@@ -111,7 +111,7 @@
         margin-bottom: 16px;
         width: 8px;
       }
-      
+
       .ui-slider .ui-slider-handle {
         width: 8px;
         margin-left: 3px;
@@ -122,27 +122,27 @@
     <script>
       var SHOW_SEQUENCER = true;
       var CURVE_EDITOR = false;
-    
+
       // Setup shared variables
       var sampleRate = 44100;
       var bufferSize = 1024;
       var bufferTime = Math.floor(1000 / (sampleRate / bufferSize));
-      
+
       var frequency  = 440;
       var amplitude  = 0.7; // Default amplitude at 70%
-      
+
       var changeAmplitude = function() {
-        amplitude = $('#amp').slider('option', 'value'); 
+        amplitude = $('#amp').slider('option', 'value');
         $('#ampLevel').html(Math.round(amplitude * 100) + "%");
-        
+
         var mix = $('#osc_mix').slider('option', 'value');
-        
+
         mix = mix > 1 ? 1 : mix;
         mix = mix < 0 ? 0 : mix;
-        
+
         osc2_amplitude = 1 - (osc1_amplitude = mix);
-  
- 
+
+
         osc1_amplitude *= amplitude;
         osc2_amplitude *= amplitude;
       };
@@ -156,18 +156,18 @@
 
       var changeFilterEnvelope = function() {
       };
-      
+
       var osc;
 
       var osc1;
       var osc2;
-      
+
       var osc1_waveform;
       var osc2_waveform;
 
       var osc1_octave;
       var osc2_octave;
-      
+
       var osc1_amplitude;
       var osc2_amplitude;
 
@@ -191,13 +191,13 @@
         osc2_octave   = parseInt($("#osc2_octave").val());
         osc2_semi     = parseInt($("#osc2_semi").val());
       };
-      
+
       var bpm = 60; // Beats per minute
-      
+
       var sequencerStep = 0; // Sequencer step postion
-      
+
       var sequencerTicksPerMillisecond = bpm * 8 / 60000; // Sequencer Ticks each millisecond. A Tick is a partial step.
-      
+
       var polyphony = 8; // Number of oscillators that can be played at the same time
       var oscPool = new Array(); // Pool of active oscillators
 
@@ -216,14 +216,14 @@
           sequencerNoteOn[i][j] = false;
         }
       }
-      
+
       var curveOver = 0;
       var curveCutoff = new Array(17);
       var curveRes = new Array(17);
       for ( var i = 0; i < 17; i++ ) {
         curveCutoff[i] = sampleRate / 4;
         curveRes[i] = 0.1;
-      } 
+      }
 
       var oct = 3; // Root Octave
 
@@ -242,8 +242,8 @@
 
       var writeCount = 0;
 
-      var timeStamp = (new Date()).getTime();  
-      
+      var timeStamp = (new Date()).getTime();
+
       var silence = new Float32Array(bufferSize);
 
       // Setup audio channel
@@ -251,40 +251,40 @@
       if ( typeof a.mozSetup == 'function' ) {
         a.mozSetup(1, sampleRate);
       }
-      
+
       var timePerWrite = 0;
       var programStart;
-      
+
       var audioWriter = function() {
-        var startTime = (new Date()).getTime();        
+        var startTime = (new Date()).getTime();
         var currentTime = (new Date()).getTime();
         var mergedOsc; // undefined
         var filterOn = $('#filter_toggle').attr('checked');
-        
+
         if ( programStart === undefined ) {
            programStart = new Date();
         }
-        
+
         // keep same pace as a real time
         //while ( (new Date() - programStart) / 1000 * sampleRate / bufferSize >= writeCount ) {
           for ( var i = 0; i < oscPool.length; i++ ) {
             // Generate new frame of the signal
             if ( oscPool[i].envelope.isActive() ) {
               oscPool[i].osc1.generate(); // This generates a new frameBuffer of signal and applies the envelope as it goes.
-              oscPool[i].osc2.generate(); 
-              
+              oscPool[i].osc2.generate();
+
               // combine osc1 and osc2
               oscPool[i].osc1.addSignal(oscPool[i].osc2.signal);
-              
+
               // apply the filter
               if ( filterOn ) {
                 filter.addEnvelope(oscPool[i].filterEnvelope);
                 filter.process(oscPool[i].osc1.signal);
               }
-              
+
               // apply the envelope
               oscPool[i].envelope.process(oscPool[i].osc1.signal);
-              
+
               // Merge down oscPool
               if ( typeof mergedOsc === 'undefined' ) {
                 mergedOsc = oscPool[i].osc1;
@@ -295,7 +295,7 @@
               oscPool[i] = null; // Mark for deletion
             }
           }
-          
+
           // Delete marked oscillators from the pool
           var tmpPool = new Array();
           for ( var i = 0; i < oscPool.length; i++ ) {
@@ -304,7 +304,7 @@
             }
           }
           oscPool = tmpPool;
-          
+
           // Set the global osc object
           if ( typeof mergedOsc !== 'undefined' ) {
             osc = mergedOsc;
@@ -313,87 +313,87 @@
             //osc.signal = undefined;
           }
 
-          // Flush buffer  
+          // Flush buffer
           a.mozWriteAudio([]);
-          
+
             // Write next audio frame
             a.mozWriteAudio(osc.signal);
 
           writeCount++;
 
           sequencerStep += (currentTime - timeStamp) * sequencerTicksPerMillisecond;
-        //}        
+        //}
 
         timeStamp = (new Date()).getTime();
         var endTime = (new Date()).getTime();
-        
+
         timePerWrite = endTime - startTime;
       }
     </script>
-      
+
     <script target="#signal" type="application/processing">
       float scale = 90.0;
 
       void setup() {
         size(512, 200);
-        
+
         // setup oscillator to hold the merged signal of osc1 and osc2
         osc = new Oscillator(osc1_waveform, 440, 0, bufferSize, sampleRate);
-        
-        setInterval(audioWriter, bufferTime); // start audioWriter timer        
+
+        setInterval(audioWriter, bufferTime); // start audioWriter timer
         frameRate(10);
       }
 
       void draw() {
         var startTime = (new Date()).getTime();
-        
+
         int sequencerCol = Math.floor(sequencerStep) % 32;
         background(0, 0, 30);
-        
+
         var curCol = Math.floor(sequencerCol/2);
         var nextCol = (curCol + 1) % 17;
         var alpha = ((sequencerStep % 32) / 2) - curCol; // should be a value between 0 and 0.99
-        
+
         if ( $("#curve_cutoff_toggle").attr("checked") ) {
           // interpolate value
           var curveVal = curveCutoff[curCol] + (curveCutoff[nextCol] - curveCutoff[curCol]) * alpha;
-          
+
           $('#cutoff').slider('option', 'value', curveVal);
           changeFilter();
         }
-        
+
         if ( $("#curve_res_toggle").attr("checked") ) {
           var curveVal = curveRes[curCol] + (curveRes[nextCol] - curveRes[curCol]) * alpha;
-          
+
           $('#res').slider('option', 'value', curveVal);
           changeFilter();
         }
-        
+
         // Check sequencer column for notes to trigger
-        for ( int i = 0; i < 24; i ++ ) {  
+        for ( int i = 0; i < 24; i ++ ) {
           if ( sequencer[sequencerCol][i] == true && !sequencerNoteOn[sequencerCol][i] ) {
             sequencerNoteOn[sequencerCol][i] = true;
-            
+
             frequency = midiNoteFreq[12*oct+(24-i)];
-            
+
             osc1_frequency = midiNoteFreq[12 * osc1_octave + (24 - i) + osc1_semi] + osc1_cent;
             osc2_frequency = midiNoteFreq[12 * osc2_octave + (24 - i) + osc2_semi] + osc2_cent;
-            
+
             // Add oscillator to the pool
             var index = oscPool.length;
-            
+
             var osc1 = new Oscillator(osc1_waveform, osc1_frequency, osc1_amplitude, bufferSize, sampleRate);
             var osc2 = new Oscillator(osc2_waveform, osc2_frequency, osc2_amplitude, bufferSize, sampleRate);
-            var adsr = new ADSR($('#A').slider('option', 'value'), $('#D').slider('option', 'value'), $('#S').slider('option', 'value'), bufferTime / 1000, $('#R').slider('option', 'value'), sampleRate);              
+            var adsr = new ADSR($('#A').slider('option', 'value'), $('#D').slider('option', 'value'), $('#S').slider('option', 'value'), bufferTime / 1000, $('#R').slider('option', 'value'), sampleRate);
             var fadsr = new ADSR($('#FA').slider('option', 'value'), $('#FD').slider('option', 'value'), $('#FS').slider('option', 'value'), bufferTime / 1000, $('#FR').slider('option', 'value'), sampleRate);
-            
+
             oscPool[index] = {};
             oscPool[index].osc1 = osc1;
             oscPool[index].osc2 = osc2;
             oscPool[index].envelope = adsr;
             oscPool[index].filterEnvelope = fadsr;
           }
-          
+
           if ( sequencer[sequencerCol][i] == false && sequencerCol > 0 ) {
             sequencerNoteOn[sequencerCol-1][i] = false;
           } else if ( sequencer[sequencerCol][i] == false && sequencerCol == 0 ) {
@@ -408,12 +408,12 @@
            *  Show step sequencer
            *
            */
-          stroke(200, 255, 255, 20); 
+          stroke(200, 255, 255, 20);
           // Render horozontal lines
           for (int i = 0; i < 24; i++ ) {
             line(0, i * (height/24), width, i * (height/24));
           }
-          
+
           // Render vertical lines
           for (int i = 0; i < 32; i++ ) {
             if ( i % 8 == 0 ) {
@@ -424,7 +424,7 @@
               strokeWeight(1);
             }
             line(i * (width/32), 0, i * (width/32), height);
-            
+
             // Render sequencer step position
             if ( i == Math.floor(sequencerStep % 32) ) {
               noStroke();
@@ -452,74 +452,74 @@
               }
             }
           }
-          
+
           if ( CURVE_EDITOR ) {
             strokeWeight(2);
-            
+
             var cutoffScale = 50;
             var resScale = 100;
-            
+
             for ( int i = 0; i < 17; i++ ) {
               fill(0, 0, 30);
-              
-              
+
+
               // Cut off curve
               if ( $("#curve_cutoff_toggle").attr("checked") ) {
                 stroke(200, 255, 255, 100);
               } else {
                 stroke(200, 255, 255, 40);
               }
-              
+
               var xPos = i * (width/16);
               var yPos = height/2 - curveCutoff[i] / cutoffScale;
-              
+
               if (i < 16 ) {
                 var xPosNext = (i+1) * (width/16);
                 var yPosNext = height/2 - curveCutoff[i+1] / cutoffScale;
-                
+
                 line(xPos, yPos, xPosNext, yPosNext);
               }
-              
-              if ( mouseX > xPos - 7 && mouseX < xPos + 7 && mouseY > yPos -7 && mouseY < yPos + 7 ) { 
+
+              if ( mouseX > xPos - 7 && mouseX < xPos + 7 && mouseY > yPos -7 && mouseY < yPos + 7 ) {
                 ellipse(xPos, yPos, 7, 7);
                 curveOver = i;
               } else {
                 ellipse(xPos, yPos, 5, 5);
               }
-              
-              
+
+
               // Resonance Curve
               if ( $("#curve_res_toggle").attr("checked") ) {
                 stroke(200, 255, 255, 100);
               } else {
                 stroke(200, 255, 255, 40);
               }
-              
+
               var xPos = i * (width/16);
               var yPos = height - curveRes[i] * resScale;
-              
+
               if (i < 16 ) {
                 var xPosNext = (i+1) * (width/16);
                 var yPosNext = height - curveRes[i+1] * resScale;
-                
+
                 line(xPos, yPos, xPosNext, yPosNext);
               }
-              
-              if ( mouseX > xPos - 7 && mouseX < xPos + 7 && mouseY > yPos -7 && mouseY < yPos + 7 ) { 
+
+              if ( mouseX > xPos - 7 && mouseX < xPos + 7 && mouseY > yPos -7 && mouseY < yPos + 7 ) {
                 ellipse(xPos, yPos, 7, 7);
                 curveOver = i;
               } else {
                 ellipse(xPos, yPos, 5, 5);
               }
-              
+
               // draw center line
               stroke(200, 255, 255, 60);
               line(0, height/2, width, height/2);
-            
-              
+
+
             }
             strokeWeight(1);
-            
+
             if ( DRAGGING ) {
               if ( mouseY < height / 2 ) {
                 curveCutoff[curveOver] = (height/2 - mouseY) * cutoffScale;
@@ -528,7 +528,7 @@
               }
             }
           }
-          
+
         } else {
           /*
            *  Signal View
@@ -538,15 +538,15 @@
            */
           strokeWeight(1.5);
           stroke(155, 200, 220);
-          for ( int i = 0; i < width/4 - 1; i++ ) {   
+          for ( int i = 0; i < width/4 - 1; i++ ) {
             line(4*i, scale + 10 - osc.signal[4*i] * scale, 4*(i+1), scale + 10 - osc.signal[4*(i+1)] * scale);
           }
         }
 
         var endTime = (new Date()).getTime();
-        
+
         if ( $('#debug_toggle').attr('checked') ) {
-          $('#debug').html(    
+          $('#debug').html(
             "Time/frame (milliseconds): " + ( endTime - startTime ) + "<br>" +
             "Time/write (milliseconds): " + ( timePerWrite ) + "<br>" +
             "Audio frame size: " + bufferSize + "<br>" +
@@ -559,9 +559,9 @@
           );
         }
       }
-      
+
       boolean DRAGGING = false;
-      
+
       void mousePressed() {
         if ( SHOW_SEQUENCER && CURVE_EDITOR ) {
           DRAGGING = true;
@@ -588,19 +588,19 @@
         }
       }
     </script>
-    
+
     <h1>Sequencer</h1>
     <p>Sequencer controlled oscillator with envelope and filter.</p>
 
     <p><b>Click in the sequencer grid to plot notes. Switch to the signal view and adjust the envelope and filter to see the resulting waveforms.</b></p>
     <p>Keyboard Commands:</p>
-    
+
     <ul>
       <li><b>S</b> - toggle between sequencer/signal view</li>
       <li><b>C</b> - toggle curve editor</li>
       <li><b>W</b> - change waveform</li>
     </ul>
-    
+
 
     <div style="float:left;margin-right: 5px;"><canvas id="signal" width="200px" height="200px"></canvas></div>
     <div class="control">
diff --git a/examples/vocoder.html b/examples/vocoder.html
index f343212..0dfb3c5 100644
--- a/examples/vocoder.html
+++ b/examples/vocoder.html
@@ -11,7 +11,7 @@
     <script language="javascript" src="js/init.js"></script>
 
     <!-- Load DSP.js -->
-    <script src="../dsp.js"></script>
+    <script src="../dist/dsp.js"></script>
     <script>
     </script>
 
diff --git a/lib/adsr.js b/lib/adsr.js
new file mode 100644
index 0000000..2384d0f
--- /dev/null
+++ b/lib/adsr.js
@@ -0,0 +1,134 @@
+/**
+ * ADSR Envelope
+ *
+ * @name ADSR
+ * @param {Number} attack The attack length in seconds
+ * @param {Number} decay The decay length in seconds
+ * @param {Number} sustain The sustain level
+ * @param {Number} release The release length in seconds
+ * @param {Number} sampleRate The the sample rate
+ *
+ * @constructor
+ */
+function ADSR(attackLength, decayLength, sustainLevel, sustainLength, releaseLength, sampleRate) {
+  this.sampleRate = sampleRate;
+  // Length in seconds
+  this.attackLength  = attackLength;
+  this.decayLength   = decayLength;
+  this.sustainLevel  = sustainLevel;
+  this.sustainLength = sustainLength;
+  this.releaseLength = releaseLength;
+  this.sampleRate    = sampleRate;
+
+  // Length in samples
+  this.attackSamples  = attackLength  * sampleRate;
+  this.decaySamples   = decayLength   * sampleRate;
+  this.sustainSamples = sustainLength * sampleRate;
+  this.releaseSamples = releaseLength * sampleRate;
+
+  // Updates the envelope sample positions
+  this.update = function() {
+    this.attack         =                this.attackSamples;
+    this.decay          = this.attack  + this.decaySamples;
+    this.sustain        = this.decay   + this.sustainSamples;
+    this.release        = this.sustain + this.releaseSamples;
+  };
+
+  this.update();
+
+  this.samplesProcessed = 0;
+}
+
+/**
+ * Start the envelope
+ */
+ADSR.prototype.noteOn = function() {
+  this.samplesProcessed = 0;
+  this.sustainSamples = this.sustainLength * this.sampleRate;
+  this.update();
+};
+
+/**
+ * Stop the envelope
+ *
+ * Send a note off when using a sustain of infinity to let the envelope enter
+ * the release phase
+ */
+ADSR.prototype.noteOff = function() {
+  this.sustainSamples = this.samplesProcessed - this.decaySamples;
+  this.update();
+};
+
+/**
+ * Process sample
+ */
+ADSR.prototype.processSample = function(sample) {
+  var amplitude = 0;
+
+  if ( this.samplesProcessed <= this.attack ) {
+    amplitude = 0 + (1 - 0) * ((this.samplesProcessed - 0) / (this.attack - 0));
+  } else if ( this.samplesProcessed > this.attack && this.samplesProcessed <= this.decay ) {
+    amplitude = 1 + (this.sustainLevel - 1) * ((this.samplesProcessed - this.attack) / (this.decay - this.attack));
+  } else if ( this.samplesProcessed > this.decay && this.samplesProcessed <= this.sustain ) {
+    amplitude = this.sustainLevel;
+  } else if ( this.samplesProcessed > this.sustain && this.samplesProcessed <= this.release ) {
+    amplitude = this.sustainLevel + (0 - this.sustainLevel) * ((this.samplesProcessed - this.sustain) / (this.release - this.sustain));
+  }
+
+  return sample * amplitude;
+};
+
+/**
+ * Get current value
+ * @return {Number} amplitude
+ */
+ADSR.prototype.value = function() {
+  var amplitude = 0;
+
+  if ( this.samplesProcessed <= this.attack ) {
+    amplitude = 0 + (1 - 0) * ((this.samplesProcessed - 0) / (this.attack - 0));
+  } else if ( this.samplesProcessed > this.attack && this.samplesProcessed <= this.decay ) {
+    amplitude = 1 + (this.sustainLevel - 1) * ((this.samplesProcessed - this.attack) / (this.decay - this.attack));
+  } else if ( this.samplesProcessed > this.decay && this.samplesProcessed <= this.sustain ) {
+    amplitude = this.sustainLevel;
+  } else if ( this.samplesProcessed > this.sustain && this.samplesProcessed <= this.release ) {
+    amplitude = this.sustainLevel + (0 - this.sustainLevel) * ((this.samplesProcessed - this.sustain) / (this.release - this.sustain));
+  }
+
+  return amplitude;
+};
+
+/**
+ * Process a buffer
+ * @param {Array} buffer
+ */
+ADSR.prototype.process = function(buffer) {
+  for ( var i = 0; i < buffer.length; i++ ) {
+    buffer[i] *= this.value();
+
+    this.samplesProcessed++;
+  }
+
+  return buffer;
+};
+
+/**
+ * Test if the envelope is active
+ * @return {Boolean}
+ */
+ADSR.prototype.isActive = function() {
+  if ( this.samplesProcessed > this.release || this.samplesProcessed === -1 ) {
+    return false;
+  } else {
+    return true;
+  }
+};
+
+/**
+ * Disable the envelope
+ */
+ADSR.prototype.disable = function() {
+  this.samplesProcessed = -1;
+};
+
+module.exports = ADSR;
diff --git a/lib/biquad.js b/lib/biquad.js
new file mode 100644
index 0000000..81c6e04
--- /dev/null
+++ b/lib/biquad.js
@@ -0,0 +1,288 @@
+/* global Float64Array */
+var DSP = require("./dsp");
+var sinh = require("./sinh");
+
+/**
+ * Biquad filter
+ *
+ * Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
+ *  Copyright 2010 Ricard Marxer. All rights reserved.
+ *
+ * Implementation based on:
+ * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
+ *
+ * @param {Number} type
+ * @param {Number} sampleRate
+ * @constructor
+ */
+function Biquad(type, sampleRate) {
+  this.Fs = sampleRate;
+  this.type = type;  // type of the filter
+  this.parameterType = DSP.Q; // type of the parameter
+
+  this.x_1_l = 0;
+  this.x_2_l = 0;
+  this.y_1_l = 0;
+  this.y_2_l = 0;
+
+  this.x_1_r = 0;
+  this.x_2_r = 0;
+  this.y_1_r = 0;
+  this.y_2_r = 0;
+
+  this.b0 = 1;
+  this.a0 = 1;
+
+  this.b1 = 0;
+  this.a1 = 0;
+
+  this.b2 = 0;
+  this.a2 = 0;
+
+  this.b0a0 = this.b0 / this.a0;
+  this.b1a0 = this.b1 / this.a0;
+  this.b2a0 = this.b2 / this.a0;
+  this.a1a0 = this.a1 / this.a0;
+  this.a2a0 = this.a2 / this.a0;
+
+  this.f0 = 3000;   // "wherever it"s happenin", man."  Center Frequency or
+                    // Corner Frequency, or shelf midpoint frequency, depending
+                    // on which filter type.  The "significant frequency".
+
+  this.dBgain = 12; // used only for peaking and shelving filters
+
+  this.Q = 1;       // the EE kind of definition, except for peakingEQ in which A*Q is
+                    // the classic EE Q.  That adjustment in definition was made so that
+                    // a boost of N dB followed by a cut of N dB for identical Q and
+                    // f0/Fs results in a precisely flat unity gain filter or "wire".
+
+  this.BW = -3;     // the bandwidth in octaves (between -3 dB frequencies for BPF
+                    // and notch or between midpoint (dBgain/2) gain frequencies for
+                    // peaking EQ
+
+  this.S = 1;       // a "shelf slope" parameter (for shelving EQ only).  When S = 1,
+                    // the shelf slope is as steep as it can be and remain monotonically
+                    // increasing or decreasing gain with frequency.  The shelf slope, in
+                    // dB/octave, remains proportional to S for all other values for a
+                    // fixed f0/Fs and dBgain.
+
+  this.coefficients = function() {
+    var b = [this.b0, this.b1, this.b2];
+    var a = [this.a0, this.a1, this.a2];
+    return {b: b, a:a};
+  };
+
+  this.setFilterType = function(type) {
+    this.type = type;
+    this.recalculateCoefficients();
+  };
+
+  this.setSampleRate = function(rate) {
+    this.Fs = rate;
+    this.recalculateCoefficients();
+  };
+
+  this.setQ = function(q) {
+    this.parameterType = DSP.Q;
+    this.Q = Math.max(Math.min(q, 115.0), 0.001);
+    this.recalculateCoefficients();
+  };
+
+  this.setBW = function(bw) {
+    this.parameterType = DSP.BW;
+    this.BW = bw;
+    this.recalculateCoefficients();
+  };
+
+  this.setS = function(s) {
+    this.parameterType = DSP.S;
+    this.S = Math.max(Math.min(s, 5.0), 0.0001);
+    this.recalculateCoefficients();
+  };
+
+  this.setF0 = function(freq) {
+    this.f0 = freq;
+    this.recalculateCoefficients();
+  };
+
+  this.setDbGain = function(g) {
+    this.dBgain = g;
+    this.recalculateCoefficients();
+  };
+
+  this.recalculateCoefficients = function() {
+    var A;
+    if (type === DSP.PEAKING_EQ || type === DSP.LOW_SHELF || type === DSP.HIGH_SHELF ) {
+      A = Math.pow(10, (this.dBgain/40));  // for peaking and shelving EQ filters only
+    } else {
+      A  = Math.sqrt( Math.pow(10, (this.dBgain/20)) );
+    }
+
+    var w0 = DSP.TWO_PI * this.f0 / this.Fs;
+
+    var cosw0 = Math.cos(w0);
+    var sinw0 = Math.sin(w0);
+
+    var alpha = 0;
+
+    switch (this.parameterType) {
+      case DSP.Q:
+        alpha = sinw0/(2*this.Q);
+        break;
+
+      case DSP.BW:
+        alpha = sinw0 * sinh( Math.LN2/2 * this.BW * w0/sinw0 );
+        break;
+
+      case DSP.S:
+        alpha = sinw0/2 * Math.sqrt( (A + 1/A)*(1/this.S - 1) + 2 );
+        break;
+    }
+
+    /**
+        FYI: The relationship between bandwidth and Q is
+             1/Q = 2*sinh(ln(2)/2*BW*w0/sin(w0))     (digital filter w BLT)
+        or   1/Q = 2*sinh(ln(2)/2*BW)             (analog filter prototype)
+
+        The relationship between shelf slope and Q is
+             1/Q = sqrt((A + 1/A)*(1/S - 1) + 2)
+    */
+
+    var coeff;
+
+    switch (this.type) {
+      case DSP.LPF:       // H(s) = 1 / (s^2 + s/Q + 1)
+        this.b0 =  (1 - cosw0)/2;
+        this.b1 =   1 - cosw0;
+        this.b2 =  (1 - cosw0)/2;
+        this.a0 =   1 + alpha;
+        this.a1 =  -2 * cosw0;
+        this.a2 =   1 - alpha;
+        break;
+
+      case DSP.HPF:       // H(s) = s^2 / (s^2 + s/Q + 1)
+        this.b0 =  (1 + cosw0)/2;
+        this.b1 = -(1 + cosw0);
+        this.b2 =  (1 + cosw0)/2;
+        this.a0 =   1 + alpha;
+        this.a1 =  -2 * cosw0;
+        this.a2 =   1 - alpha;
+        break;
+
+      case DSP.BPF_CONSTANT_SKIRT:       // H(s) = s / (s^2 + s/Q + 1)  (constant skirt gain, peak gain = Q)
+        this.b0 =   sinw0/2;
+        this.b1 =   0;
+        this.b2 =  -sinw0/2;
+        this.a0 =   1 + alpha;
+        this.a1 =  -2*cosw0;
+        this.a2 =   1 - alpha;
+        break;
+
+      case DSP.BPF_CONSTANT_PEAK:       // H(s) = (s/Q) / (s^2 + s/Q + 1)      (constant 0 dB peak gain)
+        this.b0 =   alpha;
+        this.b1 =   0;
+        this.b2 =  -alpha;
+        this.a0 =   1 + alpha;
+        this.a1 =  -2*cosw0;
+        this.a2 =   1 - alpha;
+        break;
+
+      case DSP.NOTCH:     // H(s) = (s^2 + 1) / (s^2 + s/Q + 1)
+        this.b0 =   1;
+        this.b1 =  -2*cosw0;
+        this.b2 =   1;
+        this.a0 =   1 + alpha;
+        this.a1 =  -2*cosw0;
+        this.a2 =   1 - alpha;
+        break;
+
+      case DSP.APF:       // H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1)
+        this.b0 =   1 - alpha;
+        this.b1 =  -2*cosw0;
+        this.b2 =   1 + alpha;
+        this.a0 =   1 + alpha;
+        this.a1 =  -2*cosw0;
+        this.a2 =   1 - alpha;
+        break;
+
+      case DSP.PEAKING_EQ:  // H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
+        this.b0 =   1 + alpha*A;
+        this.b1 =  -2*cosw0;
+        this.b2 =   1 - alpha*A;
+        this.a0 =   1 + alpha/A;
+        this.a1 =  -2*cosw0;
+        this.a2 =   1 - alpha/A;
+        break;
+
+      case DSP.LOW_SHELF:   // H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1)
+        coeff = sinw0 * Math.sqrt( (A^2 + 1)*(1/this.S - 1) + 2*A );
+        this.b0 =    A*((A+1) - (A-1)*cosw0 + coeff);
+        this.b1 =  2*A*((A-1) - (A+1)*cosw0);
+        this.b2 =    A*((A+1) - (A-1)*cosw0 - coeff);
+        this.a0 =       (A+1) + (A-1)*cosw0 + coeff;
+        this.a1 =   -2*((A-1) + (A+1)*cosw0);
+        this.a2 =       (A+1) + (A-1)*cosw0 - coeff;
+        break;
+
+      case DSP.HIGH_SHELF:   // H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A)
+        coeff = sinw0 * Math.sqrt( (A^2 + 1)*(1/this.S - 1) + 2*A );
+        this.b0 =    A*((A+1) + (A-1)*cosw0 + coeff);
+        this.b1 = -2*A*((A-1) + (A+1)*cosw0);
+        this.b2 =    A*((A+1) + (A-1)*cosw0 - coeff);
+        this.a0 =       (A+1) - (A-1)*cosw0 + coeff;
+        this.a1 =    2*((A-1) - (A+1)*cosw0);
+        this.a2 =       (A+1) - (A-1)*cosw0 - coeff;
+        break;
+    }
+
+    this.b0a0 = this.b0/this.a0;
+    this.b1a0 = this.b1/this.a0;
+    this.b2a0 = this.b2/this.a0;
+    this.a1a0 = this.a1/this.a0;
+    this.a2a0 = this.a2/this.a0;
+  };
+
+  this.process = function(buffer) {
+      //y[n] = (b0/a0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2]
+      //       - (a1/a0)*y[n-1] - (a2/a0)*y[n-2]
+
+      var len = buffer.length;
+      var output = new Float64Array(len);
+
+      for ( var i=0; i<buffer.length; i++ ) {
+        output[i] = this.b0a0*buffer[i] + this.b1a0*this.x_1_l + this.b2a0*this.x_2_l - this.a1a0*this.y_1_l - this.a2a0*this.y_2_l;
+        this.y_2_l = this.y_1_l;
+        this.y_1_l = output[i];
+        this.x_2_l = this.x_1_l;
+        this.x_1_l = buffer[i];
+      }
+
+      return output;
+  };
+
+  this.processStereo = function(buffer) {
+      //y[n] = (b0/a0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2]
+      //       - (a1/a0)*y[n-1] - (a2/a0)*y[n-2]
+
+      var len = buffer.length;
+      var output = new Float64Array(len);
+
+      for (var i = 0; i < len/2; i++) {
+        output[2*i] = this.b0a0*buffer[2*i] + this.b1a0*this.x_1_l + this.b2a0*this.x_2_l - this.a1a0*this.y_1_l - this.a2a0*this.y_2_l;
+        this.y_2_l = this.y_1_l;
+        this.y_1_l = output[2*i];
+        this.x_2_l = this.x_1_l;
+        this.x_1_l = buffer[2*i];
+
+        output[2*i+1] = this.b0a0*buffer[2*i+1] + this.b1a0*this.x_1_r + this.b2a0*this.x_2_r - this.a1a0*this.y_1_r - this.a2a0*this.y_2_r;
+        this.y_2_r = this.y_1_r;
+        this.y_1_r = output[2*i+1];
+        this.x_2_r = this.x_1_r;
+        this.x_1_r = buffer[2*i+1];
+      }
+
+      return output;
+  };
+}
+
+module.exports = Biquad;
diff --git a/lib/dft.js b/lib/dft.js
new file mode 100644
index 0000000..d00eaae
--- /dev/null
+++ b/lib/dft.js
@@ -0,0 +1,57 @@
+/* global Float64Array */
+var FourierTransform = require("./fourier");
+
+/**
+ * DFT is a class for calculating the Discrete Fourier Transform of a signal.
+ *
+ * @param {Number} bufferSize The size of the sample buffer to be computed
+ * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
+ *
+ * @constructor
+ */
+function DFT(bufferSize, sampleRate) {
+  FourierTransform.call(this, bufferSize, sampleRate);
+
+  var N = bufferSize/2 * bufferSize;
+  var TWO_PI = 2 * Math.PI;
+
+  this.sinTable = new Float64Array(N);
+  this.cosTable = new Float64Array(N);
+
+  for (var i = 0; i < N; i++) {
+    this.sinTable[i] = Math.sin(i * TWO_PI / bufferSize);
+    this.cosTable[i] = Math.cos(i * TWO_PI / bufferSize);
+  }
+}
+
+/**
+ * Performs a forward transform on the sample buffer.
+ * Converts a time domain signal to frequency domain spectra.
+ *
+ * @param {Array} buffer The sample buffer
+ *
+ * @returns The frequency spectrum array
+ */
+DFT.prototype.forward = function(buffer) {
+  var real = this.real,
+    imag = this.imag,
+    rval,
+    ival;
+
+  for (var k = 0; k < this.bufferSize/2; k++) {
+    rval = 0.0;
+    ival = 0.0;
+
+    for (var n = 0; n < buffer.length; n++) {
+      rval += this.cosTable[k*n] * buffer[n];
+      ival += this.sinTable[k*n] * buffer[n];
+    }
+
+    real[k] = rval;
+    imag[k] = ival;
+  }
+
+  return this.calculateSpectrum();
+};
+
+module.exports = DFT;
diff --git a/lib/dsp.js b/lib/dsp.js
new file mode 100644
index 0000000..44e05ac
--- /dev/null
+++ b/lib/dsp.js
@@ -0,0 +1,315 @@
+/* global Float64Array */
+
+////////////////////////////////////////////////////////////////////////////////
+//                                  CONSTANTS                                 //
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * DSP is an object which contains general purpose utility functions and constants
+ */
+var DSP = {
+  // Channels
+  LEFT:           0,
+  RIGHT:          1,
+  MIX:            2,
+
+  // Waveforms
+  SINE:           1,
+  TRIANGLE:       2,
+  SAW:            3,
+  SQUARE:         4,
+
+  // Filters
+  LOWPASS:        0,
+  HIGHPASS:       1,
+  BANDPASS:       2,
+  NOTCH:          3,
+
+  // Window functions
+  BARTLETT:       1,
+  BARTLETTHANN:   2,
+  BLACKMAN:       3,
+  COSINE:         4,
+  GAUSS:          5,
+  HAMMING:        6,
+  HANN:           7,
+  LANCZOS:        8,
+  RECTANGULAR:    9,
+  TRIANGULAR:     10,
+
+  // Loop modes
+  OFF:            0,
+  FW:             1,
+  BW:             2,
+  FWBW:           3,
+
+  // Math
+  TWO_PI:         2*Math.PI
+};
+
+// Setup arrays for platforms which do not support byte arrays
+function setupTypedArray(name, fallback) {
+  // check if TypedArray exists
+  // typeof on Minefield and Chrome return function, typeof on Webkit returns object.
+  if (typeof this[name] !== "function" && typeof this[name] !== "object") {
+    // nope.. check if WebGLArray exists
+    if (typeof this[fallback] === "function" && typeof this[fallback] !== "object") {
+      this[name] = this[fallback];
+    } else {
+      // nope.. set as Native JS array
+      this[name] = function(obj) {
+        if (obj instanceof Array) {
+          return obj;
+        } else if (typeof obj === "number") {
+          return new Array(obj);
+        }
+      };
+    }
+  }
+}
+
+setupTypedArray("Float64Array", "WebGLFloatArray");
+setupTypedArray("Int32Array",   "WebGLIntArray");
+setupTypedArray("Uint16Array",  "WebGLUnsignedShortArray");
+setupTypedArray("Uint8Array",   "WebGLUnsignedByteArray");
+
+
+////////////////////////////////////////////////////////////////////////////////
+//                            DSP UTILITY FUNCTIONS                           //
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Inverts the phase of a signal
+ *
+ * @param {Array} buffer A sample buffer
+ *
+ * @returns The inverted sample buffer
+ */
+DSP.invert = function(buffer) {
+  for (var i = 0, len = buffer.length; i < len; i++) {
+    buffer[i] *= -1;
+  }
+
+  return buffer;
+};
+
+/**
+ * Converts split-stereo (dual mono) sample buffers into a stereo interleaved sample buffer
+ *
+ * @param {Array} left  A sample buffer
+ * @param {Array} right A sample buffer
+ *
+ * @returns The stereo interleaved buffer
+ */
+DSP.interleave = function(left, right) {
+  if (left.length !== right.length) {
+    throw "Can not interleave. Channel lengths differ.";
+  }
+
+  var stereoInterleaved = new Float64Array(left.length * 2);
+
+  for (var i = 0, len = left.length; i < len; i++) {
+    stereoInterleaved[2*i]   = left[i];
+    stereoInterleaved[2*i+1] = right[i];
+  }
+
+  return stereoInterleaved;
+};
+
+/**
+ * Converts a stereo-interleaved sample buffer into split-stereo (dual mono) sample buffers
+ *
+ * @param {Array} buffer A stereo-interleaved sample buffer
+ *
+ * @returns an Array containing left and right channels
+ */
+DSP.deinterleave = (function() {
+  var left, right, mix, deinterleaveChannel = [];
+
+  deinterleaveChannel[DSP.MIX] = function(buffer) {
+    for (var i = 0, len = buffer.length/2; i < len; i++) {
+      mix[i] = (buffer[2*i] + buffer[2*i+1]) / 2;
+    }
+    return mix;
+  };
+
+  deinterleaveChannel[DSP.LEFT] = function(buffer) {
+    for (var i = 0, len = buffer.length/2; i < len; i++) {
+      left[i]  = buffer[2*i];
+    }
+    return left;
+  };
+
+  deinterleaveChannel[DSP.RIGHT] = function(buffer) {
+    for (var i = 0, len = buffer.length/2; i < len; i++) {
+      right[i]  = buffer[2*i+1];
+    }
+    return right;
+  };
+
+  return function(channel, buffer) {
+    left  = left  || new Float64Array(buffer.length/2);
+    right = right || new Float64Array(buffer.length/2);
+    mix   = mix   || new Float64Array(buffer.length/2);
+
+    if (buffer.length/2 !== left.length) {
+      left  = new Float64Array(buffer.length/2);
+      right = new Float64Array(buffer.length/2);
+      mix   = new Float64Array(buffer.length/2);
+    }
+
+    return deinterleaveChannel[channel](buffer);
+  };
+}());
+
+/**
+ * Separates a channel from a stereo-interleaved sample buffer
+ *
+ * @param {Array}  buffer A stereo-interleaved sample buffer
+ * @param {Number} channel A channel constant (LEFT, RIGHT, MIX)
+ *
+ * @returns an Array containing a signal mono sample buffer
+ */
+DSP.getChannel = DSP.deinterleave;
+
+/**
+ * Helper method (for Reverb) to mix two (interleaved) samplebuffers. It's possible
+ * to negate the second buffer while mixing and to perform a volume correction
+ * on the final signal.
+ *
+ * @param {Array} sampleBuffer1 Array containing Float values or a Float64Array
+ * @param {Array} sampleBuffer2 Array containing Float values or a Float64Array
+ * @param {Boolean} negate When true inverts/flips the audio signal
+ * @param {Number} volumeCorrection When you add multiple sample buffers, use this to tame your signal ;)
+ *
+ * @returns A new Float64Array interleaved buffer.
+ */
+DSP.mixSampleBuffers = function(sampleBuffer1, sampleBuffer2, negate, volumeCorrection){
+  var outputSamples = new Float64Array(sampleBuffer1);
+
+  for(var i = 0; i<sampleBuffer1.length; i++){
+    outputSamples[i] += (negate ? -sampleBuffer2[i] : sampleBuffer2[i]) / volumeCorrection;
+  }
+
+  return outputSamples;
+};
+
+// Biquad filter types
+DSP.LPF = 0;                // H(s) = 1 / (s^2 + s/Q + 1)
+DSP.HPF = 1;                // H(s) = s^2 / (s^2 + s/Q + 1)
+DSP.BPF_CONSTANT_SKIRT = 2; // H(s) = s / (s^2 + s/Q + 1)  (constant skirt gain, peak gain = Q)
+DSP.BPF_CONSTANT_PEAK = 3;  // H(s) = (s/Q) / (s^2 + s/Q + 1)      (constant 0 dB peak gain)
+DSP.NOTCH = 4;              // H(s) = (s^2 + 1) / (s^2 + s/Q + 1)
+DSP.APF = 5;                // H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1)
+DSP.PEAKING_EQ = 6;         // H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
+DSP.LOW_SHELF = 7;          // H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1)
+DSP.HIGH_SHELF = 8;         // H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A)
+
+// Biquad filter parameter types
+DSP.Q = 1;
+DSP.BW = 2; // SHARED with BACKWARDS LOOP MODE
+DSP.S = 3;
+
+/**
+ * Find RMS of signal
+ * @param {Array} buffer
+ */
+DSP.RMS = function(buffer) {
+  var total = 0;
+
+  for (var i = 0, n = buffer.length; i < n; i++) {
+    total += buffer[i] * buffer[i];
+  }
+
+  return Math.sqrt(total / n);
+};
+
+/**
+ * Find Peak of signal
+ * @param {Array} buffer
+ */
+DSP.Peak = function(buffer) {
+  var peak = 0;
+
+  for (var i = 0, n = buffer.length; i < n; i++) {
+    peak = (Math.abs(buffer[i]) > peak) ? Math.abs(buffer[i]) : peak;
+  }
+
+  return peak;
+};
+
+/**
+ * Magnitude to decibels
+ *
+ * Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
+ * Copyright 2010 Ricard Marxer. All rights reserved.
+ *
+ * @param {Array} @buffer The array of magnitudes to convert to decibels
+ * @returns the array in decibels
+ *
+ */
+DSP.mag2db = function(buffer) {
+  var minDb = -120;
+  var minMag = Math.pow(10.0, minDb / 20.0);
+
+  var log = Math.log;
+  var max = Math.max;
+
+  var result = new Float64Array(buffer.length);
+  for (var i=0; i<buffer.length; i++) {
+    result[i] = 20.0*log(max(buffer[i], minMag));
+  }
+
+  return result;
+};
+
+/**
+ *  Frequency response
+ *
+ *  Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
+ *  Copyright 2010 Ricard Marxer. All rights reserved.
+ *
+ *  Calculates the frequency response at the given points.
+ *
+ *  @param {Number} b The coefficients of the filter
+ *  @param {Number} a The coefficients of the filter
+ *  @param {Number} w The points (normally between -PI and PI) where to calculate the frequency response
+ *
+ *  @returns the frequency response in magnitude
+ */
+DSP.freqz = function(b, a, w) {
+  var i, j;
+
+  if (!w) {
+    w = new Float64Array(200);
+    for (i=0;i<w.length; i++) {
+      w[i] = DSP.TWO_PI/w.length * i - Math.PI;
+    }
+  }
+
+  var result = new Float64Array(w.length);
+
+  var sqrt = Math.sqrt;
+  var cos = Math.cos;
+  var sin = Math.sin;
+
+  for (i=0; i<w.length; i++) {
+    var numerator = {real:0.0, imag:0.0};
+    for (j=0; j<b.length; j++) {
+      numerator.real += b[j] * cos(-j*w[i]);
+      numerator.imag += b[j] * sin(-j*w[i]);
+    }
+
+    var denominator = {real:0.0, imag:0.0};
+    for (j=0; j<a.length; j++) {
+      denominator.real += a[j] * cos(-j*w[i]);
+      denominator.imag += a[j] * sin(-j*w[i]);
+    }
+
+    result[i] =  sqrt(numerator.real*numerator.real + numerator.imag*numerator.imag) / sqrt(denominator.real*denominator.real + denominator.imag*denominator.imag);
+  }
+
+  return result;
+};
+
+module.exports = DSP;
diff --git a/lib/fft.js b/lib/fft.js
new file mode 100644
index 0000000..97ec669
--- /dev/null
+++ b/lib/fft.js
@@ -0,0 +1,198 @@
+/* global Float64Array Uint32Array */
+var FourierTransform = require("./fourier");
+
+/**
+ * FFT is a class for calculating the Discrete Fourier Transform of a signal
+ * with the Fast Fourier Transform algorithm.
+ *
+ * @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2
+ * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
+ *
+ * @constructor
+ */
+function FFT(bufferSize, sampleRate) {
+  FourierTransform.call(this, bufferSize, sampleRate);
+
+  this.reverseTable = new Uint32Array(bufferSize);
+
+  var limit = 1;
+  var bit = bufferSize >> 1;
+
+  var i;
+
+  while (limit < bufferSize) {
+    for (i = 0; i < limit; i++) {
+      this.reverseTable[i + limit] = this.reverseTable[i] + bit;
+    }
+
+    limit = limit << 1;
+    bit = bit >> 1;
+  }
+
+  this.sinTable = new Float64Array(bufferSize);
+  this.cosTable = new Float64Array(bufferSize);
+
+  for (i = 0; i < bufferSize; i++) {
+    this.sinTable[i] = Math.sin(-Math.PI/i);
+    this.cosTable[i] = Math.cos(-Math.PI/i);
+  }
+}
+
+/**
+ * Performs a forward transform on the sample buffer.
+ * Converts a time domain signal to frequency domain spectra.
+ *
+ * @param {Array} buffer The sample buffer. Buffer Length must be power of 2
+ *
+ * @returns The frequency spectrum array
+ */
+FFT.prototype.forward = function(buffer) {
+  // Locally scope variables for speed up
+  var bufferSize      = this.bufferSize,
+      cosTable        = this.cosTable,
+      sinTable        = this.sinTable,
+      reverseTable    = this.reverseTable,
+      real            = this.real,
+      imag            = this.imag;
+
+  var k = Math.floor(Math.log(bufferSize) / Math.LN2);
+
+  if (Math.pow(2, k) !== bufferSize) { throw "Invalid buffer size, must be a power of 2."; }
+  if (bufferSize !== buffer.length)  { throw "Supplied buffer is not the same size as defined FFT. FFT Size: " + bufferSize + " Buffer Size: " + buffer.length; }
+
+  var halfSize = 1,
+    phaseShiftStepReal,
+    phaseShiftStepImag,
+    currentPhaseShiftReal,
+    currentPhaseShiftImag,
+    off,
+    tr,
+    ti,
+    tmpReal,
+    i;
+
+  for (i = 0; i < bufferSize; i++) {
+    real[i] = buffer[reverseTable[i]];
+    imag[i] = 0;
+  }
+
+  while (halfSize < bufferSize) {
+    //phaseShiftStepReal = Math.cos(-Math.PI/halfSize);
+    //phaseShiftStepImag = Math.sin(-Math.PI/halfSize);
+    phaseShiftStepReal = cosTable[halfSize];
+    phaseShiftStepImag = sinTable[halfSize];
+
+    currentPhaseShiftReal = 1;
+    currentPhaseShiftImag = 0;
+
+    for (var fftStep = 0; fftStep < halfSize; fftStep++) {
+      i = fftStep;
+
+      while (i < bufferSize) {
+        off = i + halfSize;
+        tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]);
+        ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]);
+
+        real[off] = real[i] - tr;
+        imag[off] = imag[i] - ti;
+        real[i] += tr;
+        imag[i] += ti;
+
+        i += halfSize << 1;
+      }
+
+      tmpReal = currentPhaseShiftReal;
+      currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag);
+      currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal);
+    }
+
+    halfSize = halfSize << 1;
+  }
+
+  return this.calculateSpectrum();
+};
+
+/**
+ * Performs a inverse FFT transformation
+ * Converts a frequency domain spectra to a time domain signal
+ *
+ * @param {Array} real
+ * @param {Array} imag
+ *
+ * @returns The time domain signal
+ */
+FFT.prototype.inverse = function(real, imag) {
+  // Locally scope variables for speed up
+  var bufferSize      = this.bufferSize,
+      cosTable        = this.cosTable,
+      sinTable        = this.sinTable,
+      reverseTable    = this.reverseTable;
+
+      real = real || this.real;
+      imag = imag || this.imag;
+
+  var halfSize = 1,
+      phaseShiftStepReal,
+      phaseShiftStepImag,
+      currentPhaseShiftReal,
+      currentPhaseShiftImag,
+      off,
+      tr,
+      ti,
+      tmpReal,
+      i;
+
+  for (i = 0; i < bufferSize; i++) {
+    imag[i] *= -1;
+  }
+
+  var revReal = new Float64Array(bufferSize);
+  var revImag = new Float64Array(bufferSize);
+
+  for (i = 0; i < real.length; i++) {
+    revReal[i] = real[reverseTable[i]];
+    revImag[i] = imag[reverseTable[i]];
+  }
+
+  real = revReal;
+  imag = revImag;
+
+  while (halfSize < bufferSize) {
+    phaseShiftStepReal = cosTable[halfSize];
+    phaseShiftStepImag = sinTable[halfSize];
+    currentPhaseShiftReal = 1;
+    currentPhaseShiftImag = 0;
+
+    for (var fftStep = 0; fftStep < halfSize; fftStep++) {
+      i = fftStep;
+
+      while (i < bufferSize) {
+        off = i + halfSize;
+        tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]);
+        ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]);
+
+        real[off] = real[i] - tr;
+        imag[off] = imag[i] - ti;
+        real[i] += tr;
+        imag[i] += ti;
+
+        i += halfSize << 1;
+      }
+
+      tmpReal = currentPhaseShiftReal;
+      currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag);
+      currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal);
+    }
+
+    halfSize = halfSize << 1;
+  }
+
+  var buffer = new Float64Array(bufferSize); // this should be reused instead
+  for (i = 0; i < bufferSize; i++) {
+    buffer[i] = real[i] / bufferSize;
+  }
+
+  return buffer;
+};
+
+module.exports = FFT;
diff --git a/lib/fourier.js b/lib/fourier.js
new file mode 100644
index 0000000..b47419c
--- /dev/null
+++ b/lib/fourier.js
@@ -0,0 +1,57 @@
+/* global Float64Array */
+
+/**
+ * A Mixin for every fourier module
+ * Fourier Transform Module used by DFT, FFT, RFFT
+ * @private
+ */
+module.exports = function FourierTransform(bufferSize, sampleRate) {
+  this.bufferSize = bufferSize;
+  this.sampleRate = sampleRate;
+  this.bandwidth  = 2 / bufferSize * sampleRate / 2;
+
+  this.spectrum   = new Float64Array(bufferSize/2);
+  this.real       = new Float64Array(bufferSize);
+  this.imag       = new Float64Array(bufferSize);
+
+  this.peakBand   = 0;
+  this.peak       = 0;
+
+  /**
+   * Calculates the *middle* frequency of an FFT band.
+   *
+   * @param {Number} index The index of the FFT band.
+   *
+   * @returns The middle frequency in Hz.
+   */
+  this.getBandFrequency = function(index) {
+    return this.bandwidth * index + this.bandwidth / 2;
+  };
+
+  /**
+   * Calculate the spectrum (amplitude magnitudes)
+   */
+  this.calculateSpectrum = function() {
+    var spectrum  = this.spectrum,
+      real      = this.real,
+      imag      = this.imag,
+      bSi       = 2 / this.bufferSize,
+      sqrt      = Math.sqrt,
+      rval,
+      ival,
+      mag;
+
+    for (var i = 0, N = bufferSize/2; i < N; i++) {
+      rval = real[i];
+      ival = imag[i];
+      mag = bSi * sqrt(rval * rval + ival * ival);
+
+      if (mag > this.peak) {
+        this.peakBand = i;
+        this.peak = mag;
+      }
+
+      spectrum[i] = mag;
+    }
+  };
+};
diff --git a/lib/graphical-eq.js b/lib/graphical-eq.js
new file mode 100644
index 0000000..61d3c0e
--- /dev/null
+++ b/lib/graphical-eq.js
@@ -0,0 +1,117 @@
+/* global Float64Array */
+var DSP = require("./dsp");
+var Biquad = require("./biquad");
+
+/**
+ *  Create a Graphical Equalizer
+ *
+ *  Implementation of a graphic equalizer with a configurable bands-per-octave
+ *  and minimum and maximum frequencies
+ *
+ *  Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
+ *  Copyright 2010 Ricard Marxer. All rights reserved.
+ *
+ * @constructor
+ * @param {SampleRate}
+ * @example
+ * var eq = new GraphicalEq(44100)
+ */
+function GraphicalEq(sampleRate) {
+  this.FS = sampleRate;
+  this.minFreq = 40.0;
+  this.maxFreq = 16000.0;
+
+  this.bandsPerOctave = 1.0;
+
+  this.filters = [];
+  this.freqzs = [];
+
+  this.calculateFreqzs = true;
+
+  this.recalculateFilters = function() {
+    var bandCount = Math.round(Math.log(this.maxFreq/this.minFreq) * this.bandsPerOctave/ Math.LN2);
+
+    this.filters = [];
+    for (var i=0; i<bandCount; i++) {
+      var freq = this.minFreq*(Math.pow(2, i/this.bandsPerOctave));
+      var newFilter = new Biquad(DSP.PEAKING_EQ, this.FS);
+      newFilter.setDbGain(0);
+      newFilter.setBW(1/this.bandsPerOctave);
+      newFilter.setF0(freq);
+      this.filters[i] = newFilter;
+      this.recalculateFreqz(i);
+    }
+  };
+
+  this.setMinimumFrequency = function(freq) {
+    this.minFreq = freq;
+    this.recalculateFilters();
+  };
+
+  this.setMaximumFrequency = function(freq) {
+    this.maxFreq = freq;
+    this.recalculateFilters();
+  };
+
+  this.setBandsPerOctave = function(bands) {
+    this.bandsPerOctave = bands;
+    this.recalculateFilters();
+  };
+
+  this.setBandGain = function(bandIndex, gain) {
+    if (bandIndex < 0 || bandIndex > (this.filters.length-1)) {
+      throw "The band index of the graphical equalizer is out of bounds.";
+    }
+
+    if (!gain) {
+      throw "A gain must be passed.";
+    }
+
+    this.filters[bandIndex].setDbGain(gain);
+    this.recalculateFreqz(bandIndex);
+  };
+
+  this.recalculateFreqz = function(bandIndex) {
+    if (!this.calculateFreqzs) {
+      return;
+    }
+
+    if (bandIndex < 0 || bandIndex > (this.filters.length-1)) {
+      throw "The band index of the graphical equalizer is out of bounds. " + bandIndex + " is out of [" + 0 + ", " + this.filters.length-1 + "]";
+    }
+
+    if (!this.w) {
+      this.w = new Float64Array(400);
+      for (var i=0; i<this.w.length; i++) {
+         this.w[i] = Math.PI/this.w.length * i;
+      }
+    }
+
+    var b = [this.filters[bandIndex].b0, this.filters[bandIndex].b1, this.filters[bandIndex].b2];
+    var a = [this.filters[bandIndex].a0, this.filters[bandIndex].a1, this.filters[bandIndex].a2];
+
+    this.freqzs[bandIndex] = DSP.mag2db(DSP.freqz(b, a, this.w));
+  };
+
+  this.process = function(buffer) {
+    var output = buffer;
+
+    for (var i = 0; i < this.filters.length; i++) {
+      output = this.filters[i].process(output);
+    }
+
+    return output;
+  };
+
+  this.processStereo = function(buffer) {
+    var output = buffer;
+
+    for (var i = 0; i < this.filters.length; i++) {
+      output = this.filters[i].processStereo(output);
+    }
+
+    return output;
+  };
+}
+
+module.exports = GraphicalEq;
diff --git a/lib/iir-filter.js b/lib/iir-filter.js
new file mode 100644
index 0000000..ba0e010
--- /dev/null
+++ b/lib/iir-filter.js
@@ -0,0 +1,120 @@
+var DSP = require("./dsp");
+var ADSR = require("./adsr");
+
+/**
+ * IIRFilter
+ * @constructor
+ */
+function IIRFilter(type, cutoff, resonance, sampleRate) {
+  this.sampleRate = sampleRate;
+
+  switch(type) {
+    case DSP.LOWPASS:
+    case DSP.LP12:
+      this.func = new IIRFilter.LP12(cutoff, resonance, sampleRate);
+      break;
+  }
+}
+
+IIRFilter.prototype.__defineGetter__("cutoff",
+  function() {
+    return this.func.cutoff;
+  }
+);
+
+IIRFilter.prototype.__defineGetter__("resonance",
+  function() {
+    return this.func.resonance;
+  }
+);
+
+/**
+ * Set filter parameters
+ * @param {Number} cutoff
+ * @param {Number} resonance
+ */
+IIRFilter.prototype.set = function(cutoff, resonance) {
+  this.func.calcCoeff(cutoff, resonance);
+};
+
+/**
+ * Process a buffer
+ * @param {Array} buffer
+ */
+IIRFilter.prototype.process = function(buffer) {
+  this.func.process(buffer);
+};
+
+/**
+ * Add an envelope to the filter
+ * @param {ADSR} envelope
+ */
+IIRFilter.prototype.addEnvelope = function(envelope) {
+  if ( envelope instanceof ADSR ) {
+    this.func.addEnvelope(envelope);
+  } else {
+    throw "Not an envelope.";
+  }
+};
+
+/**
+ * LP12 filter
+ * @constructor
+ */
+IIRFilter.LP12 = function(cutoff, resonance, sampleRate) {
+  this.sampleRate = sampleRate;
+  this.vibraPos   = 0;
+  this.vibraSpeed = 0;
+  this.envelope = false;
+
+  this.calcCoeff = function(cutoff, resonance) {
+    this.w = 2.0 * Math.PI * cutoff / this.sampleRate;
+    this.q = 1.0 - this.w / (2.0 * (resonance + 0.5 / (1.0 + this.w)) + this.w - 2.0);
+    this.r = this.q * this.q;
+    this.c = this.r + 1.0 - 2.0 * Math.cos(this.w) * this.q;
+
+    this.cutoff = cutoff;
+    this.resonance = resonance;
+  };
+
+  this.calcCoeff(cutoff, resonance);
+
+  this.process = function(buffer) {
+    for ( var i = 0; i < buffer.length; i++ ) {
+      this.vibraSpeed += (buffer[i] - this.vibraPos) * this.c;
+      this.vibraPos   += this.vibraSpeed;
+      this.vibraSpeed *= this.r;
+
+      /*
+      var temp = this.vibraPos;
+
+      if ( temp > 1.0 ) {
+        temp = 1.0;
+      } else if ( temp < -1.0 ) {
+        temp = -1.0;
+      } else if ( temp != temp ) {
+        temp = 1;
+      }
+
+      buffer[i] = temp;
+      */
+
+      if (this.envelope) {
+        buffer[i] = (buffer[i] * (1 - this.envelope.value())) + (this.vibraPos * this.envelope.value());
+        this.envelope.samplesProcessed++;
+      } else {
+        buffer[i] = this.vibraPos;
+      }
+    }
+  };
+};
+
+/**
+ * Add an envelope to the filter
+ * @param {ADSR} envelope
+ */
+IIRFilter.LP12.prototype.addEnvelope = function(envelope) {
+  this.envelope = envelope;
+};
+
+module.exports = IIRFilter;
diff --git a/lib/iir-filter2.js b/lib/iir-filter2.js
new file mode 100644
index 0000000..410ba92
--- /dev/null
+++ b/lib/iir-filter2.js
@@ -0,0 +1,88 @@
+/* global Float64Array */
+var ADSR = require("./adsr");
+
+/**
+ * IIRFilter2
+ *
+ * @constructor
+ * @param {Number} type
+ * @param {Number} cutoff
+ * @param {Number} resonance
+ * @param {Number} sampleRate
+ */
+function IIRFilter2(type, cutoff, resonance, sampleRate) {
+  this.type = type;
+  this.cutoff = cutoff;
+  this.resonance = resonance;
+  this.sampleRate = sampleRate;
+
+  this.f = new Float64Array(4);
+  this.f[0] = 0.0; // lp
+  this.f[1] = 0.0; // hp
+  this.f[2] = 0.0; // bp
+  this.f[3] = 0.0; // br
+
+  this.calcCoeff = function(cutoff, resonance) {
+    this.freq = 2 * Math.sin(Math.PI * Math.min(0.25, cutoff/(this.sampleRate*2)));
+    this.damp = Math.min(2 * (1 - Math.pow(resonance, 0.25)), Math.min(2, 2/this.freq - this.freq * 0.5));
+  };
+
+  this.calcCoeff(cutoff, resonance);
+}
+
+/**
+ * Process a buffer
+ * @param {Array} buffer
+ */
+IIRFilter2.prototype.process = function(buffer) {
+  var input, output;
+  var f = this.f;
+
+  for ( var i = 0; i < buffer.length; i++ ) {
+    input = buffer[i];
+
+    // first pass
+    f[3] = input - this.damp * f[2];
+    f[0] = f[0] + this.freq * f[2];
+    f[1] = f[3] - f[0];
+    f[2] = this.freq * f[1] + f[2];
+    output = 0.5 * f[this.type];
+
+    // second pass
+    f[3] = input - this.damp * f[2];
+    f[0] = f[0] + this.freq * f[2];
+    f[1] = f[3] - f[0];
+    f[2] = this.freq * f[1] + f[2];
+    output += 0.5 * f[this.type];
+
+    if (this.envelope) {
+      buffer[i] = (buffer[i] * (1 - this.envelope.value())) + (output * this.envelope.value());
+      this.envelope.samplesProcessed++;
+    } else {
+      buffer[i] = output;
+    }
+  }
+};
+
+/**
+ * Add an envelope to the filter
+ * @param {ADSR} envelope
+ */
+IIRFilter2.prototype.addEnvelope = function(envelope) {
+  if ( envelope instanceof ADSR ) {
+    this.envelope = envelope;
+  } else {
+    throw "This is not an envelope.";
+  }
+};
+
+/**
+ * Set filter parameters
+ * @param {Number} cutoff
+ * @param {Number} resonance
+ */
+IIRFilter2.prototype.set = function(cutoff, resonance) {
+  this.calcCoeff(cutoff, resonance);
+};
+
+module.exports = IIRFilter2;
diff --git a/lib/index.js b/lib/index.js
new file mode 100644
index 0000000..762475d
--- /dev/null
+++ b/lib/index.js
@@ -0,0 +1,34 @@
+"use strict";
+
+/*
+ *  DSP.js - a comprehensive digital signal processing  library for javascript
+ *
+ *  Created by Corban Brook <corbanbrook@gmail.com> on 2010-01-01.
+ *  Copyright 2010 Corban Brook. All rights reserved.
+ *
+ */
+var DSPJS = {
+  DSP: require("./dsp"),
+  DFT: require("./dft"),
+  FFT: require("./fft"),
+  RFFT: require("./rfft"),
+  Sampler: require("./sampler"),
+  Oscillator: require("./oscillator"),
+  ADSR: require("./adsr"),
+  IIRFilter: require("./iir-filter"),
+  IIRFilter2: require("./iir-filter2"),
+  WindowFunction: require("./window-function"),
+  sinh: require("./sinh"),
+  Biquad: require("./biquad"),
+  GraphicalEq: require("./graphical-eq"),
+  MultiDelay: require("./multi-delay"),
+  SingleDelay: require("./single-delay"),
+  Reverb: require("./reverb")
+};
+
+if (typeof module === "object" && module.exports) module.exports = DSPJS;
+if (typeof window !== "undefined") {
+  Object.keys(DSPJS).forEach(function (k) {
+    window[k] = DSPJS[k];
+  });
+}
diff --git a/lib/multi-delay.js b/lib/multi-delay.js
new file mode 100644
index 0000000..33755de
--- /dev/null
+++ b/lib/multi-delay.js
@@ -0,0 +1,102 @@
+/* global Float64Array */
+/**
+ * MultiDelay effect by Almer Thie (http://code.almeros.com).
+ * Copyright 2010 Almer Thie. All rights reserved.
+ * Example: http://code.almeros.com/code-examples/delay-firefox-audio-api/
+ *
+ * This is a delay that feeds it's own delayed signal back into its circular
+ * buffer. Also known as a CombFilter.
+ *
+ * Compatible with interleaved stereo (or more channel) buffers and
+ * non-interleaved mono buffers.
+ *
+ * @param {Number} maxDelayInSamplesSize Maximum possible delay in samples (size of circular buffer)
+ * @param {Number} delayInSamples Initial delay in samples
+ * @param {Number} masterVolume Initial master volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ * @param {Number} delayVolume Initial feedback delay volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ *
+ * @constructor
+ */
+function MultiDelay(maxDelayInSamplesSize, delayInSamples, masterVolume, delayVolume) {
+  this.delayBufferSamples   = new Float64Array(maxDelayInSamplesSize); // The maximum size of delay
+  this.delayInputPointer     = delayInSamples;
+  this.delayOutputPointer   = 0;
+
+  this.delayInSamples   = delayInSamples;
+  this.masterVolume     = masterVolume;
+  this.delayVolume     = delayVolume;
+}
+
+/**
+ * Change the delay time in samples.
+ *
+ * @param {Number} delayInSamples Delay in samples
+ */
+MultiDelay.prototype.setDelayInSamples = function (delayInSamples) {
+  this.delayInSamples = delayInSamples;
+
+  this.delayInputPointer = this.delayOutputPointer + delayInSamples;
+
+  if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
+    this.delayInputPointer = this.delayInputPointer - this.delayBufferSamples.length;
+  }
+};
+
+/**
+ * Change the master volume.
+ *
+ * @param {Number} masterVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ */
+MultiDelay.prototype.setMasterVolume = function(masterVolume) {
+  this.masterVolume = masterVolume;
+};
+
+/**
+ * Change the delay feedback volume.
+ *
+ * @param {Number} delayVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ */
+MultiDelay.prototype.setDelayVolume = function(delayVolume) {
+  this.delayVolume = delayVolume;
+};
+
+/**
+ * Process a given interleaved or mono non-interleaved float value Array and adds the delayed audio.
+ *
+ * @param {Array} samples Array containing Float values or a Float64Array
+ *
+ * @returns A new Float64Array interleaved or mono non-interleaved as was fed to this function.
+ */
+MultiDelay.prototype.process = function(samples) {
+  // NB. Make a copy to put in the output samples to return.
+  var outputSamples = new Float64Array(samples.length);
+
+  for (var i=0; i<samples.length; i++) {
+    // delayBufferSamples could contain initial NULL's, return silence in that case
+    var delaySample = (this.delayBufferSamples[this.delayOutputPointer] === null ? 0.0 : this.delayBufferSamples[this.delayOutputPointer]);
+
+    // Mix normal audio data with delayed audio
+    var sample = (delaySample * this.delayVolume) + samples[i];
+
+    // Add audio data with the delay in the delay buffer
+    this.delayBufferSamples[this.delayInputPointer] = sample;
+
+    // Return the audio with delay mix
+    outputSamples[i] = sample * this.masterVolume;
+
+    // Manage circulair delay buffer pointers
+    this.delayInputPointer++;
+    if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
+      this.delayInputPointer = 0;
+    }
+
+    this.delayOutputPointer++;
+    if (this.delayOutputPointer >= this.delayBufferSamples.length-1) {
+      this.delayOutputPointer = 0;
+    }
+  }
+
+  return outputSamples;
+};
+
+module.exports = MultiDelay;
diff --git a/lib/oscillator.js b/lib/oscillator.js
new file mode 100644
index 0000000..e193932
--- /dev/null
+++ b/lib/oscillator.js
@@ -0,0 +1,195 @@
+/* global Float64Array */
+var DSP = require("./dsp");
+
+/**
+ * Oscillator class for generating and modifying signals
+ *
+ * @param {Number} type       A waveform constant (eg. DSP.SINE)
+ * @param {Number} frequency  Initial frequency of the signal
+ * @param {Number} amplitude  Initial amplitude of the signal
+ * @param {Number} bufferSize Size of the sample buffer to generate
+ * @param {Number} sampleRate The sample rate of the signal
+ *
+ * @constructor
+ */
+function Oscillator(type, frequency, amplitude, bufferSize, sampleRate) {
+  this.frequency  = frequency;
+  this.amplitude  = amplitude;
+  this.bufferSize = bufferSize;
+  this.sampleRate = sampleRate;
+  //this.pulseWidth = pulseWidth;
+  this.frameCount = 0;
+
+  this.waveTableLength = 2048;
+
+  this.cyclesPerSample = frequency / sampleRate;
+
+  this.signal = new Float64Array(bufferSize);
+  this.envelope = null;
+
+  switch(parseInt(type, 10)) {
+    case DSP.TRIANGLE:
+      this.func = Oscillator.Triangle;
+      break;
+
+    case DSP.SAW:
+      this.func = Oscillator.Saw;
+      break;
+
+    case DSP.SQUARE:
+      this.func = Oscillator.Square;
+      break;
+
+    default:
+    case DSP.SINE:
+      this.func = Oscillator.Sine;
+      break;
+  }
+
+  this.generateWaveTable = function() {
+    Oscillator.waveTable[this.func] = new Float64Array(2048);
+    var waveTableTime = this.waveTableLength / this.sampleRate;
+    var waveTableHz = 1 / waveTableTime;
+
+    for (var i = 0; i < this.waveTableLength; i++) {
+      Oscillator.waveTable[this.func][i] = this.func(i * waveTableHz/this.sampleRate);
+    }
+  };
+
+  if ( typeof Oscillator.waveTable === "undefined" ) {
+    Oscillator.waveTable = {};
+  }
+
+  if ( typeof Oscillator.waveTable[this.func] === "undefined" ) {
+    this.generateWaveTable();
+  }
+
+  this.waveTable = Oscillator.waveTable[this.func];
+}
+
+/**
+ * Set the amplitude of the signal
+ *
+ * @param {Number} amplitude The amplitude of the signal (between 0 and 1)
+ */
+Oscillator.prototype.setAmp = function(amplitude) {
+  if (amplitude >= 0 && amplitude <= 1) {
+    this.amplitude = amplitude;
+  } else {
+    throw "Amplitude out of range (0..1).";
+  }
+};
+
+/**
+ * Set the frequency of the signal
+ *
+ * @param {Number} frequency The frequency of the signal
+ */
+Oscillator.prototype.setFreq = function(frequency) {
+  this.frequency = frequency;
+  this.cyclesPerSample = frequency / this.sampleRate;
+};
+
+/**
+ * Add an oscillator
+ * @param {Oscillator} oscillator The oscillator to be added to
+ * @return {Array} the current oscillator signal
+ */
+Oscillator.prototype.add = function(oscillator) {
+  for ( var i = 0; i < this.bufferSize; i++ ) {
+    //this.signal[i] += oscillator.valueAt(i);
+    this.signal[i] += oscillator.signal[i];
+  }
+
+  return this.signal;
+};
+
+/**
+ * Add a signal to the current generated osc signal
+ * @param {Array} signal
+ */
+Oscillator.prototype.addSignal = function(signal) {
+  for ( var i = 0; i < signal.length; i++ ) {
+    if ( i >= this.bufferSize ) {
+      break;
+    }
+    this.signal[i] += signal[i];
+
+    /*
+    // Constrain amplitude
+    if ( this.signal[i] > 1 ) {
+      this.signal[i] = 1;
+    } else if ( this.signal[i] < -1 ) {
+      this.signal[i] = -1;
+    }
+    */
+  }
+  return this.signal;
+};
+
+/**
+ * Add an envelope to the oscillator
+ * @param {ADSR} envelope
+ */
+Oscillator.prototype.addEnvelope = function(envelope) {
+  this.envelope = envelope;
+};
+
+/**
+ * Apply the oscillator envelope to its signal
+ */
+Oscillator.prototype.applyEnvelope = function() {
+  this.envelope.process(this.signal);
+};
+
+/**
+ * Get value
+ * @param {Number} offset
+ */
+Oscillator.prototype.valueAt = function(offset) {
+  return this.waveTable[offset % this.waveTableLength];
+};
+
+/**
+ * Generate the oscillator signal
+ * @return {Array} the signal
+ */
+Oscillator.prototype.generate = function() {
+  var frameOffset = this.frameCount * this.bufferSize;
+  var step = this.waveTableLength * this.frequency / this.sampleRate;
+  var offset;
+
+  for ( var i = 0; i < this.bufferSize; i++ ) {
+    //var step = (frameOffset + i) * this.cyclesPerSample % 1;
+    //this.signal[i] = this.func(step) * this.amplitude;
+    //this.signal[i] = this.valueAt(Math.round((frameOffset + i) * step)) * this.amplitude;
+    offset = Math.round((frameOffset + i) * step);
+    this.signal[i] = this.waveTable[offset % this.waveTableLength] * this.amplitude;
+  }
+
+  this.frameCount++;
+
+  return this.signal;
+};
+
+Oscillator.Sine = function(step) {
+  return Math.sin(DSP.TWO_PI * step);
+};
+
+Oscillator.Square = function(step) {
+  return step < 0.5 ? 1 : -1;
+};
+
+Oscillator.Saw = function(step) {
+  return 2 * (step - Math.round(step));
+};
+
+Oscillator.Triangle = function(step) {
+  return 1 - 4 * Math.abs(Math.round(step) - step);
+};
+
+Oscillator.Pulse = function() {
+  // stub
+};
+
+module.exports = Oscillator;
diff --git a/lib/reverb.js b/lib/reverb.js
new file mode 100644
index 0000000..8fc958c
--- /dev/null
+++ b/lib/reverb.js
@@ -0,0 +1,174 @@
+/* global Float64Array */
+var DSP = require("./dsp");
+var IIRFilter2 = require("./iir-filter2");
+var SingleDelay = require("./single-delay");
+var MultiDelay = require("./multi-delay");
+
+/**
+ * Reverb effect by Almer Thie (http://code.almeros.com).
+ * Copyright 2010 Almer Thie. All rights reserved.
+ * Example: http://code.almeros.com/code-examples/reverb-firefox-audio-api/
+ *
+ * This reverb consists of 6 SingleDelays, 6 MultiDelays and an IIRFilter2
+ * for each of the two stereo channels.
+ *
+ * Compatible with interleaved stereo buffers only!
+ *
+ * @param {Number} maxDelayInSamplesSize Maximum possible delay in samples (size of circular buffers)
+ * @param {Number} delayInSamples Initial delay in samples for internal (Single/Multi)delays
+ * @param {Number} masterVolume Initial master volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ * @param {Number} mixVolume Initial reverb signal mix volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ * @param {Number} delayVolume Initial feedback delay volume for internal (Single/Multi)delays. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ * @param {Number} dampFrequency Initial low pass filter frequency. 0 to 44100 (depending on your maximum sampling frequency)
+ *
+ * @constructor
+ */
+function Reverb(maxDelayInSamplesSize, delayInSamples, masterVolume, mixVolume, delayVolume, dampFrequency) {
+  this.delayInSamples   = delayInSamples;
+  this.masterVolume     = masterVolume;
+  this.mixVolume       = mixVolume;
+  this.delayVolume     = delayVolume;
+  this.dampFrequency     = dampFrequency;
+
+  this.NR_OF_MULTIDELAYS = 6;
+  this.NR_OF_SINGLEDELAYS = 6;
+
+  this.LOWPASSL = new IIRFilter2(DSP.LOWPASS, dampFrequency, 0, 44100);
+  this.LOWPASSR = new IIRFilter2(DSP.LOWPASS, dampFrequency, 0, 44100);
+
+  this.singleDelays = [];
+
+  var i, delayMultiply;
+
+  for (i = 0; i < this.NR_OF_SINGLEDELAYS; i++) {
+    delayMultiply = 1.0 + (i/7.0); // 1.0, 1.1, 1.2...
+    this.singleDelays[i] = new SingleDelay(maxDelayInSamplesSize, Math.round(this.delayInSamples * delayMultiply), this.delayVolume);
+  }
+
+  this.multiDelays = [];
+
+  for (i = 0; i < this.NR_OF_MULTIDELAYS; i++) {
+    delayMultiply = 1.0 + (i/10.0); // 1.0, 1.1, 1.2...
+    this.multiDelays[i] = new MultiDelay(maxDelayInSamplesSize, Math.round(this.delayInSamples * delayMultiply), this.masterVolume, this.delayVolume);
+  }
+}
+
+/**
+ * Change the delay time in samples as a base for all delays.
+ *
+ * @param {Number} delayInSamples Delay in samples
+ */
+Reverb.prototype.setDelayInSamples = function (delayInSamples){
+  this.delayInSamples = delayInSamples;
+
+  var i, delayMultiply;
+
+  for (i = 0; i < this.NR_OF_SINGLEDELAYS; i++) {
+    delayMultiply = 1.0 + (i/7.0); // 1.0, 1.1, 1.2...
+    this.singleDelays[i].setDelayInSamples( Math.round(this.delayInSamples * delayMultiply) );
+  }
+
+  for (i = 0; i < this.NR_OF_MULTIDELAYS; i++) {
+    delayMultiply = 1.0 + (i/10.0); // 1.0, 1.1, 1.2...
+    this.multiDelays[i].setDelayInSamples( Math.round(this.delayInSamples * delayMultiply) );
+  }
+};
+
+/**
+ * Change the master volume.
+ *
+ * @param {Number} masterVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ */
+Reverb.prototype.setMasterVolume = function (masterVolume){
+  this.masterVolume = masterVolume;
+};
+
+/**
+ * Change the reverb signal mix level.
+ *
+ * @param {Number} mixVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ */
+Reverb.prototype.setMixVolume = function (mixVolume){
+  this.mixVolume = mixVolume;
+};
+
+/**
+ * Change all delays feedback volume.
+ *
+ * @param {Number} delayVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ */
+Reverb.prototype.setDelayVolume = function (delayVolume){
+  this.delayVolume = delayVolume;
+
+  var i;
+
+  for (i = 0; i<this.NR_OF_SINGLEDELAYS; i++) {
+    this.singleDelays[i].setDelayVolume(this.delayVolume);
+  }
+
+  for (i = 0; i<this.NR_OF_MULTIDELAYS; i++) {
+    this.multiDelays[i].setDelayVolume(this.delayVolume);
+  }
+};
+
+/**
+ * Change the Low Pass filter frequency.
+ *
+ * @param {Number} dampFrequency low pass filter frequency. 0 to 44100 (depending on your maximum sampling frequency)
+ */
+Reverb.prototype.setDampFrequency = function (dampFrequency){
+  this.dampFrequency = dampFrequency;
+
+  this.LOWPASSL.set(dampFrequency, 0);
+  this.LOWPASSR.set(dampFrequency, 0);
+};
+
+/**
+ * Process a given interleaved float value Array and copies and adds the reverb signal.
+ *
+ * @param {Array} samples Array containing Float values or a Float64Array
+ *
+ * @returns A new Float64Array interleaved buffer.
+ */
+Reverb.prototype.process = function (interleavedSamples){
+  // NB. Make a copy to put in the output samples to return.
+  var outputSamples = new Float64Array(interleavedSamples.length);
+
+  // Perform low pass on the input samples to mimick damp
+  var leftRightMix = DSP.deinterleave(interleavedSamples);
+  this.LOWPASSL.process( leftRightMix[DSP.LEFT] );
+  this.LOWPASSR.process( leftRightMix[DSP.RIGHT] );
+  var filteredSamples = DSP.interleave(leftRightMix[DSP.LEFT], leftRightMix[DSP.RIGHT]);
+
+  var i;
+
+  // Process MultiDelays in parallel
+  for (i = 0; i<this.NR_OF_MULTIDELAYS; i++) {
+    // Invert the signal of every even multiDelay
+    outputSamples = DSP.mixSampleBuffers(outputSamples, this.multiDelays[i].process(filteredSamples), 2%i === 0, this.NR_OF_MULTIDELAYS);
+  }
+
+  // Process SingleDelays in series
+  var singleDelaySamples = new Float64Array(outputSamples.length);
+  for (i = 0; i<this.NR_OF_SINGLEDELAYS; i++) {
+    // Invert the signal of every even singleDelay
+    singleDelaySamples = DSP.mixSampleBuffers(singleDelaySamples, this.singleDelays[i].process(outputSamples), 2%i === 0, 1);
+  }
+
+  // Apply the volume of the reverb signal
+  for (i = 0; i<singleDelaySamples.length; i++) {
+    singleDelaySamples[i] *= this.mixVolume;
+  }
+
+  // Mix the original signal with the reverb signal
+  outputSamples = DSP.mixSampleBuffers(singleDelaySamples, interleavedSamples, 0, 1);
+
+  // Apply the master volume to the complete signal
+  for (i = 0; i<outputSamples.length; i++) {
+    outputSamples[i] *= this.masterVolume;
+  }
+
+  return outputSamples;
+};
+
+module.exports = Reverb;
diff --git a/lib/rfft.js b/lib/rfft.js
new file mode 100644
index 0000000..52ebbdd
--- /dev/null
+++ b/lib/rfft.js
@@ -0,0 +1,312 @@
+/* global Float64Array Uint32Array */
+var FourierTransform = require("./fourier");
+
+/**
+ * RFFT is a class for calculating the Discrete Fourier Transform of a signal
+ * with the Fast Fourier Transform algorithm.
+ *
+ * This method currently only contains a forward transform but is highly optimized.
+ *
+ * @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2
+ * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
+ *
+ * @constructor
+ */
+
+// lookup tables don't really gain us any speed, but they do increase
+// cache footprint, so don't use them in here
+
+// also we don't use sepearate arrays for real/imaginary parts
+
+// this one a little more than twice as fast as the one in FFT
+// however I only did the forward transform
+
+// the rest of this was translated from C, see http://www.jjj.de/fxt/
+// this is the real split radix FFT
+
+function RFFT(bufferSize, sampleRate) {
+  FourierTransform.call(this, bufferSize, sampleRate);
+
+  this.trans = new Float64Array(bufferSize);
+
+  this.reverseTable = new Uint32Array(bufferSize);
+
+  // don't use a lookup table to do the permute, use this instead
+  this.reverseBinPermute = function (dest, source) {
+    var bufferSize  = this.bufferSize,
+        halfSize    = bufferSize >>> 1,
+        nm1         = bufferSize - 1,
+        i = 1, r = 0, h;
+
+    dest[0] = source[0];
+
+    do {
+      r += halfSize;
+      dest[i] = source[r];
+      dest[r] = source[i];
+
+      i++;
+
+      h = halfSize << 1;
+      while (h = h >> 1, !((r ^= h) & h));
+
+      if (r >= i) {
+        dest[i]     = source[r];
+        dest[r]     = source[i];
+
+        dest[nm1-i] = source[nm1-r];
+        dest[nm1-r] = source[nm1-i];
+      }
+      i++;
+    } while (i < halfSize);
+    dest[nm1] = source[nm1];
+  };
+
+  this.generateReverseTable = function () {
+    var bufferSize  = this.bufferSize,
+        halfSize    = bufferSize >>> 1,
+        nm1         = bufferSize - 1,
+        i = 1, r = 0, h;
+
+    this.reverseTable[0] = 0;
+
+    do {
+      r += halfSize;
+
+      this.reverseTable[i] = r;
+      this.reverseTable[r] = i;
+
+      i++;
+
+      h = halfSize << 1;
+      while (h = h >> 1, !((r ^= h) & h));
+
+      if (r >= i) {
+        this.reverseTable[i] = r;
+        this.reverseTable[r] = i;
+
+        this.reverseTable[nm1-i] = nm1-r;
+        this.reverseTable[nm1-r] = nm1-i;
+      }
+      i++;
+    } while (i < halfSize);
+
+    this.reverseTable[nm1] = nm1;
+  };
+
+  this.generateReverseTable();
+}
+
+
+/**
+ * Performs a forward transform on the sample buffer.
+ * Converts a time domain signal to frequency domain spectra.
+ *
+ * @param {Array} buffer The sample buffer. Buffer Length must be power of 2
+ *
+ * @returns The frequency spectrum array
+ */
+
+// Ordering of output:
+//
+// trans[0]     = re[0] (==zero frequency, purely real)
+// trans[1]     = re[1]
+//             ...
+// trans[n/2-1] = re[n/2-1]
+// trans[n/2]   = re[n/2]    (==nyquist frequency, purely real)
+//
+// trans[n/2+1] = im[n/2-1]
+// trans[n/2+2] = im[n/2-2]
+//             ...
+// trans[n-1]   = im[1]
+
+RFFT.prototype.forward = function(buffer) {
+  var n         = this.bufferSize,
+      spectrum  = this.spectrum,
+      x         = this.trans,
+      TWO_PI    = 2*Math.PI,
+      sqrt      = Math.sqrt,
+      i         = n >>> 1,
+      bSi       = 2 / n,
+      n2, n4, n8, nn,
+      t1, t2, t3, t4,
+      i1, i2, i3, i4, i5, i6, i7, i8,
+      st1, cc1, ss1, cc3, ss3,
+      e,
+      a,
+      rval, ival, mag;
+
+  this.reverseBinPermute(x, buffer);
+
+  /*
+  var reverseTable = this.reverseTable;
+
+  for (var k = 0, len = reverseTable.length; k < len; k++) {
+    x[k] = buffer[reverseTable[k]];
+  }
+  */
+
+  for (var ix = 0, id = 4; ix < n; id *= 4) {
+    for (var i0 = ix; i0 < n; i0 += id) {
+      //sumdiff(x[i0], x[i0+1]); // {a, b}  <--| {a+b, a-b}
+      st1 = x[i0] - x[i0+1];
+      x[i0] += x[i0+1];
+      x[i0+1] = st1;
+    }
+    ix = 2*(id-1);
+  }
+
+  n2 = 2;
+  nn = n >>> 1;
+
+  while((nn = nn >>> 1)) {
+    ix = 0;
+    n2 = n2 << 1;
+    id = n2 << 1;
+    n4 = n2 >>> 2;
+    n8 = n2 >>> 3;
+    do {
+      if(n4 !== 1) {
+        for(i0 = ix; i0 < n; i0 += id) {
+          i1 = i0;
+          i2 = i1 + n4;
+          i3 = i2 + n4;
+          i4 = i3 + n4;
+
+          //diffsum3_r(x[i3], x[i4], t1); // {a, b, s} <--| {a, b-a, a+b}
+          t1 = x[i3] + x[i4];
+          x[i4] -= x[i3];
+          //sumdiff3(x[i1], t1, x[i3]);   // {a, b, d} <--| {a+b, b, a-b}
+          x[i3] = x[i1] - t1;
+          x[i1] += t1;
+
+          i1 += n8;
+          i2 += n8;
+          i3 += n8;
+          i4 += n8;
+
+          //sumdiff(x[i3], x[i4], t1, t2); // {s, d}  <--| {a+b, a-b}
+          t1 = x[i3] + x[i4];
+          t2 = x[i3] - x[i4];
+
+          t1 = -t1 * Math.SQRT1_2;
+          t2 *= Math.SQRT1_2;
+
+          // sumdiff(t1, x[i2], x[i4], x[i3]); // {s, d}  <--| {a+b, a-b}
+          st1 = x[i2];
+          x[i4] = t1 + st1;
+          x[i3] = t1 - st1;
+
+          //sumdiff3(x[i1], t2, x[i2]); // {a, b, d} <--| {a+b, b, a-b}
+          x[i2] = x[i1] - t2;
+          x[i1] += t2;
+        }
+      } else {
+        for(i0 = ix; i0 < n; i0 += id) {
+          i1 = i0;
+          i2 = i1 + n4;
+          i3 = i2 + n4;
+          i4 = i3 + n4;
+
+          //diffsum3_r(x[i3], x[i4], t1); // {a, b, s} <--| {a, b-a, a+b}
+          t1 = x[i3] + x[i4];
+          x[i4] -= x[i3];
+
+          //sumdiff3(x[i1], t1, x[i3]);   // {a, b, d} <--| {a+b, b, a-b}
+          x[i3] = x[i1] - t1;
+          x[i1] += t1;
+        }
+      }
+
+      ix = (id << 1) - n2;
+      id = id << 2;
+    } while (ix < n);
+
+    e = TWO_PI / n2;
+
+    for (var j = 1; j < n8; j++) {
+      a = j * e;
+      ss1 = Math.sin(a);
+      cc1 = Math.cos(a);
+
+      //ss3 = sin(3*a); cc3 = cos(3*a);
+      cc3 = 4*cc1*(cc1*cc1-0.75);
+      ss3 = 4*ss1*(0.75-ss1*ss1);
+
+      ix = 0; id = n2 << 1;
+      do {
+        for (i0 = ix; i0 < n; i0 += id) {
+          i1 = i0 + j;
+          i2 = i1 + n4;
+          i3 = i2 + n4;
+          i4 = i3 + n4;
+
+          i5 = i0 + n4 - j;
+          i6 = i5 + n4;
+          i7 = i6 + n4;
+          i8 = i7 + n4;
+
+          //cmult(c, s, x, y, &u, &v)
+          //cmult(cc1, ss1, x[i7], x[i3], t2, t1); // {u,v} <--| {x*c-y*s, x*s+y*c}
+          t2 = x[i7]*cc1 - x[i3]*ss1;
+          t1 = x[i7]*ss1 + x[i3]*cc1;
+
+          //cmult(cc3, ss3, x[i8], x[i4], t4, t3);
+          t4 = x[i8]*cc3 - x[i4]*ss3;
+          t3 = x[i8]*ss3 + x[i4]*cc3;
+
+          //sumdiff(t2, t4);   // {a, b} <--| {a+b, a-b}
+          st1 = t2 - t4;
+          t2 += t4;
+          t4 = st1;
+
+          //sumdiff(t2, x[i6], x[i8], x[i3]); // {s, d}  <--| {a+b, a-b}
+          //st1 = x[i6]; x[i8] = t2 + st1; x[i3] = t2 - st1;
+          x[i8] = t2 + x[i6];
+          x[i3] = t2 - x[i6];
+
+          //sumdiff_r(t1, t3); // {a, b} <--| {a+b, b-a}
+          st1 = t3 - t1;
+          t1 += t3;
+          t3 = st1;
+
+          //sumdiff(t3, x[i2], x[i4], x[i7]); // {s, d}  <--| {a+b, a-b}
+          //st1 = x[i2]; x[i4] = t3 + st1; x[i7] = t3 - st1;
+          x[i4] = t3 + x[i2];
+          x[i7] = t3 - x[i2];
+
+          //sumdiff3(x[i1], t1, x[i6]);   // {a, b, d} <--| {a+b, b, a-b}
+          x[i6] = x[i1] - t1;
+          x[i1] += t1;
+
+          //diffsum3_r(t4, x[i5], x[i2]); // {a, b, s} <--| {a, b-a, a+b}
+          x[i2] = t4 + x[i5];
+          x[i5] -= t4;
+        }
+
+        ix = (id << 1) - n2;
+        id = id << 2;
+
+      } while (ix < n);
+    }
+  }
+
+  while (--i) {
+    rval = x[i];
+    ival = x[n-i-1];
+    mag = bSi * sqrt(rval * rval + ival * ival);
+
+    if (mag > this.peak) {
+      this.peakBand = i;
+      this.peak = mag;
+    }
+
+    spectrum[i] = mag;
+  }
+
+  spectrum[0] = bSi * x[0];
+
+  return spectrum;
+};
+
+module.exports = RFFT;
diff --git a/lib/sampler.js b/lib/sampler.js
new file mode 100644
index 0000000..791d657
--- /dev/null
+++ b/lib/sampler.js
@@ -0,0 +1,133 @@
+/* global Float64Array */
+var DSP = require("./dsp");
+
+/**
+ * Sampler
+ *
+ * @constructor
+ * @param {} file
+ * @param {Number} bufferSize
+ * @param {Number} sampleRate
+ * @param {Number} playStart
+ * @param {Number} playEnd
+ * @param {Number} loopStart
+ * @param {Number} loopEnd
+ * @param {Number} loopMode
+ */
+function Sampler(file, bufferSize, sampleRate, playStart, playEnd, loopStart, loopEnd, loopMode) {
+  this.file = file;
+  this.bufferSize = bufferSize;
+  this.sampleRate = sampleRate;
+  this.playStart  = playStart || 0; // 0%
+  this.playEnd    = playEnd   || 1; // 100%
+  this.loopStart  = loopStart || 0;
+  this.loopEnd    = loopEnd   || 1;
+  this.loopMode   = loopMode  || DSP.OFF;
+  this.loaded     = false;
+  this.samples    = [];
+  this.signal     = new Float64Array(bufferSize);
+  this.frameCount = 0;
+  this.envelope   = null;
+  this.amplitude  = 1;
+  this.rootFrequency = 110; // A2 110
+  this.frequency  = 550;
+  this.step       = this.frequency / this.rootFrequency;
+  this.duration   = 0;
+  this.samplesProcessed = 0;
+  this.playhead   = 0;
+
+  var audio = /* new Audio();*/ document.createElement("AUDIO");
+  var self = this;
+
+  this.loadSamples = function(event) {
+    var buffer = DSP.getChannel(DSP.MIX, event.frameBuffer);
+    for ( var i = 0; i < buffer.length; i++) {
+      self.samples.push(buffer[i]);
+    }
+  };
+
+  this.loadComplete = function() {
+    // convert flexible js array into a fast typed array
+    self.samples = new Float64Array(self.samples);
+    self.loaded = true;
+  };
+
+  this.loadMetaData = function() {
+    self.duration = audio.duration;
+  };
+
+  audio.addEventListener("MozAudioAvailable", this.loadSamples, false);
+  audio.addEventListener("loadedmetadata", this.loadMetaData, false);
+  audio.addEventListener("ended", this.loadComplete, false);
+  audio.muted = true;
+  audio.src = file;
+  audio.play();
+}
+
+Sampler.prototype.applyEnvelope = function() {
+  this.envelope.process(this.signal);
+  return this.signal;
+};
+
+Sampler.prototype.generate = function() {
+  var loopWidth = this.playEnd * this.samples.length - this.playStart * this.samples.length;
+  var playStartSamples = this.playStart * this.samples.length; // ie 0.5 -> 50% of the length
+  var playEndSamples = this.playEnd * this.samples.length; // ie 0.5 -> 50% of the length
+
+  for ( var i = 0; i < this.bufferSize; i++ ) {
+    switch (this.loopMode) {
+      case DSP.OFF:
+        this.playhead = Math.round(this.samplesProcessed * this.step + playStartSamples);
+        if (this.playhead < (this.playEnd * this.samples.length) ) {
+          this.signal[i] = this.samples[this.playhead] * this.amplitude;
+        } else {
+          this.signal[i] = 0;
+        }
+        break;
+
+      case DSP.FW:
+        this.playhead = Math.round((this.samplesProcessed * this.step) % loopWidth + playStartSamples);
+        if (this.playhead < (this.playEnd * this.samples.length) ) {
+          this.signal[i] = this.samples[this.playhead] * this.amplitude;
+        }
+        break;
+
+      case DSP.BW:
+        this.playhead = playEndSamples - Math.round((this.samplesProcessed * this.step) % loopWidth);
+        if (this.playhead < (this.playEnd * this.samples.length) ) {
+          this.signal[i] = this.samples[this.playhead] * this.amplitude;
+        }
+        break;
+
+      case DSP.FWBW:
+        if ( Math.floor(this.samplesProcessed * this.step / loopWidth) % 2 === 0 ) {
+          this.playhead = Math.round((this.samplesProcessed * this.step) % loopWidth + playStartSamples);
+        } else {
+          this.playhead = playEndSamples - Math.round((this.samplesProcessed * this.step) % loopWidth);
+        }
+        if (this.playhead < (this.playEnd * this.samples.length) ) {
+          this.signal[i] = this.samples[this.playhead] * this.amplitude;
+        }
+        break;
+    }
+    this.samplesProcessed++;
+  }
+
+  this.frameCount++;
+
+  return this.signal;
+};
+
+Sampler.prototype.setFreq = function(frequency) {
+    var totalProcessed = this.samplesProcessed * this.step;
+    this.frequency = frequency;
+    this.step = this.frequency / this.rootFrequency;
+    this.samplesProcessed = Math.round(totalProcessed/this.step);
+};
+
+Sampler.prototype.reset = function() {
+  this.samplesProcessed = 0;
+  this.playhead = 0;
+};
+
+module.exports = Sampler;
diff --git a/lib/single-delay.js b/lib/single-delay.js
new file mode 100644
index 0000000..bd3febd
--- /dev/null
+++ b/lib/single-delay.js
@@ -0,0 +1,93 @@
+/* global Float64Array */
+
+/**
+ * SingleDelay effect by Almer Thie (http://code.almeros.com).
+ * Copyright 2010 Almer Thie. All rights reserved.
+ * Example: See usage in Reverb class
+ *
+ * This is a delay that does NOT feeds it's own delayed signal back into its
+ * circular buffer, neither does it return the original signal. Also known as
+ * an AllPassFilter(?).
+ *
+ * Compatible with interleaved stereo (or more channel) buffers and
+ * non-interleaved mono buffers.
+ *
+ * @param {Number} maxDelayInSamplesSize Maximum possible delay in samples (size of circular buffer)
+ * @param {Number} delayInSamples Initial delay in samples
+ * @param {Number} delayVolume Initial feedback delay volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ *
+ * @constructor
+ */
+function SingleDelay(maxDelayInSamplesSize, delayInSamples, delayVolume) {
+  this.delayBufferSamples = new Float64Array(maxDelayInSamplesSize); // The maximum size of delay
+  this.delayInputPointer  = delayInSamples;
+  this.delayOutputPointer = 0;
+
+  this.delayInSamples     = delayInSamples;
+  this.delayVolume        = delayVolume;
+}
+
+/**
+ * Change the delay time in samples.
+ *
+ * @param {Number} delayInSamples Delay in samples
+ */
+SingleDelay.prototype.setDelayInSamples = function(delayInSamples) {
+  this.delayInSamples = delayInSamples;
+  this.delayInputPointer = this.delayOutputPointer + delayInSamples;
+
+  if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
+    this.delayInputPointer = this.delayInputPointer - this.delayBufferSamples.length;
+  }
+};
+
+/**
+ * Change the return signal volume.
+ *
+ * @param {Number} delayVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
+ */
+SingleDelay.prototype.setDelayVolume = function(delayVolume) {
+  this.delayVolume = delayVolume;
+};
+
+/**
+ * Process a given interleaved or mono non-interleaved float value Array and
+ * returns the delayed audio.
+ *
+ * @param {Array} samples Array containing Float values or a Float64Array
+ *
+ * @returns A new Float64Array interleaved or mono non-interleaved as was fed to this function.
+ */
+SingleDelay.prototype.process = function(samples) {
+  // NB. Make a copy to put in the output samples to return.
+  var outputSamples = new Float64Array(samples.length);
+
+  for (var i=0; i<samples.length; i++) {
+
+    // Add audio data with the delay in the delay buffer
+    this.delayBufferSamples[this.delayInputPointer] = samples[i];
+
+    // delayBufferSamples could contain initial NULL's, return silence in that case
+    var delaySample = this.delayBufferSamples[this.delayOutputPointer];
+
+    // Return the audio with delay mix
+    outputSamples[i] = delaySample * this.delayVolume;
+
+    // Manage circulair delay buffer pointers
+    this.delayInputPointer++;
+
+    if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
+      this.delayInputPointer = 0;
+    }
+
+    this.delayOutputPointer++;
+
+    if (this.delayOutputPointer >= this.delayBufferSamples.length-1) {
+      this.delayOutputPointer = 0;
+    }
+  }
+
+  return outputSamples;
+};
+
+module.exports = SingleDelay;
diff --git a/lib/sinh.js b/lib/sinh.js
new file mode 100644
index 0000000..d2706f3
--- /dev/null
+++ b/lib/sinh.js
@@ -0,0 +1,16 @@
+/**
+ * Returns the hyperbolic sine of the number
+ *
+ * @meta version: 1004.2314
+ * @meta discuss at: http://phpjs.org/functions/sinh
+ * @meta original by: Onno Marsman
+ *
+ * @param {Number} num
+ * @example
+ * sinh(-0.9834330348825909); // => -1.1497971402636502
+ */
+function sinh (arg) {
+  return (Math.exp(arg) - Math.exp(-arg))/2;
+}
+
+module.exports = sinh;
diff --git a/lib/window-function.js b/lib/window-function.js
new file mode 100644
index 0000000..0e7b383
--- /dev/null
+++ b/lib/window-function.js
@@ -0,0 +1,115 @@
+var DSP = require("./dsp");
+
+/**
+ * WindowFunction
+ *
+ * @constructor
+ * @param {Number} type
+ * @param {Number} alpha
+ */
+function WindowFunction(type, alpha) {
+  this.alpha = alpha;
+
+  switch(type) {
+    case DSP.BARTLETT:
+      this.func = WindowFunction.Bartlett;
+      break;
+
+    case DSP.BARTLETTHANN:
+      this.func = WindowFunction.BartlettHann;
+      break;
+
+    case DSP.BLACKMAN:
+      this.func = WindowFunction.Blackman;
+      this.alpha = this.alpha || 0.16;
+      break;
+
+    case DSP.COSINE:
+      this.func = WindowFunction.Cosine;
+      break;
+
+    case DSP.GAUSS:
+      this.func = WindowFunction.Gauss;
+      this.alpha = this.alpha || 0.25;
+      break;
+
+    case DSP.HAMMING:
+      this.func = WindowFunction.Hamming;
+      break;
+
+    case DSP.HANN:
+      this.func = WindowFunction.Hann;
+      break;
+
+    case DSP.LANCZOS:
+      this.func = WindowFunction.Lanczoz;
+      break;
+
+    case DSP.RECTANGULAR:
+      this.func = WindowFunction.Rectangular;
+      break;
+
+    case DSP.TRIANGULAR:
+      this.func = WindowFunction.Triangular;
+      break;
+  }
+}
+
+/**
+ * Process a buffer
+ * @param {Array} buffer
+ */
+WindowFunction.prototype.process = function(buffer) {
+  var length = buffer.length;
+  for ( var i = 0; i < length; i++ ) {
+    buffer[i] *= this.func(length, i, this.alpha);
+  }
+  return buffer;
+};
+
+WindowFunction.Bartlett = function(length, index) {
+  return 2 / (length - 1) * ((length - 1) / 2 - Math.abs(index - (length - 1) / 2));
+};
+
+WindowFunction.BartlettHann = function(length, index) {
+  return 0.62 - 0.48 * Math.abs(index / (length - 1) - 0.5) - 0.38 * Math.cos(DSP.TWO_PI * index / (length - 1));
+};
+
+WindowFunction.Blackman = function(length, index, alpha) {
+  var a0 = (1 - alpha) / 2;
+  var a1 = 0.5;
+  var a2 = alpha / 2;
+
+  return a0 - a1 * Math.cos(DSP.TWO_PI * index / (length - 1)) + a2 * Math.cos(4 * Math.PI * index / (length - 1));
+};
+
+WindowFunction.Cosine = function(length, index) {
+  return Math.cos(Math.PI * index / (length - 1) - Math.PI / 2);
+};
+
+WindowFunction.Gauss = function(length, index, alpha) {
+  return Math.pow(Math.E, -0.5 * Math.pow((index - (length - 1) / 2) / (alpha * (length - 1) / 2), 2));
+};
+
+WindowFunction.Hamming = function(length, index) {
+  return 0.54 - 0.46 * Math.cos(DSP.TWO_PI * index / (length - 1));
+};
+
+WindowFunction.Hann = function(length, index) {
+  return 0.5 * (1 - Math.cos(DSP.TWO_PI * index / (length - 1)));
+};
+
+WindowFunction.Lanczos = function(length, index) {
+  var x = 2 * index / (length - 1) - 1;
+  return Math.sin(Math.PI * x) / (Math.PI * x);
+};
+
+WindowFunction.Rectangular = function() {
+  return 1;
+};
+
+WindowFunction.Triangular = function(length, index) {
+  return 2 / length * (length / 2 - Math.abs(index - (length - 1) / 2));
+};
+
+module.exports = WindowFunction;
diff --git a/package.json b/package.json
index decf3e3..00a2e89 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,16 @@
   "name": "dsp.js",
   "version": "1.0.0",
   "description": "Digital Signal Processing for Javascript",
-  "main": "dsp.js",
+  "main": "lib/index.js",
+  "scripts": {
+    "prepublish": "npm run dist",
+    "predist": "npm run test && npm run lint && npm run docs",
+    "dist": "rm dist/* && browserify lib/index.js > dist/dsp.js && uglifyjs dist/dsp.js > dist/dsp.min.js",
+    "docs": "jsdoc2md \"lib/*.js\" > docs/API.md",
+    "test": "mocha test/*.js",
+    "lint": "eslint lib/*.js",
+    "bench": "node bench/index.js"
+  },
   "directories": {
     "example": "examples",
     "test": "test"
@@ -21,5 +30,15 @@
   "bugs": {
     "url": "https://github.com/corbanbrook/dsp.js/issues"
   },
-  "homepage": "https://github.com/corbanbrook/dsp.js#readme"
+  "homepage": "https://github.com/corbanbrook/dsp.js#readme",
+  "standard": {
+    "ignore": "**/*"
+  },
+  "devDependencies": {
+    "browserify": "^13.1.1",
+    "eslint": "^3.12.2",
+    "jsdoc-to-markdown": "^2.0.1",
+    "mocha": "^3",
+    "uglifyjs": "^2.4.10"
+  }
 }
diff --git a/test/adsr-test.js b/test/audio-test/adsr-test.js
similarity index 100%
rename from test/adsr-test.js
rename to test/audio-test/adsr-test.js
diff --git a/test/audio-harness.js b/test/audio-test/audio-harness.js
similarity index 100%
rename from test/audio-harness.js
rename to test/audio-test/audio-harness.js
diff --git a/test/beatdetect-test.js b/test/audio-test/beatdetect-test.js
similarity index 100%
rename from test/beatdetect-test.js
rename to test/audio-test/beatdetect-test.js
diff --git a/test/beatdetektor.js b/test/audio-test/beatdetektor.js
similarity index 100%
rename from test/beatdetektor.js
rename to test/audio-test/beatdetektor.js
diff --git a/test/dft-test.js b/test/audio-test/dft-test.js
similarity index 100%
rename from test/dft-test.js
rename to test/audio-test/dft-test.js
diff --git a/test/fft-test.js b/test/audio-test/fft-test.js
similarity index 100%
rename from test/fft-test.js
rename to test/audio-test/fft-test.js
diff --git a/test/filter-test.js b/test/audio-test/filter-test.js
similarity index 100%
rename from test/filter-test.js
rename to test/audio-test/filter-test.js
diff --git a/test/iirfilter-test.js b/test/audio-test/iirfilter-test.js
similarity index 100%
rename from test/iirfilter-test.js
rename to test/audio-test/iirfilter-test.js
diff --git a/test/multidelay-test.js b/test/audio-test/multidelay-test.js
similarity index 100%
rename from test/multidelay-test.js
rename to test/audio-test/multidelay-test.js
diff --git a/test/oscillator-test.js b/test/audio-test/oscillator-test.js
similarity index 100%
rename from test/oscillator-test.js
rename to test/audio-test/oscillator-test.js
diff --git a/test/reverb-test.js b/test/audio-test/reverb-test.js
similarity index 100%
rename from test/reverb-test.js
rename to test/audio-test/reverb-test.js
diff --git a/test/samples.js b/test/audio-test/samples.js
similarity index 100%
rename from test/samples.js
rename to test/audio-test/samples.js
diff --git a/test/test-exports.js b/test/test-exports.js
new file mode 100644
index 0000000..5904aba
--- /dev/null
+++ b/test/test-exports.js
@@ -0,0 +1,16 @@
+/* global describe it */
+var assert = require("assert");
+var dsp = require("..");
+
+describe("dsp.js library", function () {
+  it("Export all modules", function () {
+    var keys = Object.keys(dsp);
+    assert.equal(16, keys.length, "must export 16 items");
+    assert(typeof dsp["DSP"] === "object", "DSP must be an object");
+    keys.forEach(function (key) {
+      if (key !== "DSP") {
+        assert(typeof dsp[key] === "function", key + " must be a function.");
+      }
+    });
+  });
+});