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

  1. //SawoothWave.c
  2. //gcc MKAiff.c SawtoothWave.c -o SawtoothWave
  3. #include "MKAiff.h"
  4. #define SAMPLE_RATE 44100
  5. #define NUM_CHANNELS 1
  6. #define BITS_PER_SAMPLE 16
  7. #define NUM_SECONDS 3
  8. const int numSamples = NUM_SECONDS * NUM_CHANNELS * SAMPLE_RATE;
  9. int main()
  10. {
  11. MKAiff* aiff = aiffWithDurationInSeconds(NUM_CHANNELS, SAMPLE_RATE, BITS_PER_SAMPLE, NUM_SECONDS);
  12. if(aiff == NULL) return 0;
  13. float audioBuffer[numSamples];
  14. double frequency = 440;
  15. int period = SAMPLE_RATE / frequency;
  16. int i;
  17. for(i=0; i<numSamples; i+=NUM_CHANNELS)
  18. {
  19. audioBuffer[i] = 2 * (i/NUM_CHANNELS % period) /(float) period - 1;
  20. }
  21. aiffAppendFloatingPointSamples(aiff, audioBuffer, numSamples, aiffFloatSampleType);
  22. aiffSaveWithFilename(aiff, "SawtoothWave.aif");
  23. aiff = aiffDestroy(aiff);
  24. return 1;
  25. }

Output:

Builds On

AIFF Template

Explanation of the Concepts

A sawtooth wave typically starts its cycle at its lowest value (-1 in this case), and ascends linearly to its highest value (+1) over the duration of its period. It then drops immediately to its lowest value and begins its linear ascent anew. Here is a plot of such a wave's value as a function on time:

Plot /

Saw waves are harmonically rich. All harmonic partials are represented ('harmonic' partials have a frequency that is an even multiple of the fundamental frequency), and the amplitude of each partial decreases proportionally to fundamentalFrequency / partialFrequency. This is clearly visible in the Forier spectrograph of the wave produced by the above program:

FFT

Notice that the Fundamental (440hz) is the strongest constituent. Also present are constituents at 440*1 hz, 440*2 hz, 440*3 hz... and so forth, and that each constituent is successively weaker as a function of its frequency. Notice also that the first partial (the perfect fifth) is particularly strong, and it can be heard quite audibly in the recording above.

Explanation of the Code

The concept of a linear ascent followed by an immediate return is described mathematically by the modulo operator, which is represented by the symbol % in the C language. Modulo is the remainder after an integer division, so that 3%3 is 0, because 3/3 = 1 with a remainder of 0. 4%3 is 1, because 4/3 = 1 with a remainder of 1, 5%3 is 2, and 6%3 is 0 again. Consider the equation

a%b=c

c rises linearly with a until it reaches b-1, and then it returns to 0. b, therefore, is the period. For an audio wave, the period is given by

SAMPLE_RATE / frequency;

which is calculated on line 21. The value of the wave is calculated on line 26, where

i/NUM_CHANNELS

is the first sample of the current frame. This is unnecessary for one-cahnnel audio, but it assures that the frequency will not change if more channels are added. Dividing by the period scales the output to a number between 0 and 1, and multiplying by 2 an subtracting 1 gives the proper range of -1 to +1. Note that the waveform could be inverted (so that it starts at its highest value and descends linearly to its lowest value) by instead multiplying by -2 and adding 1.