From 2f8ee45997ab0c8591807fc32f2625a8d2bdf294 Mon Sep 17 00:00:00 2001 From: Bastiaan Zapf Date: Tue, 13 Sep 2011 16:39:52 +0200 Subject: [PATCH 01/13] Interactive Envelope & Interaction demo --- examples/interaction/css/main.css | 0 examples/interaction/index.htm | 171 ++++++++++++++++++++++++ examples/interaction/js/audiolet_app.js | 65 +++++++++ src/dsp/InteractiveEnvelope.js | 77 +++++++++++ 4 files changed, 313 insertions(+) create mode 100644 examples/interaction/css/main.css create mode 100644 examples/interaction/index.htm create mode 100644 examples/interaction/js/audiolet_app.js create mode 100644 src/dsp/InteractiveEnvelope.js 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..5e8ea8b --- /dev/null +++ b/examples/interaction/index.htm @@ -0,0 +1,171 @@ + + + + + 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..9108cb6 --- /dev/null +++ b/examples/interaction/js/audiolet_app.js @@ -0,0 +1,65 @@ +window.onload = function() { + + this.audiolet = new Audiolet(); + + var play=function(f) { + var X=new Object(); + X.sine = new Sine(this.audiolet, 0); + + X.modulator = new Sine(this.audiolet, f*2.33); + X.modulatorMulAdd = new MulAdd(this.audiolet, 50, f); + X.modulator.connect(X.modulatorMulAdd); + X.modulatorMulAdd.connect(X.sine); + + X.modulator2 = new Multiply(this.audiolet, 1); + X.sine.connect(X.modulator2,0,0); + + X.envelope = new InteractiveEnvelope(this.audiolet,0.1,1e-10,function () { X.modulator2.remove();}); + + X.envelope.connect(X.modulator2,0,1); + X.modulator2.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) { + var p=new Object(); + p.play=play(this.keyboard[e.keyCode]); + playing_notes[e.keyCode]=p; + var kc=e.keyCode; + p.marker=kc; + p.switchOff=function() { p.play.envelope.newTarget(0,2e-1); 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/dsp/InteractiveEnvelope.js b/src/dsp/InteractiveEnvelope.js new file mode 100644 index 0000000..0acef1d --- /dev/null +++ b/src/dsp/InteractiveEnvelope.js @@ -0,0 +1,77 @@ +/*! + * @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); +// Envelope.call(this, audiolet, gate, levels, times, null, onComplete); +}; + +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 Date: Tue, 13 Sep 2011 22:10:46 +0200 Subject: [PATCH 02/13] Bugfix: go back in Loopkup tables (useful for Frequency Modulation) --- src/audiolet/Audiolet.js | 2 +- src/dsp/TableLookupOscillator.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/audiolet/Audiolet.js b/src/audiolet/Audiolet.js index d0fbb02..0adba4e 100644 --- a/src/audiolet/Audiolet.js +++ b/src/audiolet/Audiolet.js @@ -3437,7 +3437,7 @@ TableLookupOscillator.prototype.generate = function(inputBuffers, frequency = frequencyChannel[i]; } var step = frequency * tableSize / sampleRate; - phase += step; + phase += step + tableSize; // javascript % doesn't behave consistently if (phase >= tableSize) { phase %= tableSize; } diff --git a/src/dsp/TableLookupOscillator.js b/src/dsp/TableLookupOscillator.js index bc3a022..efd52a8 100644 --- a/src/dsp/TableLookupOscillator.js +++ b/src/dsp/TableLookupOscillator.js @@ -62,7 +62,7 @@ TableLookupOscillator.prototype.generate = function(inputBuffers, frequency = frequencyChannel[i]; } var step = frequency * tableSize / sampleRate; - phase += step; + phase += step + tableSize; // javascript % doesn't behave consistently if (phase >= tableSize) { phase %= tableSize; } From a01c5df7ec4c51582ddd8f712a21cab3c8cdf095 Mon Sep 17 00:00:00 2001 From: Bastiaan Zapf Date: Tue, 13 Sep 2011 22:11:34 +0200 Subject: [PATCH 03/13] Fixed a bug in FM code --- examples/interaction/index.htm | 1 + examples/interaction/js/audiolet_app.js | 21 +++++++++++---------- src/dsp/InteractiveEnvelope.js | 1 - 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/examples/interaction/index.htm b/examples/interaction/index.htm index 5e8ea8b..067b3e3 100644 --- a/examples/interaction/index.htm +++ b/examples/interaction/index.htm @@ -12,6 +12,7 @@ + diff --git a/examples/interaction/js/audiolet_app.js b/examples/interaction/js/audiolet_app.js index 9108cb6..431ea8c 100644 --- a/examples/interaction/js/audiolet_app.js +++ b/examples/interaction/js/audiolet_app.js @@ -4,20 +4,21 @@ window.onload = function() { var play=function(f) { var X=new Object(); - X.sine = new Sine(this.audiolet, 0); + X.sine = new Sine(this.audiolet,f); - X.modulator = new Sine(this.audiolet, f*2.33); - X.modulatorMulAdd = new MulAdd(this.audiolet, 50, f); - X.modulator.connect(X.modulatorMulAdd); - X.modulatorMulAdd.connect(X.sine); + X.modulator = new Sine(this.audiolet, f); - X.modulator2 = new Multiply(this.audiolet, 1); - X.sine.connect(X.modulator2,0,0); + 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.envelope = new InteractiveEnvelope(this.audiolet,0.1,1e-10,function () { X.modulator2.remove();}); + X.amplitude = new Multiply(this.audiolet, 1); + X.sine.connect(X.amplitude); - X.envelope.connect(X.modulator2,0,1); - X.modulator2.connect(this.audiolet.output); + X.envelope = new InteractiveEnvelope(this.audiolet,0.1,1e-10,function () { X.amplitude.remove();}); + + X.envelope.connect(X.amplitude,0,1); + X.amplitude.connect(this.audiolet.output); X.envelope.newTarget(0.02,1e-5); return X; diff --git a/src/dsp/InteractiveEnvelope.js b/src/dsp/InteractiveEnvelope.js index 0acef1d..5322066 100644 --- a/src/dsp/InteractiveEnvelope.js +++ b/src/dsp/InteractiveEnvelope.js @@ -30,7 +30,6 @@ var InteractiveEnvelope = function(audiolet, initial, trigger, this.onComplete=onComplete; this.trigger=trigger; AudioletNode.call(this, audiolet, 0, 1); -// Envelope.call(this, audiolet, gate, levels, times, null, onComplete); }; extend(InteractiveEnvelope, AudioletNode); From 200687f192d366dec5977c94dbd244ec7bf6f8b3 Mon Sep 17 00:00:00 2001 From: Bastiaan Zapf Date: Tue, 13 Sep 2011 22:15:51 +0200 Subject: [PATCH 04/13] Better bugfix for TableLookupOscillator wraparound bug --- src/audiolet/Audiolet.js | 4 ++-- src/dsp/TableLookupOscillator.js | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/audiolet/Audiolet.js b/src/audiolet/Audiolet.js index 0adba4e..3c6ad17 100644 --- a/src/audiolet/Audiolet.js +++ b/src/audiolet/Audiolet.js @@ -3437,9 +3437,9 @@ TableLookupOscillator.prototype.generate = function(inputBuffers, frequency = frequencyChannel[i]; } var step = frequency * tableSize / sampleRate; - phase += step + tableSize; // javascript % doesn't behave consistently + 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/dsp/TableLookupOscillator.js b/src/dsp/TableLookupOscillator.js index efd52a8..70e842d 100644 --- a/src/dsp/TableLookupOscillator.js +++ b/src/dsp/TableLookupOscillator.js @@ -62,9 +62,10 @@ TableLookupOscillator.prototype.generate = function(inputBuffers, frequency = frequencyChannel[i]; } var step = frequency * tableSize / sampleRate; - phase += step + tableSize; // javascript % doesn't behave consistently + phase += step; if (phase >= tableSize) { - phase %= tableSize; + phase = ((phase%tableSize)+tableSize)%tableSize; // javascript % doesn't behave consistently + } channel[i] = table[Math.floor(phase)]; } From 26f83b04537e542370285ed78c97c4850af96fbd Mon Sep 17 00:00:00 2001 From: Bastiaan Zapf Date: Wed, 21 Sep 2011 19:33:41 +0200 Subject: [PATCH 05/13] Interaction example updated --- examples/interaction/index.htm | 9 +++++++++ examples/interaction/js/audiolet_app.js | 9 +++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/examples/interaction/index.htm b/examples/interaction/index.htm index 067b3e3..3ddb6a6 100644 --- a/examples/interaction/index.htm +++ b/examples/interaction/index.htm @@ -166,6 +166,15 @@ + diff --git a/examples/interaction/js/audiolet_app.js b/examples/interaction/js/audiolet_app.js index 431ea8c..8070eaf 100644 --- a/examples/interaction/js/audiolet_app.js +++ b/examples/interaction/js/audiolet_app.js @@ -15,7 +15,7 @@ window.onload = function() { 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();}); + 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); @@ -44,15 +44,16 @@ window.onload = function() { this.playing_notes=Array(); - window.onkeydown=function(e) { + 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(this.keyboard[e.keyCode]); + 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-1); playing_notes[kc]=null;} + p.switchOff=function() { p.play.envelope.newTarget(0,2e-2); playing_notes[kc]=null;} } } } From 1177690ded3489d4e636b1d21a816bdd62427238 Mon Sep 17 00:00:00 2001 From: Bastiaan Zapf Date: Wed, 21 Sep 2011 19:34:45 +0200 Subject: [PATCH 06/13] Bandlimited Synthesis (doesn't work correctly yet, problem with filter) Oscilloscope --- examples/bandlimited/css/main.css | 0 examples/bandlimited/index.htm | 187 ++++++++++++++++++++++++ examples/bandlimited/js/audiolet_app.js | 67 +++++++++ src/core/Oscilloscope.js | 58 ++++++++ src/dsp/Bandlimited.js | 91 ++++++++++++ src/dsp/Blit.js | 80 ++++++++++ src/dsp/FixedBiquadFilter.js | 120 +++++++++++++++ 7 files changed, 603 insertions(+) create mode 100644 examples/bandlimited/css/main.css create mode 100644 examples/bandlimited/index.htm create mode 100644 examples/bandlimited/js/audiolet_app.js create mode 100644 src/core/Oscilloscope.js create mode 100644 src/dsp/Bandlimited.js create mode 100644 src/dsp/Blit.js create mode 100644 src/dsp/FixedBiquadFilter.js 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..1c5e2cf --- /dev/null +++ b/examples/bandlimited/js/audiolet_app.js @@ -0,0 +1,67 @@ +window.onload = function() { + + this.audiolet = new Audiolet(); + + var play=function(f,canvas) { + var X=new Object(); + X.square = new BlitSquare(this.audiolet,f); + + X.amplitude = new Multiply(this.audiolet, 1); + X.square.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); + + if (canvas) { + X.osci = new Oscilloscope(this.audiolet,undefined,canvas); + X.amplitude.connect(X.osci); + X.osci.connect(this.audiolet.output); + } else { + X.amplitude.connect(this.audiolet.output); + } + X.envelope.newTarget(0.02,1e-5); + + return X; + } + + x=play(440,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-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/core/Oscilloscope.js b/src/core/Oscilloscope.js new file mode 100644 index 0000000..e8b9302 --- /dev/null +++ b/src/core/Oscilloscope.js @@ -0,0 +1,58 @@ + +/** + * An oscilloscope + * + * **Inputs** + * + * - Signal + * + * **Outputs** + * + * + * **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.hold = false; + this.wait = 0; +}; + +extend(Oscilloscope, AudioletNode); + +Oscilloscope.prototype.generate = function(inputBuffers,outputBuffers) { + var c=inputBuffers[0].getChannelData(0); + var o=outputBuffers[0].getChannelData(0); + var width=this.width,height=this.height,gc=this.gc; + var h2=height/2; + if (!this.hold) { + this.canvas.width=width; + gc.fillStyle='#ffffff'; + gc.strokeStyle='#000000'; + gc.beginPath(); + gc.moveTo(0,c[0]*h2+h2); + } + for (var i=0;i1) + this.hold=true; + } +} diff --git a/src/dsp/Bandlimited.js b/src/dsp/Bandlimited.js new file mode 100644 index 0000000..d9a8212 --- /dev/null +++ b/src/dsp/Bandlimited.js @@ -0,0 +1,91 @@ + +/** + * 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(n) { + 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; + P=(44100/frequency); + M=P; + M=(Math.floor(M/2)*2); // even number, lower than the original M + this.Blit=Blit(P,M,0); + + this.filter=new FixedBiquadFilter(audiolet,[0.1,0,0],[1,-0.9999999,0]); + this.connect(this.filter); + return this.filter; +}; + +extend(BlitSquare, AudioletNode); + + +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'; +}; + diff --git a/src/dsp/Blit.js b/src/dsp/Blit.js new file mode 100644 index 0000000..e0be95c --- /dev/null +++ b/src/dsp/Blit.js @@ -0,0 +1,80 @@ +/*! + * @depends TableLookupOscillator.js + */ + +/** + * Bandlimited Impulse Train Generator + * + * **Inputs** + * + * - Frequency + * + * **Outputs** + * + * - Bandlimited Impulse Wave + * + * **Parameters** + * + * - frequency The frequency of the oscillator. Linked to input 0. + * + * @constructor + * @param {Audiolet} audiolet The audiolet object. + * @param {Number} [frequency=440] Initial frequency. + */ + +var Blit = function(audiolet, frequency) { + /** + * 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 samples. If odd is + * false, half period. + * @param {M} number of synthesized Partials + * @param {odd} assert that M is odd/even. + * + */ + function blit(P,M,odd) { + var n=0; + if ((odd && (M%2!=1)) || + (even && (M%2!=0))) { + throw "Erroneous Number of Partials in Blit."; + } + return function() { + 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)); + } + } +} + + + TableLookupOscillator.call(this, audiolet, Square.TABLE, frequency); +}; +extend(Square, TableLookupOscillator); + +/** + * toString + * + * @return {String} String representation. + */ +Square.prototype.toString = function() { + return 'Square'; +}; + +/** + * Square wave table + */ +Square.TABLE = []; +for (var i = 0; i < 8192; i++) { + Square.TABLE.push(((i - 4096) / 8192) < 0 ? 1 : -1); +} + + 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'; +}; From 37284646ca3579724d8ed402cb6da074165f1c56 Mon Sep 17 00:00:00 2001 From: Bastiaan Zapf Date: Wed, 21 Sep 2011 20:45:25 +0200 Subject: [PATCH 07/13] Bandlimited Synthesis works half way --- examples/bandlimited/js/audiolet_app.js | 3 + src/core/Oscilloscope.js | 43 +++++---- src/dsp/Bandlimited.js | 16 ++-- src/dsp/Blit.js | 112 ++++++++++++------------ 4 files changed, 96 insertions(+), 78 deletions(-) diff --git a/examples/bandlimited/js/audiolet_app.js b/examples/bandlimited/js/audiolet_app.js index 1c5e2cf..4b6464c 100644 --- a/examples/bandlimited/js/audiolet_app.js +++ b/examples/bandlimited/js/audiolet_app.js @@ -16,6 +16,9 @@ window.onload = function() { 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); }; + X.osci.paint(); + } else { X.amplitude.connect(this.audiolet.output); } diff --git a/src/core/Oscilloscope.js b/src/core/Oscilloscope.js index e8b9302..8ed5288 100644 --- a/src/core/Oscilloscope.js +++ b/src/core/Oscilloscope.js @@ -8,6 +8,7 @@ * * **Outputs** * + * - Unmodified Signal (necessary, since Audiolet uses a "pull" architecture) * * **Parameters** * @@ -26,8 +27,9 @@ var Oscilloscope = function(audiolet,signal,canvas) { this.gc = canvas.getContext('2d'); this.width = canvas.width; this.height = canvas.height; - this.hold = false; - this.wait = 0; + this.displayeddata = Array(); + this.displayedpointer = 0; + this.count=0; }; extend(Oscilloscope, AudioletNode); @@ -35,24 +37,31 @@ 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,c[0]*h2+h2); - } - for (var i=0;i1) - this.hold=true; + gc.moveTo(0,this.displayeddata[0]*h2*factor+h2); + for (var i=0;i 1e-5)) + 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*Math.cos(x)/(P*Math.cos(t)); + return M/P*Math.cos(x)/(Math.cos(t)); } }; @@ -55,12 +54,15 @@ BlitSquare = function(audiolet,frequency) { AudioletNode.call(this, audiolet, 0, 1); this.frequency = frequency || 440; this.phase = 0; - P=(44100/frequency); + P=(44100.0/frequency); + console.log(P); M=P; M=(Math.floor(M/2)*2); // even number, lower than the original M + //M=8; + // M=16; this.Blit=Blit(P,M,0); - this.filter=new FixedBiquadFilter(audiolet,[0.1,0,0],[1,-0.9999999,0]); + this.filter=new FixedBiquadFilter(audiolet,[1,0,0],[1,-0.9999,0]); this.connect(this.filter); return this.filter; }; diff --git a/src/dsp/Blit.js b/src/dsp/Blit.js index e0be95c..2646b90 100644 --- a/src/dsp/Blit.js +++ b/src/dsp/Blit.js @@ -1,80 +1,84 @@ -/*! - * @depends TableLookupOscillator.js - */ /** - * Bandlimited Impulse Train Generator + * 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** * - * - Frequency + * none (frequency modulation is hard to get right with the closed formula above) * * **Outputs** * - * - Bandlimited Impulse Wave + * - Bandlimited Square Wave * * **Parameters** * - * - frequency The frequency of the oscillator. Linked to input 0. - * * @constructor * @param {Audiolet} audiolet The audiolet object. - * @param {Number} [frequency=440] Initial frequency. + * @param {Number} [frequency=440] Frequency. */ -var Blit = function(audiolet, frequency) { - /** - * 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 samples. If odd is - * false, half period. - * @param {M} number of synthesized Partials - * @param {odd} assert that M is odd/even. - * - */ - function blit(P,M,odd) { - var n=0; - if ((odd && (M%2!=1)) || - (even && (M%2!=0))) { - throw "Erroneous Number of Partials in Blit."; - } - return function() { - 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)); - } - } -} +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); - TableLookupOscillator.call(this, audiolet, Square.TABLE, frequency); + var bufferLength = buffer.length; + for (var i = 0; i < bufferLength; i++) { + channel[i] = this.Blit(this.phase++); + } }; -extend(Square, TableLookupOscillator); /** * toString * * @return {String} String representation. */ -Square.prototype.toString = function() { - return 'Square'; +BlitSquare.prototype.toString = function() { + return 'BlitSquare'; }; -/** - * Square wave table - */ -Square.TABLE = []; -for (var i = 0; i < 8192; i++) { - Square.TABLE.push(((i - 4096) / 8192) < 0 ? 1 : -1); -} - - +extend(BlitSquare, AudioletNode); From f4944a05cbbc284263691997dfa9491b11d9b650 Mon Sep 17 00:00:00 2001 From: Bastiaan Zapf Date: Fri, 23 Sep 2011 14:31:05 +0200 Subject: [PATCH 08/13] clean up debugging remains --- src/dsp/Bandlimited.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/dsp/Bandlimited.js b/src/dsp/Bandlimited.js index 9281485..a5be6b1 100644 --- a/src/dsp/Bandlimited.js +++ b/src/dsp/Bandlimited.js @@ -1,6 +1,6 @@ /** - * BLIT closed formula, cf. + * Bandlimited impulse train closed formula, cf. * http://www.music.mcgill.ca/~gary/307/week5/bandlimited.html * * It is possible to synthesize a sign-alternating impulse train @@ -55,13 +55,15 @@ BlitSquare = function(audiolet,frequency) { this.frequency = frequency || 440; this.phase = 0; P=(44100.0/frequency); - console.log(P); - M=P; - M=(Math.floor(M/2)*2); // even number, lower than the original M - //M=8; - // M=16; + + M=Math.floor(P/2)*2; // even number, lower than the original P + + // bipolar BLIT + this.Blit=Blit(P,M,0); + // a leaky integrator -> process bipolar BLIT to square wave + this.filter=new FixedBiquadFilter(audiolet,[1,0,0],[1,-0.9999,0]); this.connect(this.filter); return this.filter; @@ -69,7 +71,6 @@ BlitSquare = function(audiolet,frequency) { extend(BlitSquare, AudioletNode); - BlitSquare.prototype.generate = function(inputBuffers, outputBuffers) { var buffer = outputBuffers[0]; @@ -91,3 +92,5 @@ BlitSquare.prototype.toString = function() { return 'BlitSquare'; }; + + From b90d628300ad5ab550c3dbe3a90770ae22c1f7c9 Mon Sep 17 00:00:00 2001 From: Bastiaan Zapf Date: Fri, 23 Sep 2011 18:28:38 +0200 Subject: [PATCH 09/13] Better comments in Bandlimited.js Bandlimited Sawtooth --- examples/bandlimited/js/audiolet_app.js | 2 +- src/dsp/Bandlimited.js | 70 ++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/examples/bandlimited/js/audiolet_app.js b/examples/bandlimited/js/audiolet_app.js index 4b6464c..65a607d 100644 --- a/examples/bandlimited/js/audiolet_app.js +++ b/examples/bandlimited/js/audiolet_app.js @@ -4,7 +4,7 @@ window.onload = function() { var play=function(f,canvas) { var X=new Object(); - X.square = new BlitSquare(this.audiolet,f); + X.square = new BlitSaw(this.audiolet,f); X.amplitude = new Multiply(this.audiolet, 1); X.square.connect(X.amplitude); diff --git a/src/dsp/Bandlimited.js b/src/dsp/Bandlimited.js index a5be6b1..d92cda6 100644 --- a/src/dsp/Bandlimited.js +++ b/src/dsp/Bandlimited.js @@ -56,13 +56,13 @@ BlitSquare = function(audiolet,frequency) { this.phase = 0; P=(44100.0/frequency); - M=Math.floor(P/2)*2; // even number, lower than the original P + M=Math.floor(P/2)*2; // even number of harmonics, lower than the original P // bipolar BLIT this.Blit=Blit(P,M,0); - // a leaky integrator -> process bipolar BLIT to square wave + // leaky integrator - bipolar BLIT to square wave this.filter=new FixedBiquadFilter(audiolet,[1,0,0],[1,-0.9999,0]); this.connect(this.filter); @@ -94,3 +94,69 @@ BlitSquare.prototype.toString = function() { +/** + * 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. + */ + +BlitSaw = 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.Blit=Blit(P,M,1); + this.P=P; + + // offset 1/2 - integral (DC part) of signal will be 0 + + this.add=new Add(audiolet,(-1/P)); + this.connect(this.add); + + // leaky integrator - offset BLIT to saw wave + + this.filter=new FixedBiquadFilter(audiolet,[1,0,0],[1,-0.9999,0]); + this.add.connect(this.filter); + + return this.filter; +}; + +extend(BlitSaw, AudioletNode); + +BlitSaw.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. + */ +BlitSaw.prototype.toString = function() { + return 'BlitSaw'; +}; From fce4f6dc9fecc71721fb85eda5501549f7d0443a Mon Sep 17 00:00:00 2001 From: Bastiaan Zapf Date: Sat, 24 Sep 2011 14:40:01 +0200 Subject: [PATCH 10/13] improved internal architecture: Avoid use of "fixed Biquad" --- src/dsp/Bandlimited.js | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/dsp/Bandlimited.js b/src/dsp/Bandlimited.js index d92cda6..72afd9b 100644 --- a/src/dsp/Bandlimited.js +++ b/src/dsp/Bandlimited.js @@ -64,9 +64,7 @@ BlitSquare = function(audiolet,frequency) { // leaky integrator - bipolar BLIT to square wave - this.filter=new FixedBiquadFilter(audiolet,[1,0,0],[1,-0.9999,0]); - this.connect(this.filter); - return this.filter; + this.store=0; }; extend(BlitSquare, AudioletNode); @@ -78,7 +76,10 @@ BlitSquare.prototype.generate = function(inputBuffers, var bufferLength = buffer.length; for (var i = 0; i < bufferLength; i++) { - channel[i] = this.Blit(this.phase++); + var blit=this.Blit(this.phase++); + channel[i] = blit+this.store; + this.store += blit; + this.store *= 0.9999; } }; @@ -127,15 +128,8 @@ BlitSaw = function(audiolet,frequency) { // offset 1/2 - integral (DC part) of signal will be 0 - this.add=new Add(audiolet,(-1/P)); - this.connect(this.add); - - // leaky integrator - offset BLIT to saw wave - - this.filter=new FixedBiquadFilter(audiolet,[1,0,0],[1,-0.9999,0]); - this.add.connect(this.filter); - - return this.filter; + this.offset=-1/P; + this.store=0; }; extend(BlitSaw, AudioletNode); @@ -147,7 +141,10 @@ BlitSaw.prototype.generate = function(inputBuffers, var bufferLength = buffer.length; for (var i = 0; i < bufferLength; i++) { - channel[i] = this.Blit(this.phase++); + var blit= this.Blit(this.phase++)+this.offset; + channel[i] = blit+this.store; + this.store += blit; + this.store *= 0.9999; } }; From 7df332ca5f109c24fea124cc883c2ac360c3399f Mon Sep 17 00:00:00 2001 From: Bastiaan Zapf Date: Sat, 24 Sep 2011 14:44:42 +0200 Subject: [PATCH 11/13] fixed square wave octave bug --- src/dsp/Bandlimited.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dsp/Bandlimited.js b/src/dsp/Bandlimited.js index 72afd9b..aad117d 100644 --- a/src/dsp/Bandlimited.js +++ b/src/dsp/Bandlimited.js @@ -54,7 +54,7 @@ BlitSquare = function(audiolet,frequency) { AudioletNode.call(this, audiolet, 0, 1); this.frequency = frequency || 440; this.phase = 0; - P=(44100.0/frequency); + 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 From 9c1d98e704e8babb8f960e0f554691008b2e5605 Mon Sep 17 00:00:00 2001 From: Bastiaan Zapf Date: Sat, 24 Sep 2011 20:09:09 +0200 Subject: [PATCH 12/13] Triangular wave works. This is not yet perfect. --- examples/bandlimited/js/audiolet_app.js | 20 ++++-- src/core/Oscilloscope.js | 4 +- src/dsp/Bandlimited.js | 87 ++++++++++++++++++++++++- 3 files changed, 101 insertions(+), 10 deletions(-) diff --git a/examples/bandlimited/js/audiolet_app.js b/examples/bandlimited/js/audiolet_app.js index 65a607d..a5d0218 100644 --- a/examples/bandlimited/js/audiolet_app.js +++ b/examples/bandlimited/js/audiolet_app.js @@ -4,12 +4,17 @@ window.onload = function() { var play=function(f,canvas) { var X=new Object(); - X.square = new BlitSaw(this.audiolet,f); + X.square = new BlitSquare(this.audiolet,f); + X.saw = new BlitSaw(this.audiolet,f); + X.tri = new BlitTri(this.audiolet,f); X.amplitude = new Multiply(this.audiolet, 1); - X.square.connect(X.amplitude); + // 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 = new InteractiveEnvelope(this.audiolet,0.1,1e-10,function () { X.amplitude.remove(); delete X; }); X.envelope.connect(X.amplitude,0,1); if (canvas) { @@ -17,18 +22,19 @@ window.onload = function() { 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.02,1e-5); + X.envelope.newTarget(0.002,1e-5); return X; } - x=play(440,document.getElementById('scope')); - window.setTimeout(function () { x.envelope.newTarget(0,2e-2); },200); + // 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, @@ -56,7 +62,7 @@ window.onload = function() { 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;} + p.switchOff=function() { p.play.envelope.newTarget(0,2e-4); playing_notes[kc]=null;} } } } diff --git a/src/core/Oscilloscope.js b/src/core/Oscilloscope.js index 8ed5288..ec01103 100644 --- a/src/core/Oscilloscope.js +++ b/src/core/Oscilloscope.js @@ -47,7 +47,7 @@ Oscilloscope.prototype.generate = function(inputBuffers,outputBuffers) { Oscilloscope.prototype.paint = function () { var width=this.width,height=this.height,gc=this.gc; - var factor=1; + var factor=10; var h2=height/2; if (this.displayedpointer>this.width) { this.canvas.width=this.width; @@ -62,6 +62,6 @@ Oscilloscope.prototype.paint = function () { this.displayedpointer=0; this.count++; } - if (this.count<5) + if (this.count<0) this.raf(); } \ No newline at end of file diff --git a/src/dsp/Bandlimited.js b/src/dsp/Bandlimited.js index aad117d..5cae48d 100644 --- a/src/dsp/Bandlimited.js +++ b/src/dsp/Bandlimited.js @@ -62,7 +62,7 @@ BlitSquare = function(audiolet,frequency) { this.Blit=Blit(P,M,0); - // leaky integrator - bipolar BLIT to square wave + // initialize integrator storage this.store=0; }; @@ -157,3 +157,88 @@ BlitSaw.prototype.generate = function(inputBuffers, BlitSaw.prototype.toString = function() { return 'BlitSaw'; }; + + +/** + * 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. + */ + + +BlitTri = 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.Blit=Blit(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(BlitTri, AudioletNode); + +BlitTri.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.Blit(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. + */ +BlitTri.prototype.toString = function() { + return 'BlitTri'; +}; + From 7363205112fa8d7d53d66674f40e645304d6176f Mon Sep 17 00:00:00 2001 From: Bastiaan Zapf Date: Wed, 5 Oct 2011 23:15:38 +0200 Subject: [PATCH 13/13] Some function names replaced --- examples/bandlimited/js/audiolet_app.js | 6 ++-- src/dsp/Bandlimited.js | 48 ++++++++++++------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/examples/bandlimited/js/audiolet_app.js b/examples/bandlimited/js/audiolet_app.js index a5d0218..73ef8a1 100644 --- a/examples/bandlimited/js/audiolet_app.js +++ b/examples/bandlimited/js/audiolet_app.js @@ -4,9 +4,9 @@ window.onload = function() { var play=function(f,canvas) { var X=new Object(); - X.square = new BlitSquare(this.audiolet,f); - X.saw = new BlitSaw(this.audiolet,f); - X.tri = new BlitTri(this.audiolet,f); + 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); diff --git a/src/dsp/Bandlimited.js b/src/dsp/Bandlimited.js index 5cae48d..25933df 100644 --- a/src/dsp/Bandlimited.js +++ b/src/dsp/Bandlimited.js @@ -14,13 +14,13 @@ * @returns a function that will calculate a Bandlimited impulse train * depending on a time parameter */ -function Blit(P,M,odd) { +function BandlimitedImpulseTrain(P,M,odd) { if ((odd && (M%2!=1)) || (!odd && (M%2!=0))) { - throw "Erroneous Number of Partials in Blit."; + throw "Erroneous Number of Partials in BandlimitedImpulseTrain."; } if (M>P) { - throw "Too many Partials in Blit."; + throw "Too many Partials in BandlimitedImpulseTrain."; } return function(n) { var x=Math.PI*M*n/P; @@ -50,7 +50,7 @@ function Blit(P,M,odd) { * @param {Number} [frequency=440] Frequency. */ -BlitSquare = function(audiolet,frequency) { +BandlimitedSquare = function(audiolet,frequency) { AudioletNode.call(this, audiolet, 0, 1); this.frequency = frequency || 440; this.phase = 0; @@ -60,23 +60,23 @@ BlitSquare = function(audiolet,frequency) { // bipolar BLIT - this.Blit=Blit(P,M,0); + this.BandlimitedImpulseTrain=BandlimitedImpulseTrain(P,M,0); // initialize integrator storage this.store=0; }; -extend(BlitSquare, AudioletNode); +extend(BandlimitedSquare, AudioletNode); -BlitSquare.prototype.generate = function(inputBuffers, +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.Blit(this.phase++); + var blit=this.BandlimitedImpulseTrain(this.phase++); channel[i] = blit+this.store; this.store += blit; this.store *= 0.9999; @@ -89,8 +89,8 @@ BlitSquare.prototype.generate = function(inputBuffers, * * @return {String} String representation. */ -BlitSquare.prototype.toString = function() { - return 'BlitSquare'; +BandlimitedSquare.prototype.toString = function() { + return 'BandlimitedSquare'; }; @@ -113,7 +113,7 @@ BlitSquare.prototype.toString = function() { * @param {Number} [frequency=440] Frequency. */ -BlitSaw = function(audiolet,frequency) { +BandlimitedSaw = function(audiolet,frequency) { AudioletNode.call(this, audiolet, 0, 1); this.frequency = frequency || 440; this.phase = 0; @@ -123,7 +123,7 @@ BlitSaw = function(audiolet,frequency) { // unipolar BLIT - this.Blit=Blit(P,M,1); + this.BandlimitedImpulseTrain=BandlimitedImpulseTrain(P,M,1); this.P=P; // offset 1/2 - integral (DC part) of signal will be 0 @@ -132,16 +132,16 @@ BlitSaw = function(audiolet,frequency) { this.store=0; }; -extend(BlitSaw, AudioletNode); +extend(BandlimitedSaw, AudioletNode); -BlitSaw.prototype.generate = function(inputBuffers, +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.Blit(this.phase++)+this.offset; + var blit= this.BandlimitedImpulseTrain(this.phase++)+this.offset; channel[i] = blit+this.store; this.store += blit; this.store *= 0.9999; @@ -154,8 +154,8 @@ BlitSaw.prototype.generate = function(inputBuffers, * * @return {String} String representation. */ -BlitSaw.prototype.toString = function() { - return 'BlitSaw'; +BandlimitedSaw.prototype.toString = function() { + return 'BandlimitedSaw'; }; @@ -178,7 +178,7 @@ BlitSaw.prototype.toString = function() { */ -BlitTri = function(audiolet,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 @@ -189,7 +189,7 @@ BlitTri = function(audiolet,frequency) { // bipolar BLIT this.Period=P; - this.Blit=Blit(P,M,0); + this.BandlimitedImpulseTrain=BandlimitedImpulseTrain(P,M,0); // initialize integrator storage @@ -203,9 +203,9 @@ BlitTri = function(audiolet,frequency) { console.log("Tri!"); }; -extend(BlitTri, AudioletNode); +extend(BandlimitedTriangle, AudioletNode); -BlitTri.prototype.generate = function(inputBuffers, +BandlimitedTriangle.prototype.generate = function(inputBuffers, outputBuffers) { var buffer = outputBuffers[0]; var channel = buffer.getChannelData(0); @@ -215,7 +215,7 @@ BlitTri.prototype.generate = function(inputBuffers, var frequency=1/this.Period; var frequency2=0.5/this.Period; for (var i = 0; i < bufferLength; i++) { - var blit=this.Blit(1+this.phase++); + var blit=this.BandlimitedImpulseTrain(1+this.phase++); this.store[0] += blit*frequency; this.store[0] *= 0.9999; this.offset *= 0.9999; @@ -238,7 +238,7 @@ BlitTri.prototype.generate = function(inputBuffers, * * @return {String} String representation. */ -BlitTri.prototype.toString = function() { - return 'BlitTri'; +BandlimitedTriangle.prototype.toString = function() { + return 'BandlimitedTriangle'; };