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

  1. //TraingelWave.c
  2. //gcc MKAiff.c TriangleWave.c -o TriangleWave
  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 halfWavelength = SAMPLE_RATE / (2 * frequency);
  16. float nextSample = -1;
  17. float incrementAmmount = -2.0 / (halfWavelength+1);
  18. int i;
  19. for(i=0; i<numSamples; i+=NUM_CHANNELS)
  20. {
  21. audioBuffer[i] = nextSample;
  22. if(!(i/NUM_CHANNELS % halfWavelength)) incrementAmmount *= -1;
  23. nextSample += incrementAmmount;
  24. }
  25. aiffAppendFloatingPointSamples(aiff, audioBuffer, numSamples, aiffFloatSampleType);
  26. aiffSaveWithFilename(aiff, "TriangleWave.aif");
  27. aiff = aiffDestroy(aiff);
  28. return 1;
  29. }

Output:

Builds On

AIFF Template

Explanation of the Concepts

A triangle wave typically starts at its lowest value, ascends linearly to its highest value over half of its period, and then descends linearly to its lowest value over the remainder of its period. Here is a plot of its value as a function of time:

Plot

Triangle waves are very similar in shape to sine waves, and are consequently very similar in harmonic content. The Fourier spectrograph shows that the triangle wave is basically 'pure', with only a few weak higher partials present:

FFT

Explanation of the Code

Programming a triangle wave is like programming Pong in one dimension (a very boring game indeed!). The position of the ball (ie the value of the current sample) is incremented by some value until the ball reaches the edge of the screen (ie the value os the sample reaches +1), and then the position-increment is multiplied by -1 so that the direction of the ball reverses. The ball must reverse direction every half-period. The number of frames in a half-perido is calculated in line 21. The ball's position must be incremented from -1 to +1 every half period, and the value of the increment amount is calculated on line 23. The position of the ball is incremented on line 30, and the direction of the ball is reversed on line 21, but only if the current frame (i/NUM_CHANNELS) is an even multiple of the half-period.