Close

A Composition in a Box (with a Pot): Terry Riley's In C

A project log for Fluxamasynth Modules

The Fluxamasynth is a module that makes it easy to add high quality MIDI sound to any Arduino or Raspberry Pi project.

fluxlyFluxly 10/19/2018 at 22:330 Comments

One of the example programs bundled with the Fluxamasynth Arduino library is an interpretation of Terry Riley's 1964 algorithmic composition In C. It shows one way of encoding a composition with multiple parts and an adjustable master tempo.

In C was written for 20 to 35 players. Each player works their way through 53 short phrases, listening to the other players and following the rules of the score. The basic structural rules are that a player repeats a phrase as long as they like before moving to the next. Players always move forward through the score, and the piece is finished when everyone reaches the end, which usually takes around 40 minutes at the recommended tempo.

A single potentiometer attached to analog in 1, on top of a Fluxamasynth Arduino shield.

The original score is available under a Creative Commons license. 

One thing the Fluxamasynth allows is to play with different physical interfaces to algorithmic music like In C. When played by human musicians the tempo is determined by a pulse on the piano, and players listening to each other. In the Fluxamasynth version a single potentiometer controls the tempo and allows you to speed up the 40 minute performance to 3 minutes or so if you want.

// Terry Riley's In C
// This code expects an analog input on A1 to control tempo
 
#include <Fluxamasynth.h>
#include <PgmChange.h>
 
#define numInstruments  13
#define numParts 53
 
Fluxamasynth synth;
 
// The PgmChange.h header defines all of these instrument names
int instrument[numInstruments] = {
  BANK0_Vibraphone, 
  BANK0_Marimba,
  BANK0_ElPiano1,
  BANK0_Vibraphone,
  BANK0_ElPiano1,
  BANK0_ElPiano1,
  BANK0_Vibraphone,
  BANK0_Marimba,
  BANK0_ElGrd_Piano3,
  BANK0_Vibraphone, 
  BANK0_Marimba,
  BANK0_SynthBass1,
  BANK0_Grand_Piano1
};
 
