An Audio Synthesis Textbook For Musicians, Digital Artists and Programmers by Mike Krzyzaniak

  1. //ExponentialEnvelope.c
  2. //gcc MKAiff.c ExponentialEnvelope.c -o ExponentialEnvelope
  3. #include "MKAiff.h"
  4. #include <math.h>
  5. #define SAMPLE_RATE 44100
  6. #define NUM_CHANNELS 1
  7. #define BITS_PER_SAMPLE 16
  8. #define NUM_SECONDS 3
  9. const int numSamples = NUM_SECONDS * NUM_CHANNELS * SAMPLE_RATE;
  10. #define PI 3.141592653589793
  11. const double TWO_PI_OVER_SAMPLE_RATE = 2*PI/SAMPLE_RATE;
  12. int main()
  13. {
  14. MKAiff* aiff = aiffWithDurationInSeconds(NUM_CHANNELS, SAMPLE_RATE, BITS_PER_SAMPLE, NUM_SECONDS);
  15. if(aiff == NULL) return 0;
  16. float audioBuffer[numSamples];
  17. double frequency = 440, angle = 0;
  18. double envelopeDuration = 3, startingValue = 0, endingValue=1, exponent=2;
  19. if(exponent) startingValue = pow(startingValue, 1/exponent);
  20. if(exponent) endingValue = pow(endingValue, 1/exponent);
  21. envelopeDuration *= SAMPLE_RATE;
  22. double currentVolume = startingValue;
  23. double valueIncrement = ((endingValue - startingValue)/envelopeDuration);
  24. envelopeDuration *= SAMPLE_RATE;
  25. int i;
  26. for(i=0; i<numSamples; i+=NUM_CHANNELS)
  27. {
  28. audioBuffer[i] = sin(angle) * pow(currentVolume, exponent);
  29. angle += frequency * TWO_PI_OVER_SAMPLE_RATE;
  30. if(i/NUM_CHANNELS < envelopeDuration) currentVolume += valueIncrement;
  31. }
  32. aiffAppendFloatingPointSamples(aiff, audioBuffer, numSamples, aiffFloatSampleType);
  33. aiffSaveWithFilename(aiff, "ExponentialEnvelope.aif");
  34. aiffDestroy(aiff);
  35. return 0;
  36. }

Output:

Explanation of the Concepts

An exponential envelope is, in principal, nearly identical to a linear envelope, except that it has been given an exponential curve. When applied to amplitude, this can create a smooth increase in loudness (discussed in Basic Volume Control), or, when applied to pitch, it can create a smooth increase in pitch. The shape of the curve can be controlled by adjusting the exponent. If the exponent is 1, the envelope will just be linear. Increasing the exponent a little will create a gentle concavity in the surface of the line, like this, where the exponent is 2 (the envelope's value is plotted as a function of time, and a straight line is also shewn for comparison):

Exponential Envelope

Increasing the exponent further makes the concavity more steep, as here, where the exponent is 5:

Exponential Envelope

On the other hand, a convex curve may be obtained by using an exponent between 0 and 1, like here where the exponent is 0.5:

Exponential Envelope

Explanation of the Code

Line 23 is identical to line 23 of the linear envelope, except that an exponent has also been declared.

The exponent may confound the starting and ending values. This is because those values are raised to the exponent before they are used. For instance, if you declare the ending value as 0.5, and the exponent as 2, the actual ending value will be 0.5 squared, or 0.25. This is, presumably not the intended behavior. The program must correct this by adjusting the ending value such that, in this example, the ending value squared will be 0.5. This is accomplished by raising the the declared ending value to 1/exponent before proceeding to calculate the envelope. Notice that if for some reason someone declares the exponent as 0, this will cause a divide by zero error, which is likely to bring the whole world spiraling into oblivion. So, on line 24, the program checks to make sure the exponent is not 0, and, if it is not, it raises to starting value to 1/exponent. Ditto for the ending value on line 25.

Henceforth, the program proceeds identically to the Linear Envelope, where the envelope's increment is calculated on lines 26-28. Notice that the envelope will actually be incremented linearly. It will not be given its exponential curve until line 34, where the otherwise linear envelope is raised to the exponent.