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

  1. //WhiteNoise.c
  2. //gcc MKAiff.c WhiteNoise.c -o WhiteNoise
  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. int i;
  15. for(i=0; i<numSamples; i+=NUM_CHANNELS)
  16. {
  17. audioBuffer[i] = (random()/(float)RAND_MAX) * 2 - 1;
  18. }
  19. aiffAppendFloatingPointSamples(aiff, audioBuffer, numSamples, aiffFloatSampleType);
  20. aiffSaveWithFilename(aiff, "WhiteNoise.aif");
  21. aiff = aiffDestroy(aiff);
  22. return 1;
  23. }

Output:

Builds On

AIFF Template

Explanation of the Concepts

Ohm's acoustic law states that any sound, no matter how complex, is the sum of many (a bounded infinite number of them to be precise) individual sine waves (more will be said about this in the chapter on additive synthesis). Theoretically, white noise is the sum of every possible sine wave at every possible frequency (i.e. an unbounded infinite number of them), so we could make white-noise by making lots of sine waves (covered in the next chapter) and adding them to together. Of course, we can not make an infinite number of anything, including sine waves, for which reason true white noise can not exist. We could satisfy ourselves by making lots and lots of sine waves. Making enough of them to get a good approximation, however, is impractical for most applications, but luckily for us there is an easier way.

Noise is related to randomness, and filling an audio buffer with random samples creates noise. Here is a plot of such an audio buffer with the value of each sample (on the y axis) plotted as a function of time (on the x axis):

noise plot /

In places where the difference between two consecutive numbers is great the plot shows a steep slope. This creates a high-frequency component of the sound. In other places, where the difference between two consecutive numbers is slight, the plot shows a gentle slope, which corresponds to a low-frequency component. Truly random numbers will yield all possible slopes with equal probability, without being periodic, thereby creating an even frequency distribution. The following is a Fourier spectrograph of the above audio buffer. Software was used to analyze the frequency content of the buffer, and here amplitude (on the y axis) is plotted as a function of frequency (on the x axis).

noise FFT

This clearly shows an even distribution of frequency content over the audio spectrum.

Explanation of the Code

The code here is identical to the AIFF template, with the exception that line 23 now fills the first sample of each frame with a random number. random() is the standard c library function for generating pseudo-random numbers, and is declared in libc.h. This library does not need to be included explicitly, because it is included in "MKAudio.h", and therefore gets included on line 5. random() returns an integer between 0 and RAND_MAX (a constant, also defined in libc.h). The audio buffer needs to be filled with samples between 1 and -1. Dividing random() by RAND_MAX gives a number between 0 and 1. Note, however, that an integer divided by an integer always returns an integer, which in this case will always be zero, so at least one of the terms (in this case RAND_MAX) needs to be cast as a float so that the division results in a float. Multiplying the result by 2 results in a number between 0 and 2. Note, however, that although this:

(random()/(float)RAND_MAX) * 2;

creates a number between 0 and 2, this:

2*random()/(float)RAND_MAX;

which seems like an algebraic equivalent, does not. If RAND_MAX is the largest number representable by an integer (which is often the case), then multiplying random() by 2 will cause an integer overflow for values of random() that are greater than RAND_MAX/2. This will create values between -1*RAND_MAX and RAND_MAX. This would be okay, except that the exact value of RAND_MAX is not guaranteed, and besides, deliberately overflowing integers is probably not a safe practice, so it will be better to multiply by 2 after the division.

Subtracting 1 then gives a number between -1 and 1.