DEPARTMENT OF COMPUTING

CS 3005: Programming in C++

Envelope Classes

Introduction

Envelopes are used in audio synthesis to control the volume of a sound over time. For example, a sound may start out loud, then become quieter over time. Or it may take time to reach full volume.

Audio designers will try many different envelope shapes to create different sounds.

A simple envelope shape is the “Attack/Decay” (AD) envelope. In this envelope style, the volume initially increases from 0 to the maximum. It then decays to 0.

AD-args-envelope.png

A common envelope shape is the “Attack/Decay/Sustain/Release” (ADSR) envelope. In this envelope style, the volume initially increases from 0 to the maximum. It then decays to an intermediate (sustain) level. Next, it stays at this level for some sustain duration. Finally, it decays to 0 (release).

ADSR-args-envelope.png

You may notice that the AD envelope is a simplified form of the ADSR envelope.

Assignment

In this assignment, you will create a class hierarchy to represent the functionality of an envelope, including specialization for the ADSR and AD envelopes.

The Envelope class will be a base class. ADSREnvelope will inherit from Envelope. ADEnvelope will inherit from ADSREnvelope.

You will also create a simple program that generates an envelope and displays its amplitudes as text. In a future assignment, you’ll use the envelopes together with waveforms to generate sounds.

A sample interaction with the program may look like this:

$ ./program-envelope-test/envelope_test 
Samples/Second: 10
Seconds: 1.0
Envelope style: ADSR
Maximum amplitude: 0.75
Attack seconds: 0.2
Decay seconds: 0.3
Release seconds: 0.4
Sustain amplitude: 0.25

sample_number,amplitude
0,0
1,0.375
2,0.75
3,0.583333
4,0.416667
5,0.25
6,0.25
7,0.1875
8,0.125
9,0.0625

Notice that the output is in CSV form, so you could plot it to produce images like shown above.

This example demonstrates a user giving an invalid envelope style.

$ ./program-envelope-test/envelope_test 
Samples/Second: 10
Seconds: 1.0
Envelope style: USPS
Maximum amplitude: 0.8
Envelope style 'USPS' is not known.

sample_number,amplitude
0,0
1,0
2,0
3,0
4,0
5,0
6,0
7,0
8,0
9,0

Computing Envelopes

Let’s discuss the computational steps for the ADSR envelope. Each envelope computation has a duration in seconds. We’ll use t to denote duration. Each envelope computation also has a samples per seconds. We’ll use sps to denote this value. If you multiply these two values, you get the number of samples that will be computed by the envelope. We’ll use n to denote the number of samples. Let a be the attack duration in seconds, d be the decay duration in seconds, s be the sustain duration in seconds, and r be the release duration in seconds.

In order to compute the amplitudes for the ADSR envelope, we need to compute straight lines in 4 different sections, with each section connecting to the one before and one after it.

Attack Section

The attack section starts at amplitude 0.0 and ends at the maximum amplitude. The sample positions start at position 0, and end after the attack duration. The position of the end of the attack duration can be computed by sps * a.

Decay Section

The decay section starts at the maximum amplitude and ends at the sustain amplitude. The sample positions start at position where the attack ends, and end after the decay duration. The position of the end of the decay duration can be computed by attack_end + sps * d.

Sustain Section

The sustain section starts at the sustain amplitude and ends at the sustain amplitude. The sample positions start at position where the decay ends, and end after the sustain duration. The sustain duration can be computed as s = t - (a+d+r).

The position of the end of the sustain duration can be computed by release_end - sps * r.

Release Section

The release section starts at the sustain amplitude and ends at 0.0 amplitude. The sample positions start at position where the sustain ends, and end at the end of the envelope. The position of the end of the release duration can be computed by t*sps.

Computing “Straight Line” Amplitudes

Each of the sections above need to compute a straight line of amplitudes for their sections. Each as a starting amplitude (y0), an ending amplitude (y1), a starting position (x0) and an ending position (x1). Let x be the position we want to compute for, and y be the amplitude we want to compute.

We’ll use the formula from algebra for computing straight lines from two points.

y = (x - x0) * (y1 - y0) / (x1 - x0) + y0

Programming Requirements

Create library-envelope/Envelope.{h,cpp}

Envelope Class

Data Members:

The Envelope class should contain data members to track the following information. These data members should be protected or private. They are not allowed to be public.

public Methods:

Create library-envelope/ADSREnvelope.{h,cpp}

ADSREnvelope Class

Publicly inherits from Envelope.

Data Members:

The ADSREnvelope class should contain data members to track the following information. These data members should be protected or private. They are not allowed to be public.

public Methods:

Create library-envelope/ADEnvelope.{h,cpp}

ADEnvelope Class

Publicly inherits from ADSREnvelope.

Data Members:

The ADEnvelope class does not define any new data members.

public Methods:

Create library-envelope/Makefile

This file must contain rules such that any of the following commands will build the libenvelope.a library:

This file must contain rules such that the following command will install the libenvelope.a library into the lib directory, and all .h files to the include directory:

Update library-commands/wav_file_creator_aux.{h,cpp}

Functions:

Create library-commands/envelope_test_aux.{h,cpp}

Functions:

Update library-commands/Makefile

Add envelope_test_aux.{h,cpp} in the appropriate places to add them to the library and install the header file.

Create program-envelope-test/envelope_test.cpp

Functions:

Create program-envelope-test/Makefile

This file must contain rules such that any of the following commands will build the envelope_test program:

Create program-envelope-test/.gitignore

The file program-envelope-test/.gitignore needs to store one line of text:

envelope_test

Update Makefile

Update the project-level Makefile so that make and make all in the project directory will call make install in the library-envelope directory, and make in the program-envelope-test directory.

Additional Documentation

Grading Instructions

To receive credit for this assignment:

Extra Challenges (Not Required)

TBA

Last Updated 02/13/2025