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

  1. //SineWave.c
  2. //gcc MKAiff.c SineWave.c -o SineWave
  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. int i;
  19. for(i=0; i<numSamples; i+=NUM_CHANNELS)
  20. {
  21. audioBuffer[i] = sin(angle);
  22. angle += frequency * TWO_PI_OVER_SAMPLE_RATE;
  23. }
  24. aiffAppendFloatingPointSamples(aiff, audioBuffer, numSamples, aiffFloatSampleType);
  25. aiffSaveWithFilename(aiff, "SineWave.aif");
  26. aiffDestroy(aiff);
  27. return 0;
  28. }

Output:

Builds On

AIFF Template

Explanation of the Concepts

A lone reveler on a ferris-wheel takes delight. Naomi, beside the ferris-wheel, eyes him with weary unrest. Her eyes cut annular paths through the twilight as she watches him get carried round and round, inexorably, in circles. Her boyfriend, Hercules, on the other hand, has had a little too much green punch, and has taken respite on a bench directly in front of the ferris-wheel. His interest in the reveler is somewhat more passive, but he too traces the reveler's path with his eyes. Fortunately for his enfeebled stomach this does not require moving his eyes in inexorable circles at all. From his vantage point, he needs only to move his eyes up and down, down and up, and back again. He finds this motion soothing, and feels an inexplicable sense of harmony, such that he does not even notice the strange creaking noise coming from the ferris-wheel's axle. Naomi feels a sudden nauseous disquietude, and the pale dusky light seems to begin to strobe. In each pulse, she traces an imaginary line from the reveler to the axle, and from the axle to Hercules. In her distraught mind, the light pulses off and on again. The reveler has advanced a few degrees around the circle, and again she looks from him to the axle and over to Hercules. Suddenly, a single dreadful thought rips through her soul like a blue streak of lightning: "An angle," she thinks. "It's just like high-school math!"

Sine is a mathematical function that describes how high up the reveler appears from Hercules' perspective, as a function the angle between him, the axle, and the reveler, as seen from Naomi's perspective. In the following demonstration, you have Naomi's perspective. Hercules is on the positive x axis, somewhere to the right of the image, looking towards the origin. The yellow line represents the revelers path around the ferris-wheel, and the length of the red line, positive or negative, shows how far up or down Hercules has to move his eyes to see the reveler. Mathematically, the yellow line is the measure of the angle, and the length of the red line is sin(angle):

sinusoidal animation demo

As the angle (the yellow line) approaches 90 degrees, sine(angle) (the length of the red line) approaches 1 unit in length. As the angle then approaches 180 degrees, sin(angle) approaches 0 units in length. As the angle approaches 270 degrees, sin(angle) approaches -1 unit in length. Finally, as the angle approaches 360 degrees, sin(angle) approaches -0 units in length again. A plot of this relationship, with the angle on the x axis and sin(angle) on the y axis, can be seen here:

sine wave

If we are to interpret this shape as sound, we may imagine that it is plot of air pressure as a function of space, or the displacement of a speaker-cone from rest over time. In order to further interpret this shape as digital sound, we must break up the smooth continuous curve into discrete samples. The basic problem then will be to figure out how many degrees to move around the circle between each sample. How far around the circle does the reveler advance every time the strobe light flashes? The answer depends first on the sample rate. If the sample rate is, say 44100 samples per second, and we want to complete the circle with a frequency of once per second, then we will need to divide the entire (360 degrees) circle into 44100 individual slices of pie, and advance the angle by that amount between each sample. If instead we want to complete the circle with a frequency of, say, two times a second, then we need to advance the angle twice as much between samples. This gives the following relationship:

n = frequency * 360 / sampleRate

where n is the number of degrees to advance between each sample.

Explanation of the Code

sin() is the standard c function to compute the value of sine for a given angle. It is declared in math.h, which is therefore included on line 4 of the code. As an argument, it takes the angle, not in degrees, but in radians. This is not a problem if we simply remember that there are 2*PI radians in 360 degrees. To accommodate, we only need need to replace 360 in the above equation with 2*PI, like so:

n = frequency * 2 * PI / sampleRate

The angle and frequency are declared as variables on line 22. On line 27, the first sample of each frame of the audio buffer is filled with sin of the current angle, and then on line 28 the angle is incremented according to the above equation.

Because the value of 2 * PI / sampleRate will not change over the course of the program, it is computed once at the beginning (on lines 13 and 14) and stored as a constant, rather than being computed again and again for each sample (ie on line 28). This saves 132,299 multiplications and an equal number of divisions over the course of the program, and this only makes a 3 second file! In this program, the frequency also does not change, and therefore could have been computed as part of the constant (saving another 132,299 multiplications). Nonetheless, it has been left as a variable which will make it easier to later modify the code to support changing frequencies which are, of course, more common in actual music.