// An array containing the score. The first element is a MIDI note 0-127
// followed by a duration in sixteenth notes. Each part ends with a 255 
//
byte score[] = {
  0, 1, 255,                                             // part 0
  60, 1, 64, 3, 60, 1, 64, 3, 60, 1, 64, 3, 255,         // part 1
  60, 1, 64, 1, 65, 2, 64, 2, 0, 3, 255,                 // part 2
  0, 2, 64, 2, 65, 2, 64, 2, 255,                        // part 3
  0, 2, 64, 2, 65, 2, 67, 2, 255,                        // part 4
  64, 2, 65, 2, 67, 2, 0, 2, 255,                        // part 5
  72, 16, 72, 16, 255,                                   // part 6
  0, 14, 60, 1, 60, 1, 60, 2, 0, 18, 255,                // part 7
  67, 24, 65, 16, 65, 16, 255,                           // part 8
  71, 1, 67, 1, 0, 14, 255,                              // part 9
  71, 1, 67, 1, 255,                                     // part 10
  65, 1, 67, 1, 71, 1, 67, 1, 71, 1, 67, 1, 255,         // part 11
  65, 2, 67, 2, 71, 16, 71, 4, 255,                      // part 12
  71, 1, 67, 3, 67, 1, 65, 1, 67, 2, 0, 3, 67, 7, 255,   // part 13
  72, 16, 71, 16, 67, 16, 66, 16, 255,                   // part 14
  67, 1, 0, 15, 255,                                     // part 15
  67, 1, 71, 1, 72, 1, 71, 1, 255,                       // part 16
  71, 1, 72, 1, 71, 1, 72, 1, 71, 1, 0, 1, 255,          // part 17
  64, 1, 68, 1, 64, 1, 68, 1, 64, 3, 64, 2, 255,         // part 18
  0, 6, 79, 6, 255,                                      // part 19
  64, 1, 66, 1, 64, 1, 66, 1, 57, 3, 64, 1, 65, 1, 
  64, 1, 65, 1, 64, 1, 255,                              // part 20
  66, 12, 255,                                           // part 21
  64, 6, 64, 6, 64, 6, 64, 6, 64, 6, 66, 6, 67, 6, 
  69, 6, 71, 2, 255,                                     // part 22
  64, 2, 66, 6, 66, 6, 66, 6, 66, 6, 67, 6, 
  69, 6, 71, 6, 255,                                     // part 23
  64, 2, 66, 2, 67, 6, 67, 6, 67, 6, 67, 6, 
  67, 6, 69, 6, 71, 2, 255,                              // part 24
  64, 2, 66, 2, 67, 2, 69, 6, 69, 6, 69, 6, 
  69, 6, 69, 6, 71, 6, 255,                              // part 25
  64, 2, 66, 2, 67, 2, 69, 2, 71, 6, 71, 6, 71, 6, 
  71, 6, 71, 6, 255,                                     // part 26
  64, 1, 66, 1, 64, 1, 66, 1, 67, 2, 64, 1, 
  67, 1, 66, 1, 64, 1, 66, 1, 64, 1, 255,                // part 27
  64, 1, 66, 1, 64, 1, 66, 1, 64, 3, 64, 1, 255,         // part 28
  64, 12, 67, 12, 72, 12, 255,                           // part 29
  72, 24, 255,                                           // part 30
  67, 1, 65, 1, 67, 1, 71, 1, 67, 1, 71, 1, 255,         // part 31
  65, 1, 67, 1, 65, 1, 67, 1, 71, 1, 65, 13, 67, 6, 255, // part 32
  67, 1, 65, 1, 0, 2, 255,                               // part 33
  67, 1, 65, 1, 255,                                     // part 34
  65, 1, 67, 1, 71, 1, 67, 1, 71, 1, 67, 1, 71, 1, 
  67, 1, 71, 1, 67, 1, 0, 14, 70, 4, 79, 12,
  81, 2, 79, 4, 83, 2, 79, 6, 79, 2, 76, 12, 
  79, 2, 78, 14, 0, 10, 76, 10, 77, 24, 255,             // part 35
  65, 1, 67, 1, 71, 1, 67, 1, 71, 1, 67, 1, 255,         // part 36
  65, 1, 67, 1, 255,                                     // part 37
  65, 1, 67, 1, 71, 1, 255,                              // part 38
  71, 1, 67, 1, 65, 1, 67, 1, 71, 1, 72, 1, 255,         // part 39
  71, 1, 65, 1, 255,                                     // part 40
  71, 1, 67, 1,                                          // part 41
  72, 16, 71, 16, 69, 16, 72, 16, 255,                   // part 42
  77, 1, 76, 1, 77, 1, 76, 1, 76, 2, 76, 2, 76, 2, 77, 
  1, 76, 1, 255,                                         // part 43
  77, 2, 76, 4, 76, 2, 72, 4, 255,                       // part 44
  74, 4, 74, 4, 67, 4, 255,                              // part 45
  67, 1, 74, 1, 76, 1, 74, 1, 0, 2, 67, 2, 67, 2, 0, 
  2, 67, 2, 67, 1, 74, 1, 76, 1, 74, 1, 255,             // part 46
  74, 1, 76, 1, 74, 2, 255,                              // part 47
  67, 24, 67, 16, 64, 16, 64, 4, 255,                    // part 48
  65, 1, 67, 1, 70, 1, 67, 1, 70, 1, 67, 1, 255,         // part 49
  65, 1, 67, 1, 255,                                     // part 50
  65, 1, 67, 1, 70, 1, 255,                              // part 51
  67, 1, 70, 1, 255,                                     // part 52
  70, 1, 67, 1, 255                                      // part 53
};
 
unsigned long  startTime;
int chance;
int max = 0;
int spread = 0;
int targetVolume=127; 
int startPart = 1;
int partIndices[53];
int partBeats[53];
int index[numInstruments]; 
int part[numInstruments];
int beat[numInstruments];
int startDelay[numInstruments];
int note[numInstruments];
int volume[numInstruments];
int prevNote[numInstruments];
int noteCount[numInstruments];
boolean playNote[numInstruments];
boolean hitEnd[numInstruments];
boolean started[numInstruments];
boolean finished[numInstruments];
int numFinished = 0;
int tempo = 30;     // 30 to 150, center is 90
 
