Envelopes
You can think of an envelope as a function mapping some feature of sound over
time. This feature can be anything the Web Audio API implements as an
AudioParam
: a GainNode
's gain, an OscillatorNode
's frequency, a
BiquadFilterNode
's Q, etc. To construct an envelope, you can pick and choose
from the methods available in the AudioParam
interface, for example,
.linearRampToValueAtTime()
,
exponentialRampToValueAtTime()
,
and
setValueCurveAtTime()
.
Let's start with setValueCurveAtTime()
. This method takes three arguments:
an array of floats, a start time, and a duration. The array of floats
represents a series of values the AudioParam
will navigate through; the start
time represents an offset from audioCtx.currentTime
, and the duration is a
time in seconds over which the method will navigate the array of floats.
Here are some examples. In this example, setValueCurveAtTime()
is attached
to a filter's cutoff frequency, causing it to decrease and then bounce back.
The envelope would look something like this.
In this example, setValueCurveAtTime()
is attached to the gain of a
GainNode
. This type of envelope—which starts and ends at 0—may also be
called a "window".
Now let's look at linearRampToValueAtTime()
. This method is much simpler
than setValueCurveAtTime()
since it only repesents one linear change in a
parameter's value: its first argument is the value to get to, and its second
argument is the time in seconds to take to get there. This method is quite
useful when trying to create a traditional ADSR envelope. To review, "ADSR"
stands for "attack", "decay", "sustain", and "release". "Attack" is a time to
get from 0 to our maximum value; "decay" is a time to get from our maximum
value to our sustain value; "sustain" is not a time, but rather the value to
get to in our "decay"; and "release" is a time to get from our sustain value
back to 0.
Let's try and construct a traditional ADSR envelope to control the volume of an
oscillator. For this, let's make two functions: one to represent the onset of
the note, one to represent the release.
const audioCtx = new AudioContext();
// Create an oscillator.
const oscillator = audioCtx.createOscillator();
oscillator.frequency.value = 220;
oscillator.type = "sawtooth";
oscillatorGain = audioCtx.createGain();
oscillatorGain.gain.setValueAtTime(0, audioCtx.currentTime); // start from silence!
oscillator.connect(oscillatorGain);
oscillatorGain.connect(audioCtx.destination);
oscillator.start();
// Create attack and release functions.
const attack = (attackTime, decayTime, sustainValue) => {
oscillatorGain.gain.setValueAtTime(0, audioCtx.currentTime);
oscillatorGain.gain.linearRampToValueAtTime(1,
audioCtx.currentTime + attackTime);
oscillatorGain.gain.linearRampToValueAtTime(sustainValue,
audioCtx.currentTime + attackTime + decayTime);
};
const release = (releaseTime) => {
oscillatorGain.gain.linearRampToValueAtTime(0,
audioCtx.currentTime + releaseTime);
};
An example application that uses these functions may be found in the webpage
below this video.