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

  1. //AdditiveSynthesis.c
  2. //gcc MKAiff.c AdditiveSynthesis.c -o AdditiveSynthesis
  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 BYTES_PER_SAMPLE 2
  9. #define NUM_SECONDS 3
  10. const int numSamples = NUM_SECONDS * NUM_CHANNELS * SAMPLE_RATE;
  11. #define PI 3.141592653589793
  12. const double TWO_PI_OVER_SAMPLE_RATE = 2*PI/SAMPLE_RATE;
  13. int main()
  14. {
  15. float audioBuffer[numSamples];
  16. for(i=0; i<numSamples; audioBuffer[i++]=0);
  17. double fundamentalFrequency = 110, frequency, phase = 0, amplitude;
  18. int i, j, numFrequenciesToAdd = 15;
  19. for(i=0; i<numSamples*NUM_CHANNELS; audioBuffer[i++]=0);
  20. for(j=1; j<=numFrequenciesToAdd; j++)
  21. {
  22. //Sawtooth Wave
  23. frequency = j;
  24. amplitude = 1.0/frequency;
  25. phase = 0;
  26. /*
  27. //Square Wave
  28. frequency = j*2-1;
  29. amplitude = 1.0/frequency;
  30. phase = 0;
  31. */
  32. /*
  33. //Triangle Wave
  34. frequency = j*2-1;
  35. amplitude = 1.0/(frequency * frequency);
  36. phase = (j-1%2) * PI;
  37. */
  38. /*
  39. //Pulse Wave
  40. frequency = j;
  41. amplitude = 1/8;
  42. phase = (j-1%4) * PI / -2.0;
  43. */
  44. frequency *= fundamentalFrequency;
  45. amplitude *= 0.5;
  46. for(i=0; i<numSamples; i+=NUM_CHANNELS)
  47. {
  48. audioBuffer[i] += sin(phase) * amplitude * 0.5;
  49. phase += frequency * TWO_PI_OVER_SAMPLE_RATE;
  50. }
  51. }
  52. MKAiff* aiff = aiffWithDurationInSeconds(NUM_CHANNELS, SAMPLE_RATE, BITS_PER_SAMPLE, NUM_SECONDS);
  53. if(aiff == NULL) return 1;
  54. aiffAppendFloatingPointSamples(aiff, audioBuffer, numSamples, aiffFloatSampleType);
  55. aiffSaveWithFilename(aiff, "AdditiveSyntheses.aif");
  56. aiffDestroy(aiff);
  57. return 0;
  58. }

Output:

Explanation of the Concepts

This example sums sine waves to create the other simple waveforms, and saves the result as a 3 second aiff file.

Theoretically, any periodic waveform can be created by summing sinusoids. An easy way to demonstrate this is by using sinusoids to recreate the other basic waveforms: sawtooth, pulse, triangle, and square. This is not a particularly efficient way of producing these waveforms, but it is edifying to see how it works. A saw-wave has sinusoidal constituents whose frequencies are whole-number multiples of the fundamental. Their amplitude ratios are equal to 1/frequencyRatio (i.e. 1/harmonicNumber), so that the second sine wave, whose frequency is twice the fundamental, has an amplitude that is half the fundamental. The next sinusoid, whose frequency is 3 times that of the fundamental, has an amplitude of 1/3 the fundamental. The sinusoids are all if phase, meaning that they all begin their cycle at 0 radians together. The top portion of this image shows 5 such sinusoids, and the bottom portion (the white line), is their sum, which is nearly a sawtooth wave:
Saw
This agrees with the spectrogram of a sawtooth wave from chapter 1, which clearly shows evenly spaced overtones that get weaker as they get higher (although the spectrogram does not report on the phase of the constituents):
Saw
The sawtooth wave above is a little wavy: it could be made to more closely resemble a true sawtooth wave by adding in even more, higher partials. Theoretically, it would be a perfect sawtooth if it were the sum of an infinite number of sinusoids.

A pulse wave can be made by just adjusting the phase and amplitude of the sinusoidal constituents of a sawtooth wave. For a pulse wave, the sinusoids are all of equal amplitude. Their phases are aligned so that the waves all reach pi/2 radians (their peak) at the same time:
Pulse

The square wave consists of only odd-numbered partials, which means that the sinusoidal constituents have frequencies of 1*fundamental, 3*fundamental, 5*fundamental and so forth. Aside from that, it is identical to a sawtooth wave. The amplitudes are inversely proportional to the frequency ratios, and they all begin together at 0 radians:
Square

The triangle wave, too, has only odd-numbered partials. However, they diminish in amplitude more quickly than they do in the square wave. For a triangle wave, the amplitude of each sine wave is proportional to one over the square of the harmonic number, so that a sine-wave whose frequency is 5 times the fundamental will have an amplitude of 25 times less than the fundamental. Furthermore, the sinusoids are not in phase. Each successive partial is 180 degrees out of phase from the previous one.
Triangle

Explanation of the Code

In the sine-wave example in chapter 1, we wrote a sine wave into a buffer and attached the buffer to an aiff file. In this example, we will write several sine-waves into the same buffer. On each pass, we will add the new samples to whatever samples already exist. In the C language, automatic variables (like the audio buffer) are not initialized, and could just contain any garbage values. We will not want to be adding sine-waves to garbage, so we will have to initialize all of the samples in the buffer to 0 before starting. This is accomplished on line 23 by way of a loop with no body.

The basic structure of the program comprises 2 nested loops. The inner loop, from lines 53-57, actually writes the sine-wave into the buffer. This is identical to chapter 1, except that there the sine waves are added with the += operator, rather than assigned with the = operator. The outer loop, from lines 25-58 calculates the frequency, phase and amplitude of the next sinusoid to be summed, and then enters the inner loop that actually writes that wave into the buffer. The total number of sine-waves written into the buffer is determined by the variable "numFrequenciesToAdd", declared on line 21.