EDIT: found the fix. I was missing g.gain.setValueAtTime(0,now);
before the line
setting the attack ramp below. The g.gain.value = 0;
was also redundant after adding that.
So I'm having a weird issue. Let me give some background: I'm implementing a polyphonic synthesizer where notes are created and played in a throw-away fashion - a new oscillator is created every time a key is triggered.
I first implemented the volume envelope globally but this turned out to mess with decay/release of past notes when new notes were triggered, but monophonically, the attack, decay, and release envelopes all worked as expected.
So my fix was to create both oscillators and envelopes on-the-fly when keys are triggered. This worked nicely with one bit of strangeness: the decay and release both work as before with the benefit of note volumes enveloping uniquely, BUT the attack envelope doesn't ramp linearly. Instead it delays for whatever attack time is specified and shoots up all at once.
Can anyone see why the attack envelope is delaying and acting instantly rather than modifying the gain linearly as how it should?
Here is the relevant function:
synth.prototype.playNote = function(i, freq) {
// create oscillator
var o = this.context.createOscillator();
o.frequency.value = freq * Math.pow( 2, this.osc[i].oct-4 );
o.type = this.osc[i].type; o.start( 0 );
// create envelope node and connect
var g = this.context.createGainNode(); g.gain.value = 0;
o.connect( g ); g.connect( this.master_gain );
// enveloping
now = this.context.currentTime;
// the line below delays for the attack time at 0 gain and
// shoots to volume 1 after (now + this.amp_env.attack) rather than curving linearly
g.gain.linearRampToValueAtTime(1, now + this.amp_env.attack );
g.gain.linearRampToValueAtTime(this.amp_env.decay, now + this.amp_env.attack + this.amp_env.decay + this.amp_env.release );
g.gain.linearRampToValueAtTime(0, now + this.amp_env.attack + this.amp_env.decay + this.amp_env.release );
// kill note after envelopes are done;
o.stop( now + this.amp_env.attack + this.amp_env.release + this.amp_env.release );
}