Skip to content

Commit

Permalink
OSC1 + OSC2, filter, envelopes completely implemented.
Browse files Browse the repository at this point in the history
Still to do: modulation, drive, reverb
  • Loading branch information
cwilso committed Sep 6, 2012
1 parent bd29540 commit 84681ce
Show file tree
Hide file tree
Showing 3 changed files with 277 additions and 61 deletions.
4 changes: 2 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
body { background-color: #999999; overflow: hidden; font: 12px 'Syncopate', sans-serif;}
#draw { height: 500px; }
#synthbox {
position:relative; width: 1000px; height: 380px; background: #222222; border: 3px solid #AAA; border-radius: 10px}
.loaded#synthbox { border-color: green;}
position:relative; width: 910px; height: 380px; background: #222222; border: 3px solid blue; border-radius: 10px}
.loaded#synthbox { border-color: #AAA;}
.error#synthbox { border-color: red;}
.section { position: absolute; }
.knobContainer { position:absolute; width: 100px; height:100px; overflow: visible }
Expand Down
263 changes: 230 additions & 33 deletions js/synth.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
var voices = new Array();
var audioContext = null;
var FOURIER_SIZE = 4096;
var wave = false;
var ATTACKTIME = 0.1;
var RELEASETIME = 0.5;
var currentFilterFrequency = 2500;
var currentFilterQ = 10;
var currentOsc1Waveform = 0; // SINE

var synthBox = null;
var currentOsc1Waveform = 2; // SAW
var currentOsc1Octave = 1; // 16'
var currentOsc1Detune = 0; // 0
var currentOsc1Mix = 50.0; // 50%

var currentOsc2Waveform = 2; // SAW
var currentOsc2Octave = 1; // 8'
var currentOsc2Detune = 15; // slight detune makes pretty analogue-y sound. :)
var currentOsc2Mix = 50.0; // 0%

var currentFilterFrequency = 1492.0;
var currentFilterQ = 5.0;
var currentFilterMod = 0;
var currentFilterEnv = 67;

var currentEnvA = 10;
var currentEnvD = 15;
var currentEnvS = 75;
var currentEnvR = 20;

var currentFilterEnvA = 35;
var currentFilterEnvD = 15;
var currentFilterEnvS = 50;
var currentFilterEnvR = 40;

var currentRev = 0;
var currentOverdrive = 0;
var currentVol = 50;

var keys = new Array( 256 );
keys[65] = 60; // = C4
keys[65] = 60; // = C4 ("middle C")
keys[87] = 61;
keys[83] = 62;
keys[69] = 63;
Expand Down Expand Up @@ -56,19 +77,22 @@ function noteOff( note ) {
function controller( number, value ) {
if (number == 1) {
currentFilterFrequency = 5000 * value;
$("#fFreq input").val( currentFilterFrequency );
$("#fFreq input").trigger('change');
$("#fFreq").val( currentFilterFrequency );
$("#fFreq").trigger('change');
onUpdateFilterFrequency( currentFilterFrequency );
return;
} else if (number == 2) {
currentFilterQ = 40 * value;
$("#fQ input").val( currentFilterFrequency );
$("#fQ input").trigger('change');
$("#fQ").val( currentFilterQ );
$("#fQ").trigger('change');
onUpdateFilterQ( currentFilterQ );
}
}

function onUpdateFilterFrequency( value ) {
if (value.currentTarget)
value = value.currentTarget.value;
currentFilterFrequency = value;
// console.log("update Filter Freq: " + value);
for (var i=0; i<255; i++) {
if (voices[i] != null) {
Expand All @@ -80,6 +104,7 @@ function onUpdateFilterFrequency( value ) {
function onUpdateFilterQ( value ) {
if (value.currentTarget)
value = value.currentTarget.value;
currentFilterQ = value;
// console.log("update Filter Q: " + value);
for (var i=0; i<255; i++) {
if (voices[i] != null) {
Expand All @@ -88,6 +113,10 @@ function onUpdateFilterQ( value ) {
}
}

function onUpdateFilterEnv( value ) {
currentFilterEnv = value;
}

function onUpdateOsc1Wave( ev ) {
currentOsc1Waveform = ev.target.selectedIndex;
for (var i=0; i<255; i++) {
Expand All @@ -97,7 +126,113 @@ function onUpdateOsc1Wave( ev ) {
}
}

function onUpdateOsc1Octave( ev ) {
currentOsc1Octave = ev.target.selectedIndex;
for (var i=0; i<255; i++) {
if (voices[i] != null) {
voices[i].updateOsc1Frequency();
}
}
}

function onUpdateOsc1Detune( value ) {
if (value.currentTarget)
value = value.currentTarget.value;
currentOsc1Detune = value;
for (var i=0; i<255; i++) {
if (voices[i] != null) {
voices[i].updateOsc1Frequency();
}
}
}

function onUpdateOsc1Mix( value ) {
if (value.currentTarget)
value = value.currentTarget.value;
currentOsc1Mix = value;
for (var i=0; i<255; i++) {
if (voices[i] != null) {
voices[i].updateOsc1Mix( value );
}
}
}

function onUpdateOsc2Wave( ev ) {
currentOsc2Waveform = ev.target.selectedIndex;
for (var i=0; i<255; i++) {
if (voices[i] != null) {
voices[i].setOsc2Waveform( currentOsc2Waveform );
}
}
}

function onUpdateOsc2Octave( ev ) {
currentOsc2Octave = ev.target.selectedIndex;
for (var i=0; i<255; i++) {
if (voices[i] != null) {
voices[i].updateOsc2Frequency();
}
}
}

function onUpdateOsc2Detune( value ) {
if (value.currentTarget)
value = value.currentTarget.value;
currentOsc2Detune = value;
for (var i=0; i<255; i++) {
if (voices[i] != null) {
voices[i].updateOsc2Frequency();
}
}
}

function onUpdateOsc2Mix( value ) {
if (value.currentTarget)
value = value.currentTarget.value;
currentOsc2Mix = value;
for (var i=0; i<255; i++) {
if (voices[i] != null) {
voices[i].updateOsc2Mix( value );
}
}
}

function onUpdateEnvA( value ) {
currentEnvA = value;
}

function onUpdateEnvD( value ) {
currentEnvD = value;
}

function onUpdateEnvS( value ) {
currentEnvS = value;
}

function onUpdateEnvR( value ) {
currentEnvR = value;
}

function onUpdateFilterEnvA( value ) {
currentFilterEnvA = value;
}

function onUpdateFilterEnvD( value ) {
currentFilterEnvD = value;
}

function onUpdateFilterEnvS( value ) {
currentFilterEnvS = value;
}

function onUpdateFilterEnvR( value ) {
currentFilterEnvR = value;
}

/*
var FOURIER_SIZE = 4096;
var wave = false;
if ( wave ) {
var real = new Float32Array(FOURIER_SIZE);
var imag = new Float32Array(FOURIER_SIZE);
Expand All @@ -115,44 +250,107 @@ function onUpdateOsc1Wave( ev ) {
*/
function Voice( note, velocity ) {
this.osc = audioContext.createOscillator();
this.osc.frequency.value = frequencyFromNoteNumber( note );
this.osc.type = currentOsc1Waveform;
this.originalFrequency = frequencyFromNoteNumber( note );

// create osc 1
this.osc1 = audioContext.createOscillator();
this.updateOsc1Frequency();
this.osc1.type = currentOsc1Waveform;

this.osc1Gain = audioContext.createGainNode();
this.osc1Gain.gain.value = 0.0033 * currentOsc1Mix;
// this.gain.gain.value = 0.05 + (0.33 * velocity);
this.osc1.connect( this.osc1Gain );

// create osc 2
this.osc2 = audioContext.createOscillator();
this.updateOsc2Frequency();
this.osc2.type = currentOsc2Waveform;

this.osc2Gain = audioContext.createGainNode();
this.osc2Gain.gain.value = 0.0033 * currentOsc2Mix;
this.osc2.connect( this.osc2Gain );

this.filter1 = audioContext.createBiquadFilter();
this.filter1.type = this.filter1.LOWPASS;
this.filter1.Q.value = currentFilterQ;
this.filter2 = audioContext.createBiquadFilter();
this.filter2.type = this.filter2.LOWPASS;
this.filter2.Q.value = currentFilterQ;

this.osc1Gain.connect( this.filter1 );
this.osc2Gain.connect( this.filter1 );
this.filter1.connect( this.filter2 );

this.envelope = audioContext.createGainNode();
this.gain = audioContext.createGainNode();
this.gain.gain.value = 0.05 + (0.33 * velocity);
this.osc.connect( this.gain );
this.filter = audioContext.createBiquadFilter();
this.filter.type = this.filter.LOWPASS;
this.filter.frequency.value = currentFilterFrequency;
this.filter.Q.value = currentFilterQ;
this.gain.connect( this.filter );
this.filter.connect( this.envelope );
this.filter2.connect( this.envelope );
this.envelope.connect( audioContext.destination );
var now = audioContext.currentTime;
var envAttackEnd = now + (currentEnvA/100.0);
var filterAttackEnd = now + (currentFilterEnvA/100.0);
this.envelope.gain.setValueAtTime( 0, now );
this.envelope.gain.linearRampToValueAtTime( 1.0, now + ATTACKTIME );
this.osc.noteOn(0);
this.envelope.gain.linearRampToValueAtTime( 1.0, envAttackEnd );
this.envelope.gain.linearRampToValueAtTime( (currentEnvS/100.0), envAttackEnd + (currentEnvD/100.0) );
var initFilter = currentFilterFrequency * (1.0-(currentFilterEnv/100.0));
this.filter1.frequency.setValueAtTime( initFilter, now );
this.filter1.frequency.linearRampToValueAtTime( currentFilterFrequency, filterAttackEnd );
this.filter1.frequency.linearRampToValueAtTime( initFilter + (currentFilterFrequency * currentFilterEnv * currentFilterEnvS/10000.0), filterAttackEnd + (currentFilterEnvD/100.0) );
this.filter2.frequency.setValueAtTime( initFilter, now );
this.filter2.frequency.linearRampToValueAtTime( currentFilterFrequency, filterAttackEnd );
this.filter2.frequency.linearRampToValueAtTime( initFilter + (currentFilterFrequency * currentFilterEnv * currentFilterEnvS/10000.0), filterAttackEnd + (currentFilterEnvD/100.0) );
this.osc1.noteOn(0);
this.osc2.noteOn(0);
}

Voice.prototype.setOsc1Waveform = function( value ) {
this.osc.type = value;
this.osc1.type = value;
}

Voice.prototype.updateOsc1Frequency = function( value ) {
this.osc1.frequency.value = (this.originalFrequency*Math.pow(2,currentOsc1Octave-2)); // -2 because osc1 is 32', 16', 8'
this.osc1.detune.value = currentOsc1Detune;
}

Voice.prototype.updateOsc1Mix = function( value ) {
this.osc1Gain.gain.value = 0.0033 * value;
}

Voice.prototype.setOsc2Waveform = function( value ) {
this.osc2.type = value;
}

Voice.prototype.updateOsc2Frequency = function( value ) {
this.osc2.frequency.value = (this.originalFrequency*Math.pow(2,currentOsc2Octave-1));
this.osc2.detune.value = currentOsc2Detune;
}

Voice.prototype.updateOsc2Mix = function( value ) {
this.osc2Gain.gain.value = 0.0033 * value;
}

Voice.prototype.setFilterFrequency = function( value ) {
this.filter.frequency.value = value;
var now = audioContext.currentTime;
this.filter1.frequency.cancelScheduledValues( now );
this.filter1.frequency.setValueAtTime(value, now );
this.filter2.frequency.cancelScheduledValues( now );
this.filter2.frequency.setValueAtTime(value, now );
// this.filter.frequency.value = value;
}

Voice.prototype.setFilterQ = function( value ) {
// this.filter.Q.setTargetValueAtTime( value, audioContext.currentTime, 0.1 );
this.filter.Q.value = value;
this.filter1.Q.value = value;
this.filter2.Q.value = value;
}

Voice.prototype.noteOff = function() {
var now = audioContext.currentTime;
this.envelope.gain.cancelScheduledValues(now);
this.envelope.gain.setValueAtTime( this.envelope.gain.value, now );
this.envelope.gain.linearRampToValueAtTime( 0.0, now + RELEASETIME );
this.osc.noteOff( now + RELEASETIME );
var release = now + (currentEnvR/100.0);
this.envelope.gain.linearRampToValueAtTime( 0.0, release );
this.osc1.noteOff( release );
this.osc2.noteOff( release );
}

function keyDown( ev ) {
Expand Down Expand Up @@ -180,7 +378,6 @@ function initAudio() {
}
window.addEventListener('keydown', keyDown, false);
window.addEventListener('keyup', keyUp, false);
synthBox = document.getElementById("synthbox");
setupSynthUI();
}

Expand Down
Loading

0 comments on commit 84681ce

Please sign in to comment.