Attack, Hold, Decay using linearRampToValueAtTime

Hello,

     I have encountered some unexpected behavior in creating Attack, 
Hold, Release envelopes.

  Standard scenario:

Let's say you want
     a 1 second attack to start on some "play" event, and
     a 1 second decay to start on some "release" event.
You want the decay to start at whatever the *current* amplitude is 
(whether yourattack is still in progress or not).


One might expect to be able to respond to the "release" event with the 
following code:

     now = config.audioContext.currentTime;
     stopTime = now + m_releaseTime;

     gainEnvNode.gain.cancelScheduledValues(now);
     gainEnvNode.gain.linearRampToValueAtTime(0, stopTime);


However, the actual behavior of this code appears to be:
    a) if the attack has completed, the decay ramp is computed from the 
time the attack completed, rather than "now". Thus there is a sudden 
jump to the middle of the intended decay ramp values.
    b) if the attach has not completed, the sound stops suddenly with no 
decay (not sure how to explain that).


So my comment/suggestion is :
    While it makes sense to schedule a ramp to begin at the last 
scheduled value, the *time* used to begin the new events should perhaps 
be the time of the last scheduled value, or "now" (whichever is greater).


The only solution that I have found that works in each case is to insert 
a flat ramp to update the time and value of the envelope when the 
"release" event occurs:

     now = config.audioContext.currentTime;
     stopTime = now + m_releaseTime;

     gainEnvNode.gain.cancelScheduledValues(now);
     gainEnvNode.gain.linearRampToValueAtTime(gainEnvNode.gain.value, 
now);   // THIS IS THE CHANGE FROM PREVIOUS CODE EXAMPLE
     gainEnvNode.gain.linearRampToValueAtTime(0, stopTime);


This makes some sense when you get it, but is not very intuitive since 
(if I understand it correctly) since
     a) if the attack has not completed, the inserted ramp has 0 
duration, and
     b) if the attack has completed, I am creating a ramp starting at a 
time long since passed.

(Inserting gain.setValueAtTime(gainEnvNode.gain.value, now) instead 
would seem more intuitive, but it does not work either.) Of course, I 
may have missed a better way to do the AHD thing completely....

Congratulations on all the great work so far,

- lonce

Received on Thursday, 3 January 2013 01:57:27 UTC