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

  1. //ForwardBackwardPitch.c
  2. //gcc MKAiff.c ForwardBackwardPitch.c -o ForwardBackwardPitch
  3. #include "MKAiff.h"
  4. #include <math.h>
  5. #define AUDIO_SAMPLE_PATH "demo.aif"
  6. #define NUM_SECONDS 3
  7. #define PLAYBACK_PITCH 80
  8. void linearInterpolateBuffer(float* previousFrame, int numChannels, float* input, int inNumFrames, float* output, int outNumFrames);
  9. int main()
  10. {
  11. MKAiff* audioSample = aiffWithContentsOfFile(AUDIO_SAMPLE_PATH);
  12. if(audioSample == NULL) return 1;
  13. const int NUM_CHANNELS = aiffNumChannels (audioSample);
  14. const int SAMPLE_RATE = aiffSampleRate (audioSample);
  15. const int BITS_PER_SAMPLE = aiffBitsPerSample(audioSample);
  16. int WAVETABLE_NUM_SAMPLES, WAVETABLE_NUM_FRAMES, sampleLocation;
  17. int originalPitch;
  18. if(aiffHasInstrumentInfo(audioSample))
  19. {
  20. uint32_t beginning, end;
  21. aiffPositionInFramesOfMarkerWithID(audioSample, aiffInstrumentSustainLoopStartMarkerID(audioSample), &beginning);
  22. aiffPositionInFramesOfMarkerWithID(audioSample, aiffInstrumentSustainLoopEndMarkerID (audioSample), &end);
  23. WAVETABLE_NUM_FRAMES = end-beginning;
  24. sampleLocation = beginning;
  25. originalPitch = aiffInstrumentBaseNote(audioSample);
  26. }
  27. else
  28. {
  29. WAVETABLE_NUM_FRAMES = aiffDurationInSamples(audioSample) / 100;
  30. srandom(time(NULL));
  31. sampleLocation = (random() / (double) RAND_MAX) * (aiffDurationInFrames(audioSample) - WAVETABLE_NUM_FRAMES);
  32. originalPitch = 62;
  33. }
  34. MKAiff* aiff = aiffWithDurationInSeconds(NUM_CHANNELS, SAMPLE_RATE, BITS_PER_SAMPLE, NUM_SECONDS);
  35. if(aiff == NULL) return 1;
  36. WAVETABLE_NUM_FRAMES *= 2;
  37. WAVETABLE_NUM_SAMPLES = WAVETABLE_NUM_FRAMES * NUM_CHANNELS;
  38. float wavetable[WAVETABLE_NUM_SAMPLES];
  39. aiffSetPlayheadToFrames(audioSample, sampleLocation);
  40. aiffReadFloatingPointSamplesAtPlayhead(audioSample, wavetable, WAVETABLE_NUM_SAMPLES/2);
  41. int i, j;
  42. for(i=0; i<WAVETABLE_NUM_SAMPLES/2; i+=NUM_CHANNELS)
  43. for(j=0; j<NUM_CHANNELS; j++)
  44. wavetable[WAVETABLE_NUM_SAMPLES - (i+NUM_CHANNELS) + j] = wavetable[i+j];
  45. double PLAYBACK_SPEED = pow(pow(2, (originalPitch - PLAYBACK_PITCH)), (1/12.0));
  46. int numInterpolatedSamples = PLAYBACK_SPEED * WAVETABLE_NUM_SAMPLES;
  47. while(numInterpolatedSamples % NUM_CHANNELS) numInterpolatedSamples -= 1;
  48. float interpolatedBuffer[numInterpolatedSamples];
  49. float previousFrame[NUM_CHANNELS]; for(i=0; i<NUM_CHANNELS; previousFrame[i] = wavetable[i++]);
  50. linearInterpolateBuffer(previousFrame, NUM_CHANNELS, wavetable, WAVETABLE_NUM_FRAMES, interpolatedBuffer, numInterpolatedSamples / NUM_CHANNELS);
  51. int NUM_BUFFERS = (NUM_SECONDS * SAMPLE_RATE * NUM_CHANNELS) / (numInterpolatedSamples);
  52. for(i=0; i<NUM_BUFFERS; i++)
  53. aiffAppendFloatingPointSamples(aiff, interpolatedBuffer, numInterpolatedSamples, aiffFloatSampleType);
  54. aiffSaveWithFilename(aiff, "ForwardBackwardPitch.aif");
  55. aiffDestroy(aiff);
  56. aiffDestroy(audioSample);
  57. return 0;
  58. }
  59. void linearInterpolateBuffer(float* previousFrame, int numChannels, float* input, int inNumFrames, float* output, int outNumFrames)
  60. {
  61. int i, j, prevIndex;
  62. double distance, prevValue, nextValue;
  63. for(i=0; i<outNumFrames; i++)
  64. {
  65. for(j=0; j<numChannels; j++)
  66. {
  67. distance = i * (inNumFrames / (float)outNumFrames) - 1;
  68. prevIndex = ((int)distance) * numChannels + j;
  69. nextValue = distance < 0 ? input[j] : input[prevIndex + numChannels];
  70. prevValue = distance < 0 ? previousFrame[j] : input[prevIndex];
  71. distance -= (int)distance;
  72. if(distance < 0) distance += 1;
  73. output[i*numChannels+j] = (nextValue-prevValue) * distance + prevValue;
  74. }
  75. }
  76. for(j=0; j<numChannels; j++)
  77. previousFrame[j] = input[(inNumFrames - 1) * numChannels + j];
  78. }

Output:

Explanation of the Concepts

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

This example is identical to the previous example, except that this example transposes the pitch of the wavetable using linear interpolation. The interpolation used here is identical to what is discussed in the chapter on "How to use interpolation", which is linked in the "Builds On" section above. The only difference is that here, the interpolation is done all at once, instead of in many small buffers. Furthermore, in the interpolation example, the pitch of the resulting audio was expressed in terms of transposition from the original. Here, if the aiff file contains instrument info, then the audio sample knows its own original pitch. This means we can specify the absolute pitch of the resulting audio, and let the program calculate out how far a transposition that requires. If there is no instrument info, then the pitch is chosen at random.

This example can transpose the original sound sample to any pitch. Here are some more examples that are transposed to do, sol, le, and do (an octave below), in a key a major third above the original audio.






It is easy to hear how this technique can be used to create unusual timbres.

Explanation of the Code

Code Explanation