diff --git a/examples/bandlimited/css/main.css b/examples/bandlimited/css/main.css
new file mode 100644
index 0000000..e69de29
diff --git a/examples/bandlimited/index.htm b/examples/bandlimited/index.htm
new file mode 100644
index 0000000..3b3f691
--- /dev/null
+++ b/examples/bandlimited/index.htm
@@ -0,0 +1,187 @@
+
+
+
+
+ Bandlimited Synthesis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This example demonstrates how to interact with audiolet. For now, it emulates
+ a musical keyboard on your computer keyboard.
+
+
+
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ 4
+
+
+ 5
+
+
+ 6
+
+
+ 7
+
+
+ 8
+
+
+ 9
+
+
+ 0
+
+
+
+
higher octave
+
+ Q
+
+
+ W
+
+
+ E
+
+
+ R
+
+
+ T
+
+
+ Z
+
+
+ U
+
+
+ I
+
+
+ O
+
+
+ P
+
+
+
+
+
+ A
+
+
+ S
+
+
+ D
+
+
+ F
+
+
+ G
+
+
+ H
+
+
+ J
+
+
+ K
+
+
+ L
+
+
+
+
lower octave
+
+ Y
+
+
+ X
+
+
+ C
+
+
+ V
+
+
+ B
+
+
+ N
+
+
+ M
+
+
+ ;
+
+
+ :
+
+
+
+
+
+
+
+
+
diff --git a/examples/bandlimited/js/audiolet_app.js b/examples/bandlimited/js/audiolet_app.js
new file mode 100644
index 0000000..73ef8a1
--- /dev/null
+++ b/examples/bandlimited/js/audiolet_app.js
@@ -0,0 +1,76 @@
+window.onload = function() {
+
+ this.audiolet = new Audiolet();
+
+ var play=function(f,canvas) {
+ var X=new Object();
+ X.square = new BandlimitedSquare(this.audiolet,f);
+ X.saw = new BandlimitedSaw(this.audiolet,f);
+ X.tri = new BandlimitedTriangle(this.audiolet,f);
+
+ X.amplitude = new Multiply(this.audiolet, 1);
+ // X.square.connect(X.amplitude);
+ // X.saw.connect(X.amplitude);
+ X.tri.connect(X.amplitude);
+
+ X.envelope = new InteractiveEnvelope(this.audiolet,0.5,1e-10,function () { X.amplitude.remove(); delete X; }); // Don't have a clue if this is sufficient to prevent mem leaks
+
+ X.envelope.connect(X.amplitude,0,1);
+
+ if (canvas) {
+ X.osci = new Oscilloscope(this.audiolet,undefined,canvas);
+ X.amplitude.connect(X.osci);
+ X.osci.connect(this.audiolet.output);
+ X.osci.raf=function () { window.mozRequestAnimationFrame(function () {X.osci.paint();},canvas); };
+ // window.setTimeout(X.osci.raf,300);
+ X.osci.paint();
+
+ } else {
+ X.amplitude.connect(this.audiolet.output);
+ }
+ X.envelope.newTarget(0.002,1e-5);
+
+ return X;
+ }
+
+ // x=play(220,document.getElementById('scope'));
+ // window.setTimeout(function () { x.envelope.newTarget(0,2e-2); },200);
+
+ function keyboard() {
+ var keyboard_scale=[89,83,88,68,67,86,71,66,72,78,74,77,
+ 81,50,87,51,69,82,53,84,54,90,55,85];
+
+ var f=220;
+ var keyboard=Array();
+ for (k in keyboard_scale) {
+ keyboard[keyboard_scale[k]]=f;
+ f*=Math.pow(2,1/12);
+ }
+ return keyboard;
+ }
+
+ this.keyboard=keyboard();
+
+ this.playing_notes=Array();
+
+ window.onkeydown=function(e) {
+ if ((f=this.keyboard[e.keyCode])!=null) {
+ if (playing_notes[e.keyCode]==null) {
+ f*=Math.pow(2,document.getElementById('octave').value);
+ var p=new Object();
+ p.play=play(f);
+ playing_notes[e.keyCode]=p;
+ var kc=e.keyCode;
+ p.marker=kc;
+ p.switchOff=function() { p.play.envelope.newTarget(0,2e-4); playing_notes[kc]=null;}
+ }
+ }
+ }
+ window.onkeyup=function(e) {
+ var kc=e.keyCode;
+ if ((f=this.playing_notes[kc])!=null) {
+ this.playing_notes[kc].switchOff();
+ }
+ }
+
+};
diff --git a/examples/interaction/css/main.css b/examples/interaction/css/main.css
new file mode 100644
index 0000000..e69de29
diff --git a/examples/interaction/index.htm b/examples/interaction/index.htm
new file mode 100644
index 0000000..3ddb6a6
--- /dev/null
+++ b/examples/interaction/index.htm
@@ -0,0 +1,181 @@
+
+
+
+
+ Audiolet Template
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This example demonstrates how to interact with audiolet. For now, it emulates
+ a musical keyboard on your computer keyboard.
+
+
+
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ 4
+
+
+ 5
+
+
+ 6
+
+
+ 7
+
+
+ 8
+
+
+ 9
+
+
+ 0
+
+
+
+
higher octave
+
+ Q
+
+
+ W
+
+
+ E
+
+
+ R
+
+
+ T
+
+
+ Z
+
+
+ U
+
+
+ I
+
+
+ O
+
+
+ P
+
+
+
+
+
+ A
+
+
+ S
+
+
+ D
+
+
+ F
+
+
+ G
+
+
+ H
+
+
+ J
+
+
+ K
+
+
+ L
+
+
+
+
lower octave
+
+ Y
+
+
+ X
+
+
+ C
+
+
+ V
+
+
+ B
+
+
+ N
+
+
+ M
+
+
+ ;
+
+
+ :
+
+
+
+
+
+
+
+
diff --git a/examples/interaction/js/audiolet_app.js b/examples/interaction/js/audiolet_app.js
new file mode 100644
index 0000000..8070eaf
--- /dev/null
+++ b/examples/interaction/js/audiolet_app.js
@@ -0,0 +1,67 @@
+window.onload = function() {
+
+ this.audiolet = new Audiolet();
+
+ var play=function(f) {
+ var X=new Object();
+ X.sine = new Sine(this.audiolet,f);
+
+ X.modulator = new Sine(this.audiolet, f);
+
+ X.modulator_op = new MulAdd(this.audiolet,f/4,f);
+ X.modulator.connect(X.modulator_op,0,0);
+ X.modulator_op.connect(X.sine);
+
+ X.amplitude = new Multiply(this.audiolet, 1);
+ X.sine.connect(X.amplitude);
+
+ X.envelope = new InteractiveEnvelope(this.audiolet,0.1,1e-10,function () { X.amplitude.remove(); delete X; });
+
+ X.envelope.connect(X.amplitude,0,1);
+ X.amplitude.connect(this.audiolet.output);
+
+ X.envelope.newTarget(0.02,1e-5);
+ return X;
+ }
+
+ x=play(440);
+ window.setTimeout(function () { x.envelope.newTarget(0,2e-2); },200);
+
+ function keyboard() {
+ var keyboard_scale=[89,83,88,68,67,86,71,66,72,78,74,77,
+ 81,50,87,51,69,82,53,84,54,90,55,85];
+
+ var f=220;
+ var keyboard=Array();
+ for (k in keyboard_scale) {
+ keyboard[keyboard_scale[k]]=f;
+ f*=Math.pow(2,1/12);
+ }
+ return keyboard;
+ }
+
+ this.keyboard=keyboard();
+
+ this.playing_notes=Array();
+
+ window.onkeydown=function(e) {
+ if ((f=this.keyboard[e.keyCode])!=null) {
+ if (playing_notes[e.keyCode]==null) {
+ f*=Math.pow(2,document.getElementById('octave').value);
+ var p=new Object();
+ p.play=play(f);
+ playing_notes[e.keyCode]=p;
+ var kc=e.keyCode;
+ p.marker=kc;
+ p.switchOff=function() { p.play.envelope.newTarget(0,2e-2); playing_notes[kc]=null;}
+ }
+ }
+ }
+ window.onkeyup=function(e) {
+ var kc=e.keyCode;
+ if ((f=this.playing_notes[kc])!=null) {
+ this.playing_notes[kc].switchOff();
+ }
+ }
+
+};
diff --git a/src/audiolet/Audiolet.js b/src/audiolet/Audiolet.js
index d0fbb02..3c6ad17 100644
--- a/src/audiolet/Audiolet.js
+++ b/src/audiolet/Audiolet.js
@@ -3439,7 +3439,7 @@ TableLookupOscillator.prototype.generate = function(inputBuffers,
var step = frequency * tableSize / sampleRate;
phase += step;
if (phase >= tableSize) {
- phase %= tableSize;
+ phase = ((phase%tableSize)+tableSize)%tableSize; // javascript % doesn't behave consistently
}
channel[i] = table[Math.floor(phase)];
}
diff --git a/src/core/Oscilloscope.js b/src/core/Oscilloscope.js
new file mode 100644
index 0000000..ec01103
--- /dev/null
+++ b/src/core/Oscilloscope.js
@@ -0,0 +1,67 @@
+
+/**
+ * An oscilloscope
+ *
+ * **Inputs**
+ *
+ * - Signal
+ *
+ * **Outputs**
+ *
+ * - Unmodified Signal (necessary, since Audiolet uses a "pull" architecture)
+ *
+ * **Parameters**
+ *
+ * @constructor
+ * @param {Audiolet} audiolet The audiolet object.
+ * @param {signal} signal Signal input
+ * @param {canvas} canvas A canvas object to display the oscilloscope
+ * @extends AudioletNode
+ */
+
+var Oscilloscope = function(audiolet,signal,canvas) {
+ AudioletNode.call(this, audiolet, 1,1);
+ this.linkNumberOfOutputChannels(0, 0);
+ // this.value = new AudioletParameter(this, 0, signal);
+ this.canvas=canvas;
+ this.gc = canvas.getContext('2d');
+ this.width = canvas.width;
+ this.height = canvas.height;
+ this.displayeddata = Array();
+ this.displayedpointer = 0;
+ this.count=0;
+};
+
+extend(Oscilloscope, AudioletNode);
+
+Oscilloscope.prototype.generate = function(inputBuffers,outputBuffers) {
+ var c=inputBuffers[0].getChannelData(0);
+ var o=outputBuffers[0].getChannelData(0);
+ for (var i=0;ithis.width) {
+ this.canvas.width=this.width;
+ gc.fillStyle='#ffffff';
+ gc.strokeStyle='#000000';
+ gc.beginPath();
+ gc.moveTo(0,this.displayeddata[0]*h2*factor+h2);
+ for (var i=0;iP) {
+ throw "Too many Partials in BandlimitedImpulseTrain.";
+ }
+ return function(n) {
+ var x=Math.PI*M*n/P;
+ var t=x/M;
+ if (Math.abs(Math.sin(t)) > 1e-5)
+ return Math.sin(x)/(P*Math.sin(t));
+ else
+ return M/P*Math.cos(x)/(Math.cos(t));
+ }
+};
+
+/**
+ * Bandlimited Square Wave Generator
+ *
+ * **Inputs**
+ *
+ * none (frequency modulation is hard to get right with the closed formula above)
+ *
+ * **Outputs**
+ *
+ * - Bandlimited Square Wave
+ *
+ * **Parameters**
+ *
+ * @constructor
+ * @param {Audiolet} audiolet The audiolet object.
+ * @param {Number} [frequency=440] Frequency.
+ */
+
+BandlimitedSquare = function(audiolet,frequency) {
+ AudioletNode.call(this, audiolet, 0, 1);
+ this.frequency = frequency || 440;
+ this.phase = 0;
+ P=(44100.0/frequency)/2; // double "frequency" for square wave - see source mentioned in header
+
+ M=Math.floor(P/2)*2; // even number of harmonics, lower than the original P
+
+ // bipolar BLIT
+
+ this.BandlimitedImpulseTrain=BandlimitedImpulseTrain(P,M,0);
+
+ // initialize integrator storage
+
+ this.store=0;
+};
+
+extend(BandlimitedSquare, AudioletNode);
+
+BandlimitedSquare.prototype.generate = function(inputBuffers,
+ outputBuffers) {
+ var buffer = outputBuffers[0];
+ var channel = buffer.getChannelData(0);
+
+ var bufferLength = buffer.length;
+ for (var i = 0; i < bufferLength; i++) {
+ var blit=this.BandlimitedImpulseTrain(this.phase++);
+ channel[i] = blit+this.store;
+ this.store += blit;
+ this.store *= 0.9999;
+ }
+};
+
+
+/**
+ * toString
+ *
+ * @return {String} String representation.
+ */
+BandlimitedSquare.prototype.toString = function() {
+ return 'BandlimitedSquare';
+};
+
+
+
+/**
+ * Bandlimited Saw Wave Generator
+ *
+ * **Inputs**
+ *
+ * none (frequency modulation is hard to get right with the closed formula above)
+ *
+ * **Outputs**
+ *
+ * - Bandlimited Saw Wave
+ *
+ * **Parameters**
+ *
+ * @constructor
+ * @param {Audiolet} audiolet The audiolet object.
+ * @param {Number} [frequency=440] Frequency.
+ */
+
+BandlimitedSaw = function(audiolet,frequency) {
+ AudioletNode.call(this, audiolet, 0, 1);
+ this.frequency = frequency || 440;
+ this.phase = 0;
+ P=(44100.0/frequency);
+
+ M=Math.floor(P/2-1)*2+1; // odd number of harmonics, lower than the original P
+
+ // unipolar BLIT
+
+ this.BandlimitedImpulseTrain=BandlimitedImpulseTrain(P,M,1);
+ this.P=P;
+
+ // offset 1/2 - integral (DC part) of signal will be 0
+
+ this.offset=-1/P;
+ this.store=0;
+};
+
+extend(BandlimitedSaw, AudioletNode);
+
+BandlimitedSaw.prototype.generate = function(inputBuffers,
+ outputBuffers) {
+ var buffer = outputBuffers[0];
+ var channel = buffer.getChannelData(0);
+
+ var bufferLength = buffer.length;
+ for (var i = 0; i < bufferLength; i++) {
+ var blit= this.BandlimitedImpulseTrain(this.phase++)+this.offset;
+ channel[i] = blit+this.store;
+ this.store += blit;
+ this.store *= 0.9999;
+ }
+};
+
+
+/**
+ * toString
+ *
+ * @return {String} String representation.
+ */
+BandlimitedSaw.prototype.toString = function() {
+ return 'BandlimitedSaw';
+};
+
+
+/**
+ * Bandlimited Triangle Wave Generator
+ *
+ * **Inputs**
+ *
+ * none (frequency modulation is hard to get right with the closed formula above)
+ *
+ * **Outputs**
+ *
+ * - Bandlimited Triangle Wave
+ *
+ * **Parameters**
+ *
+ * @constructor
+ * @param {Audiolet} audiolet The audiolet object.
+ * @param {Number} [frequency=440] Frequency.
+ */
+
+
+BandlimitedTriangle = function(audiolet,frequency) {
+ AudioletNode.call(this, audiolet, 0, 1);
+ this.frequency = frequency || 440;
+ P=(44100.0/frequency)/2; // double "frequency" for square wave - see source mentioned in header
+
+ this.phase = P/2;
+
+ M=Math.floor(P/2)*2; // even number of harmonics, lower than the original P
+
+ // bipolar BLIT
+ this.Period=P;
+ this.BandlimitedImpulseTrain=BandlimitedImpulseTrain(P,M,0);
+
+ // initialize integrator storage
+
+ this.store=Array();
+ this.store[0]=0;
+ this.store[1]=0;
+ this.store[2]=0;
+ this.overflow=false;
+ this.count=0;
+ this.offset=0.5/P;
+ console.log("Tri!");
+};
+
+extend(BandlimitedTriangle, AudioletNode);
+
+BandlimitedTriangle.prototype.generate = function(inputBuffers,
+ outputBuffers) {
+ var buffer = outputBuffers[0];
+ var channel = buffer.getChannelData(0);
+
+ var bufferLength = buffer.length;
+
+ var frequency=1/this.Period;
+ var frequency2=0.5/this.Period;
+ for (var i = 0; i < bufferLength; i++) {
+ var blit=this.BandlimitedImpulseTrain(1+this.phase++);
+ this.store[0] += blit*frequency;
+ this.store[0] *= 0.9999;
+ this.offset *= 0.9999;
+ this.store[1] += (this.store[0]+this.offset);
+ this.store[1] *= 0.999;
+ channel[i] = this.store[1];
+ }
+ if (!this.overflow) {
+ if (Math.abs(channel[0])>2) {
+ this.overflow=true;
+ console.log("0 "+this.store[0]);
+ console.log("1 "+this.store[1]);
+ }
+ }
+};
+
+
+/**
+ * toString
+ *
+ * @return {String} String representation.
+ */
+BandlimitedTriangle.prototype.toString = function() {
+ return 'BandlimitedTriangle';
+};
+
diff --git a/src/dsp/Blit.js b/src/dsp/Blit.js
new file mode 100644
index 0000000..2646b90
--- /dev/null
+++ b/src/dsp/Blit.js
@@ -0,0 +1,84 @@
+
+/**
+ * BLIT closed formula, cf.
+ * http://www.music.mcgill.ca/~gary/307/week5/bandlimited.html
+ *
+ * It is possible to synthesize a sign-alternating impulse train
+ * by supplying an even number of Partials. However, then the
+ * Interpretation of P is different.
+ *
+ * @param {P} If odd is true, period in (fractional) samples. If odd
+ * is false, half period.
+ * @param {M} number of synthesized Partials
+ * @param {odd} assert that M is odd/even.
+ * @returns a function that will calculate a Bandlimited impulse train
+ * depending on a time parameter
+ */
+function Blit(P,M,odd) {
+ if ((odd && (M%2!=1)) ||
+ (!odd && (M%2!=0))) {
+ throw "Erroneous Number of Partials in Blit.";
+ }
+ if (M>P) {
+ throw "Too many Partials in Blit.";
+ }
+ return function(t) {
+ var t=Math.PI*n/P;
+ var x=t*M;
+ n++;
+ if (Math.abs(Math.sin(t) > 1e-5))
+ return Math.sin(x)/(P*Math.sin(t));
+ else
+ return M*Math.cos(x)/(P*Math.cos(t));
+ }
+};
+
+/**
+ * Bandlimited Square Wave Generator
+ *
+ * **Inputs**
+ *
+ * none (frequency modulation is hard to get right with the closed formula above)
+ *
+ * **Outputs**
+ *
+ * - Bandlimited Square Wave
+ *
+ * **Parameters**
+ *
+ * @constructor
+ * @param {Audiolet} audiolet The audiolet object.
+ * @param {Number} [frequency=440] Frequency.
+ */
+
+BlitSquare = function(audiolet,frequency) {
+ AudioletNode.call(this, audiolet, 0, 1);
+ this.frequency = frequency || 440;
+ this.phase = 0;
+ M=(44100/frequency); // XXX fixed sampling frequency
+ M=(Math.floor(M/2)*2); // even number, lower than the original M
+ P=(44100/frequency);
+ this.Blit=Blit(M,P,0);
+};
+
+BlitSquare.prototype.generate = function(inputBuffers,
+ outputBuffers) {
+ var buffer = outputBuffers[0];
+ var channel = buffer.getChannelData(0);
+
+ var bufferLength = buffer.length;
+ for (var i = 0; i < bufferLength; i++) {
+ channel[i] = this.Blit(this.phase++);
+ }
+};
+
+/**
+ * toString
+ *
+ * @return {String} String representation.
+ */
+BlitSquare.prototype.toString = function() {
+ return 'BlitSquare';
+};
+
+extend(BlitSquare, AudioletNode);
diff --git a/src/dsp/FixedBiquadFilter.js b/src/dsp/FixedBiquadFilter.js
new file mode 100644
index 0000000..279edc3
--- /dev/null
+++ b/src/dsp/FixedBiquadFilter.js
@@ -0,0 +1,120 @@
+/*!
+ * @depends ../core/AudioletNode.js
+ */
+
+/**
+ * Generic biquad filter. The coefficients (a0, a1, a2, b0, b1 and b2) are
+ * set at initalization. This simplifies bandlimited synthesis
+ *
+ * **Inputs**
+ *
+ * - Audio
+ *
+ * **Outputs**
+ *
+ * - Filtered audio
+ *
+ * **Parameters**
+ *
+ * @constructor
+ * @extends AudioletNode
+ * @param {Audiolet} audiolet The audiolet object.
+ * @param {b} Nominator of the transfer function
+ * @param {a} Denominator of the transfer function
+ */
+var FixedBiquadFilter = function(audiolet, b, a) {
+ AudioletNode.call(this, audiolet, 2, 1);
+
+ // Same number of output channels as input channels
+ this.linkNumberOfOutputChannels(0, 0);
+
+ // Delayed values
+ this.xValues = [];
+ this.yValues = [];
+
+ // Coefficients
+ this.b0 = b[0];
+ this.b1 = b[1];
+ this.b2 = b[2];
+ this.a0 = a[0];
+ this.a1 = a[1];
+ this.a2 = a[2];
+};
+extend(FixedBiquadFilter, AudioletNode);
+
+/**
+ * Process a block of samples
+ *
+ * @param {AudioletBuffer[]} inputBuffers Samples received from the inputs.
+ * @param {AudioletBuffer[]} outputBuffers Samples to be sent to the outputs.
+ */
+
+FixedBiquadFilter.prototype.generate = function(inputBuffers, outputBuffers) {
+ var inputBuffer = inputBuffers[0];
+ var outputBuffer = outputBuffers[0];
+
+ if (inputBuffer.isEmpty) {
+ outputBuffer.isEmpty = true;
+ return;
+ }
+
+ var xValueArray = this.xValues;
+ var yValueArray = this.yValues;
+
+ var inputChannels = [];
+ var outputChannels = [];
+ var numberOfChannels = inputBuffer.numberOfChannels;
+ for (var i = 0; i < numberOfChannels; i++) {
+ inputChannels.push(inputBuffer.getChannelData(i));
+ outputChannels.push(outputBuffer.getChannelData(i));
+ if (i >= xValueArray.length) {
+ xValueArray.push([0, 0]);
+ yValueArray.push([0, 0]);
+ }
+ }
+
+ var a0 = this.a0;
+ var a1 = this.a1;
+ var a2 = this.a2;
+ var b0 = this.b0;
+ var b1 = this.b1;
+ var b2 = this.b2;
+
+ var bufferLength = outputBuffer.length;
+ for (var i = 0; i < bufferLength; i++) {
+ for (var j = 0; j < numberOfChannels; j++) {
+ var inputChannel = inputChannels[j];
+ var outputChannel = outputChannels[j];
+
+ var xValues = xValueArray[j];
+ var x1 = xValues[0];
+ var x2 = xValues[1];
+ var yValues = yValueArray[j];
+ var y1 = yValues[0];
+ var y2 = yValues[1];
+
+ var x0 = inputChannel[i];
+ var y0 = (b0 / a0) * x0 +
+ (b1 / a0) * x1 +
+ (b2 / a0) * x2 -
+ (a1 / a0) * y1 -
+ (a2 / a0) * y2;
+
+ outputChannel[i] = y0;
+
+ xValues[0] = x0;
+ xValues[1] = x1;
+ yValues[0] = y0;
+ yValues[1] = y1;
+ }
+ }
+};
+
+/**
+ * toString
+ *
+ * @return {String} String representation.
+ */
+FixedBiquadFilter.prototype.toString = function() {
+ return 'Fixed Biquad Filter';
+};
diff --git a/src/dsp/InteractiveEnvelope.js b/src/dsp/InteractiveEnvelope.js
new file mode 100644
index 0000000..5322066
--- /dev/null
+++ b/src/dsp/InteractiveEnvelope.js
@@ -0,0 +1,76 @@
+/*!
+ * @depends Envelope.js
+ */
+
+/**
+ * Exponential Level Changes
+ *
+ * **Inputs**
+ *
+ * - Asynchronous: Target Level, Decay Time
+ *
+ * **Outputs**
+ *
+ * - Envelope
+ *
+ *
+ * @constructor
+ * @extends Envelope
+ * @param {Audiolet} audiolet The audiolet object.
+ * @param {Number} initial initial level
+ * @param {Number} [trigger] trigger level
+ * @param {Function} [onComplete] A function called when the level sinks
+ * below the trigger level
+ */
+var InteractiveEnvelope = function(audiolet, initial, trigger,
+ onComplete) {
+ this.level = initial;
+ this.targetLevel = initial;
+ this.decay = 1;
+ this.onComplete=onComplete;
+ this.trigger=trigger;
+ AudioletNode.call(this, audiolet, 0, 1);
+};
+
+extend(InteractiveEnvelope, AudioletNode);
+
+/**
+ * toString
+ *
+ * @return {String} String representation.
+ */
+InteractiveEnvelope.prototype.toString = function() {
+ return 'Interactive Envelope';
+};
+
+InteractiveEnvelope.prototype.newTarget = function (target,decay) {
+ this.targetLevel=target;
+ this.decay=decay;
+}
+
+/**
+ * Process a block of samples
+ *
+ * @param {AudioletBuffer[]} inputBuffers Samples received from the inputs.
+ * @param {AudioletBuffer[]} outputBuffers Samples to be sent to the outputs.
+ */
+InteractiveEnvelope.prototype.generate = function(inputBuffers, outputBuffers) {
+
+ var level = this.level;
+ var targetLevel = this.targetLevel;
+ var decay = this.decay;
+ var buffer = outputBuffers[0].getChannelData(0);
+ var bufferLength = buffer.length;
+
+
+ for (var i = 0; i < bufferLength; i++) {
+ level -= (level-targetLevel)*decay;
+ buffer[i]=level;
+ }
+ // console.log(level+ " " + this.trigger);
+ if (level= tableSize) {
- phase %= tableSize;
+ phase = ((phase%tableSize)+tableSize)%tableSize; // javascript % doesn't behave consistently
+
}
channel[i] = table[Math.floor(phase)];
}