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

  1. //SampleAndHold.c
  2. //gcc MKAiff.c SampleAndHold.c -o SampleAndHold
  3. #include "MKAiff.h"
  4. #define SAMPLE_RATE 44100
  5. #define NUM_CHANNELS 1
  6. #define BITS_PER_SAMPLE 16
  7. #define NUM_SECONDS 8
  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 minimumSample = 30, maximumSample = 800;
  14. float sampleDuration = 0.1;
  15. sampleDuration *= SAMPLE_RATE;
  16. srandom(time(NULL));
  17. float audioBuffer[numSamples];
  18. double frequency;
  19. int wavelength;
  20. int i;
  21. for(i=0; i<numSamples; i+=NUM_CHANNELS)
  22. {
  23. if(!(i/NUM_CHANNELS % (int)sampleDuration))
  24. {
  25. frequency = random()/(double)RAND_MAX;
  26. frequency *= (maximumSample-minimumSample);
  27. frequency += minimumSample;
  28. wavelength = SAMPLE_RATE / frequency;
  29. }
  30. audioBuffer[i] = 2 * (i/NUM_CHANNELS % wavelength) /(float) wavelength - 1;
  31. }
  32. aiffAppendFloatingPointSamples(aiff, audioBuffer, numSamples, aiffFloatSampleType);
  33. aiffSaveWithFilename(aiff, "SampleAndHold.aif");
  34. aiff = aiffDestroy(aiff);
  35. return 1;
  36. }

Output:

Explanation of the Concepts

Many analog synthesizers have a 'Sample and Hold' module, whose purpose is to grab onto the value of a signal at a specific point in time, remember that value, and provide it upon request. In the digital domain, where we have direct access to the actual numeric values of each sample in the audio stream, plenty of spare memory to hold those values, and a programming language whose most basic purpose is to be able to recall stored values at will, the actual operation of a sample and hold mechanism becomes trivial. Nonetheless, one of the classic sounds of modular synthesizers is the use of a sample and hold unit to sample a noise generator at regular intervals, and use the sampled values to control the frequency of an audio oscillator. We will reproduce that behavior here. The audio signal will be a Sawtooth wave.

Explanation of the Code

Lines 18-20 set up variables for the sample and hold unit. The unit will take random samples between minimumSample and maximumSample every sampleDuration number of seconds. line 20 converst this number of seconds to the corresponding number of frames. Line 31 checks to see if the current frame (i/NUM_CHANNELS) corresponds to the beginning of a sample period (% sampleDuration). If it is, then line 33 sets the frequency of the sawtooth wave to a random number between 1 and 0, and lines 34 and 35 scale that number so that it is between the specified minimumSample and maximumSample. Since the sawtooth wave uses its wavelength rather than its frequency to calculate the actual sample, the wavelength is updated to f=reflect the change in frequency on line 36. Line 38 just makes the standard calculation of the value of the sawtooth wave.