An envelope is a time-varying contour applied to a synthesized signal. An ADSR envelope shapes amplitude through attack, decay, sustain, and release stages, changing the sound’s character. section 10.2.2 section 10.3.2
Build an ADSR contour from four time-based components:
- Increase the attack curve linearly to full volume over the attack duration. section 10.2.2
- Taper the decay curve toward the sustain level over the decay duration. section 10.2.2
- Repeat the sustain level indefinitely so the contour can cover an arbitrarily long signal. section 10.2.2
- Reverse a release curve, take the required number of samples, and multiply the curves with the original signal. section 10.2.2
Applying an ADSR envelope
Use `adsr (ADSR 0.1 0.2 0.5 0.1) (tone tri 550 1)` and write the resulting signal to `adsrSignal.wav`. The parameters specify attack, decay, sustain, and release values; an audio editor reveals the resulting contour.
Listing 10.8 A function to apply an ADSR envelope to a signal
import Util.Types
adsr :: ADSR -> Signal -> Signal
adsr (ADSR a d s r) signal =
zipWith3 #1
(\adsCurve rCurve sample ->
adsCurve * rCurve * sample) #1
(att ++ dec ++ sus) #2
rel
signal
where
attackSamples = fromIntegral $ samplesPerSecond a #3
decaySamples = fromIntegral $ samplesPerSecond d #3
releaseSamples = fromIntegral $ samplesPerSecond r #3
att = map (/ attackSamples) [0.0 .. attackSamples] #4
dec =
reverse $ #5
map #6
(\x -> ((x / decaySamples) * (1 - s)) + s) #6
[0.0 .. decaySamples - 1] #6
sus = repeat s #7
rel =
reverse $ #8
take #9
(length signal)
(map (/ releaseSamples) #9
[0.0 .. releaseSamples] ++ repeat 1.0) #10
Inspect the implementation to see how `zipWith3` combines attack-decay-sustain values, release values, and signal samples, while infinite sustain guarantees coverage. section 10.2.2
Oscillators pair a waveform with ADSR parameters, producing tones whose contours can differ markedly across piano, ocarina, violin, pluck, and bass examples. section 10.3.2