void setNextNote(int j) {
  // Sets up the next note to be played
  if (index[j] > 0) {
    prevNote[j] = score[index[j]-1];
  } 
  else {
    prevNote[j] = 0;
  }
  note[j] = score[index[j]];
  if (j == 11) { 
    note[j] -= 24; 
  }
  noteCount[j] = score[index[j]+1];
  playNote[j] = true;
}
 
void setup() {
  // The tempo is determined by a potentiometer on Analog 1. 
  pinMode(A0, OUTPUT);
  pinMode(A2, OUTPUT);
  digitalWrite(A0, LOW); // pot is powered by A0 and A2
  digitalWrite(A2, HIGH);
  // Calculate number of beats in each part
  int beatCount = 0;
  int c = 3;
  partIndices[0] = 0;
  for (int i=1; i<53; i++) {
    partIndices[i]=c;
    while (score[c] != 255) {
      c++;
      beatCount+=score[c];
      c++;
    }
    c++;
    partBeats[i] = beatCount;
    beatCount=0;
  }
  synth.setMasterVolume(75);
 
  // Set up the individual instruments
  for (int i=0; i<numInstruments; i++) {
    synth.programChange(0, i, instrument[i]);
    pan(i,127/numInstruments*i);
    volume[i] = targetVolume;
    synth.setChannelVolume(i, volume[i]);
    synth.setReverb(i, 3, 127, 25);
    //synth.setChorus(i, 3, 64, 25, 25);
  }
  randomSeed(analogRead(3));
  for (int i=0; i<numInstruments; i++) {
    index[i] = 0;
    part[i] = 0;
    note[i] = 0;
    noteCount[i] = 0;
    startDelay[i] = random(1, 20) * partBeats[1];
    playNote[i] = true;
    started[i] = false;
  }
  synth.setChannelVolume(9,0);  // Turn off the percussion channel
}
 
void loop() {
  tempo = map(analogRead(A1), 0, 1024, 30, 150);
 
  startTime = millis();
  Serial.print(startTime);
  Serial.print(" ");
  for (int i=0; i< numInstruments; i++) { 
    if ((!started[i]) && (startDelay[i] == 0)) {
      started[i] = true;
      part[i]+=startPart;
      index[i] = partIndices[part[i]];
      setNextNote(i);
    } 
    else {
      startDelay[i]--;
    }
 
    if ((started[i]) && (!finished[i])) {
      if (playNote[i]) {
        synth.allNotesOff(i);
        synth.noteOn(i, note[i], 127);
        playNote[i] = false;
      } 
      beat[i]++;
      noteCount[i]--;
      if (noteCount[i] == 0) {
        if (beat[i] >= partBeats[part[i]]) {
          beat[i] = 0;
          // Add a spread to hold back the lead if it gets too far ahead
          if ((max-part[i] > 4)) {
            chance = 50;
          } else {
            chance = 90;
          }
          if ((max == part[i]) && (spread > 4)) {
            chance = 99;
          }
          if ((random(1, 100) > chance)) {
            part[i]++;
            if ((i==9) || (random(1, 100)>90)) {
              synth.setChannelVolume(i, 0);
            } 
            else {
              synth.setChannelVolume(i, volume[i]);
            }   
            max = 0;
            spread=0;
            for (int j=0; j<numInstruments; j++) {
              if (part[j]> max) { 
                max = part[j]; 
              }
              if ((max-part[j])>spread) {
                spread = max-part[j];
              }
            }
            if (part[i]>numParts) {
              finished[i] = true;
              numFinished++;
            }
          }
          index[i] = partIndices[part[i]];
        } 
        else {
          index[i] = index[i]+2;
        }
        setNextNote(i);
      }
    } 
  }
 
  delay(tempo-(millis()-startTime));
 
}
 
void pan(int channel, int value) {
  // TODO: Add this to library
  byte command[3] = { 
    (0xb0 | (channel & 0x0f)), 0x0A, (value)   };
  synth.fluxWrite(command, 3);
}

Here's the full length song at normal tempo (a bit rushed in the part changes for brevity):

Play Sample 1

Here's the 4 minute version with the tempo pot all the way up:

Play Sample 2

Discussions