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

  1. //ExponentialEnvelope.c
  2. //gcc MKAiff.c ExponentialPitchEnvelope.c -o ExponentialPitchEnvelope
  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 envelopeDuration = 3, startingValue = 200, endingValue=1000, exponent=2;
  18. if(exponent) startingValue = pow(startingValue, 1/exponent);
  19. if(exponent) endingValue = pow(endingValue, 1/exponent);
  20. envelopeDuration *= SAMPLE_RATE;
  21. double valueIncrement = ((endingValue - startingValue)/envelopeDuration);
  22. double frequency = startingValue, angle = 0;
  23. int i;
  24. for(i=0; i<numSamples; i+=NUM_CHANNELS)
  25. {
  26. audioBuffer[i] = sin(angle);
  27. if(i/NUM_CHANNELS < envelopeDuration) frequency += valueIncrement;
  28. angle += pow(frequency, exponent) * TWO_PI_OVER_SAMPLE_RATE;
  29. }
  30. aiffAppendFloatingPointSamples(aiff, audioBuffer, numSamples, aiffFloatSampleType);
  31. aiffSaveWithFilename(aiff, "ExponentialPitchEnvelope.aif");
  32. aiffDestroy(aiff);
  33. return 0;
  34. }

Output:

Explanation of the Concepts

In the previous chapter an exponential envelope was applied to the amplitude of a sine wave. In this chapter an exponential envelope will be applied to the pitch of a sine wave.

An exponential envelope is particularly suited to this purpose because pitch, like loudness, is logarithmic. By definition, doubling the frequency of a sound raises it by one octave. So a 100 Hz wave will have to be raised 100 Hz to make it an octave higher, whereas a 1000 Hz wave needs to be raised by ten times as many Hz as a 100 Hz wave to make it an octave higher. Because of this, just like with amplitude, raising the frequency linearly would create a sound that rises in pitch steeply at the beginning of the envelope, and in continuously smaller amounts as the envelope progresses. For instance if you started with a 100 Hz wave and raised it 100 hz per second, then it would rise an octave during the first second, but only a perfect fifth in the next second, and only a fourth in the next second. This, again, can be compensated by raising the frequency by smaller amounts in the beginning and in progressively greater amounts as the envelope progresses, which, by definition, requires an exponential curve.

Explanation of the Code

This is nearly identical to the previous chapter, with only a few minor differences. On line 22 the starting and ending values of the envelope have been set to values, in cycles per second, that are more meaningful for pitch. In the previous example, since we were controlling the volume of the wave, we declared an additional variable, 'currentVolume' to store the value of the envelope at any given moment. Here, the current volume will be constant (just 1). So we won't need that variable. Since we are controlling the pitch, we will just use the usual 'frequency' variable, that was declared for the sine wave, to hold the current value of the envelope. On line 28 the frequency is set to the initial value of the envelope. Line 33 checks to see if the bounds of the envelope have been exceeded, and if they have not, the envelope (the frequency) is incremented linearly. This linear shape is given its exponential curve on line 34, where the frequency is raised to the specified exponent before it is used in calculating the angle of the sine oscillator.