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):
Here's the 4 minute version with the tempo pot all the way up:
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.