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

  1. //Stepper.c
  2. //gcc MKAiff.c Stepper.c -o Stepper
  3. #include "MKAiff.h"
  4. #define SAMPLE_RATE 44100
  5. #define NUM_CHANNELS 2
  6. #define BITS_PER_SAMPLE 16
  7. #define NUM_SECONDS 6
  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 stepperValue[] = {330, 392.5, 495, 660, 785, 741, 660, 623, 660, 523.8, 440.5, 350};
  15. int numSteps = sizeof(stepperValue) / sizeof(*stepperValue);
  16. int stepperIndex = 0;
  17. double stepperDuration = 0.1;
  18. stepperDuration *= SAMPLE_RATE;
  19. double frequency;
  20. int wavelength;
  21. int i;
  22. for(i=0; i<numSamples; i+=NUM_CHANNELS)
  23. {
  24. if(!((i/NUM_CHANNELS) % (int)stepperDuration))
  25. {
  26. frequency = stepperValue[stepperIndex];
  27. wavelength = SAMPLE_RATE / frequency;
  28. stepperIndex++;
  29. stepperIndex %= numSteps;
  30. }
  31. audioBuffer[i] = 2 * (i/NUM_CHANNELS % wavelength) /(float) wavelength - 1;
  32. }
  33. aiffAppendFloatingPointSamples(aiff, audioBuffer, numSamples, aiffFloatSampleType);
  34. aiffSaveWithFilename(aiff, "Stepper.aif");
  35. aiff = aiffDestroy(aiff);
  36. return 1;
  37. }

Output:

Explanation of the Concepts

Many modular synthesizers also provide a 'step sequencer' that allows the user to select a number of discrete values by adjusting an array of knobs. The module outputs those values one by one, in sequence, then loops back to the first value after the last has been surpassed. The switching from one value to the next is generally triggered by a clock. This example models that behavior.

More specifically, this example uses a step sequencer to control the frequency of a a sawtooth wave. This will create a melody that repeats ad infinitum.

Explanation of the Code

Line 20 declares an array of discrete values, analogous to the position of the knobs on a physical step-sequencer. In the digital implementation, there could be any number of steps in the sequence (i.e. numbers in the array), so the computer will be responsible for figuring out how many there are, which it does on line 31. line 22 declares a variable that will keep track of where the sequencer is within its sequence, or which 'step' it is on.

Line 24 declares the interval in seconds of the clock that will advance the sequencer, and line 25 converts the seconds into the corresponding number of samples.

Much like the previous example, line 33 checks to see if the current frame corresponds to the beginning of a clock cycle, and if it does, the frequency (line 35) of the sawtooth wave are set to the current value in the sequencer's array, and the wavelength is updated accordingly (line 36). The sequencer is then advanced on line 37, in preparation for the next clock pulse. Line 37 ensures that the sequencer wraps back around on itself, so that the last step is folowed again by the first step.