With everything that we have added to our library so far we are almost capable of generated small tunes. One thing that’s missing to make it sound more ‘natural’ is a way for the notes to start and stop.
In this post we will implement a type of envelope called “ADSR”, for “Attack, Decay, Sustain, Release”. Which will make the notes sound more natural as they are played in sequence.
To see why we need this, listen to this sound generated without an ADSR envelope around the generated frames.
If you want to read the (not pretty) code that generated this, check out this github gist.
The Attack, Decay, Sustain and Release envelope is a common type of envelope. Schematically this can be represented as below (from wikipedia):
As we apply this envelope to a signal, the signal will change in amplitude depending on which phase we are in of the ADSR envelope. In the image it is visible that the amplitude rises during the attack step, reaches a peak amplitude before decreasing a bit. After decreasing it reached the sustain amplitude, where it will stay until the note is released, and after release it decays until the ampltide is zero.
For our parameters, three will relate to time:
- attack (time to rise)
- decay (time to fall to sustain level)
- release (time to decay from sutan to zero)
So the Sustain parameter does not refer to time, but rather to the amplitude we will maintain.
Turning this schematic into code, we get :
One parameter in this function that you don’t find in the schematic is the need for a control rate. The control rate will be used to turn a duration in seconds into an amount of frames. The control rate could just be sample rate but this does not necessarily have to be the case. One such use-case us sub-audio modulation, whereby the modulating oscillator is running below 20Hz. You can check this chapter for a bit more on that.
To apply the ADSR envelope to a signal, for example to one that was generating using the oscillator we created we have to iterate over each frame, pass the current frame to the ADSR function, and modify the amplitude of the frame with the result. For example, this is the complete example program included in GoAudio.
In this example, notice that our control rate is the same as our sample rate, and the adsrtime
increases together with the frames that we have processed. (We could thus pass the
variable to the function, but I thought making it explit was clearer).
If you liked this and want to know when I write new posts, the best way to keep up to date is by following me on twitter.