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

  1. //ForwardBackward.c
  2. //gcc MKAiff.c ForwardBackward.c -o ForwardBackward
  3. #include "MKAiff.h"
  4. #define AUDIO_SAMPLE_PATH "demo.aif"
  5. #define NUM_SECONDS 3
  6. int main()
  7. {
  8. MKAiff* audioSample = aiffWithContentsOfFile(AUDIO_SAMPLE_PATH);
  9. if(audioSample == NULL) return 1;
  10. const int NUM_CHANNELS = aiffNumChannels (audioSample);
  11. const int SAMPLE_RATE = aiffSampleRate (audioSample);
  12. const int BITS_PER_SAMPLE = aiffBitsPerSample(audioSample);
  13. int WAVETABLE_NUM_SAMPLES, WAVETABLE_NUM_FRAMES, sampleLocation;
  14. if(aiffHasInstrumentInfo(audioSample))
  15. {
  16. uint32_t beginning, end;
  17. aiffPositionInFramesOfMarkerWithID(audioSample, aiffInstrumentSustainLoopStartMarkerID(audioSample), &beginning);
  18. aiffPositionInFramesOfMarkerWithID(audioSample, aiffInstrumentSustainLoopEndMarkerID (audioSample), &end);
  19. WAVETABLE_NUM_FRAMES = end-beginning;
  20. sampleLocation = beginning;
  21. }
  22. else
  23. {
  24. WAVETABLE_NUM_FRAMES = aiffDurationInSamples(audioSample) / 10;
  25. srandom(time(NULL));
  26. sampleLocation = (random() / (double) RAND_MAX) * (aiffDurationInFrames(audioSample) - WAVETABLE_NUM_FRAMES);
  27. }
  28. WAVETABLE_NUM_SAMPLES = WAVETABLE_NUM_FRAMES * NUM_CHANNELS;
  29. const int NUM_BUFFERS = (NUM_SECONDS * SAMPLE_RATE) / (2 * WAVETABLE_NUM_FRAMES);
  30. MKAiff* aiff = aiffWithDurationInSeconds(NUM_CHANNELS, SAMPLE_RATE, BITS_PER_SAMPLE, NUM_SECONDS);
  31. if(aiff == NULL) return 1;
  32. float wavetable[WAVETABLE_NUM_SAMPLES * 2];
  33. aiffSetPlayheadToFrames(audioSample, sampleLocation);
  34. aiffReadFloatingPointSamplesAtPlayhead(audioSample, wavetable, WAVETABLE_NUM_SAMPLES);
  35. int i, j;
  36. for(i=0; i<WAVETABLE_NUM_SAMPLES; i+=NUM_CHANNELS)
  37. for(j=0; j<NUM_CHANNELS; j++)
  38. wavetable[2*WAVETABLE_NUM_SAMPLES - (i+NUM_CHANNELS) + j] = wavetable[i+j];
  39. for(i=0; i<NUM_BUFFERS; i++)
  40. aiffAppendFloatingPointSamples(aiff, wavetable, WAVETABLE_NUM_SAMPLES*2, aiffFloatSampleType);
  41. aiffSaveWithFilename(aiff, "ForwardBackward.aif");
  42. aiffDestroy(aiff);
  43. aiffDestroy(audioSample);
  44. return 0;
  45. }

Output:

Explanation of the Concepts

This example reads the "Sustain Loop" from the audio-sample "demo.aif" and copies it alternately forwards and backwards into a new audio file.

It is possible to create a forward-backward loop by making a wavetable that is just big enough to hold the forward version of the audio, and using some sort of play-head that scans the wavetable and switches direction when it reaches the beginning or end of the table. This possibility is implemented in the ASR example, later in this chapter. It will, however, be easier to create a wavetable that is twice as big as the sound to be looped, and of course, put the forward version in the first half of the table, and copy that backwards into the second half of the table, so that the table can always be read in the forward direction. This is the approach taken here. In either case, the difficult part is that, because the channels are interleaved, if we just reverse all of the samples in a stereo buffer, the channels will get switched, so that the right channel goes to the left ear, and vice-versa. This is not usually the desired effect, so care must be taken to only reverse the order of the frames, without reversing the order of the samples within those frames.

This example checks to see if the pre-recorded audio-sample already has 'instrument info' embedded within it, according to the previous example. If it does, then the wavetable is made from the 'sustain loop', otherwise, some samples are taken at random and used for the wavetable.

In this example, the wavetable is simply copied into a new audio file as many times as it will fit in its entirety, before the end of the new file draws too nigh. In real-time applications you would keep a pointer to the next sample in the wavetable, which would be incremented by 1 (and wrapped around to the beginning after the last sample) each time one sample is given to the output stream.

Explanation of the Code

Code Explanation