Pipe Organ "Front End"

/*

MIDI Pipe Organ Controller

==========================

Custom software to operate DC four rank unit organ.

Runs on Arduino Mega 2560.

This sketch accepts MIDI input from any keyboard (channel 1).

Depending on the stops selected, the Arduino performs

unification and duplexing and then outputs the data onto

channels 4 (Blockflote 2), 5 (Quintadena 8), 6 (Principal 4)and 7 (Gedeckt 8)

for transmission to four MIDI driver boards (64 outputs each).

Top C acts as a general cancel after which the user selects stops

by pressing keys in the top octave. Playing a note outside this octave

causes the top octave to revert back to its normal playing function.

Equipment: Arduino Mega with MIDI shield

created 2016 Oct. 12

modified 2016 Dec. 18

by John Coenraads

*/

// Declarations==========================================

//Counter (old Fortran habit)

int i, j, k;

//Transmit variables

byte noteStatusTx;

byte noteNumberTx;

byte noteVelocityTx;

//Receive variables

byte noteStatusRx;

byte noteNumberRx;

byte noteVelocityRx;

//Flag set when general cancel (Top C, Note # 96) is pressed.

byte setStops = 0;

const byte genCan = 96;

//Flag to keep track of last valid status (90, 80)

byte statusBuffer = 0;

//Array to keep track of which pipes are on or off. Zero = OFF.

//Values are incremented when a note ON message is received.

//Values are decremented when a note OFF message is received.

//Col. 0 = Gedeckt, 1 = Principal, 2 = Quintaton, 3 = Blockflote.

//

byte pipeArray [96][4];

//Array to track which stops are selected.

//Col. 0 = Gedeckt, 1 = Principal, 2 = Quintaton, 3 = Blockflote.

//Up to four stops per rank.

//Each cell contains offset, e.g., -5, 0, 7, 12, 19, etc, to be added

// to noteNumber. But if 1, no stop selected.

//

byte manStops [4][4];

int turnOnOff; // 1 = turn on, -1 = turn off

//Initialize =========================================================

void setup()

{

// Set MIDI baud rate:

Serial.begin(31250);

//Perform a general cancel

genCancel();

}

//Main Loop ===========================================================

void loop()

{

if (Serial.available())

{

noteStatusRx = Serial.read ();

if (noteStatusRx > 0x7F) //is status byte

{

if ((noteStatusRx == 0x90) || //channel 1, note ON

(noteStatusRx == 0x80)) //channel 1, note OFF

{

statusBuffer = noteStatusRx;

while (!Serial.available()) {} //wait for serial data

noteNumberRx = Serial.read ();

while (!Serial.available()) {} //wait for serial data

noteVelocityRx = Serial.read ();

processDataRx();

}

else //not 90 or 80

{

statusBuffer = 0;

}

}

else //is data byte

{

if (statusBuffer != 0)

{

noteNumberRx = noteStatusRx;

noteStatusRx = statusBuffer;

while (!Serial.available()) {} //wait for serial data

noteVelocityRx = Serial.read ();

processDataRx();

}

}

}

}

//Process data received: status, note number, velocity

void processDataRx ()

{

if (noteNumberRx == genCan)

{

setStops = 1;

genCancel(); //Perform a general cancel

}

else

{

if (setStops == 1) processStop();

if (setStops == 0) processNote();

}

}

//General Cancel Procedure ===============================================

void genCancel()

{

// Send out an "All Notes Off" on channels 4,5,6,7

Serial.write(0xB3);

Serial.write(0x7B);

Serial.write(0x00);

Serial.write(0xB4);

Serial.write(0x7B);

Serial.write(0x00);

Serial.write(0xB5);

Serial.write(0x7B);

Serial.write(0x00);

Serial.write(0xB6);

Serial.write(0x7B);

Serial.write(0x00);

//Turn all stops off.

//1 = Off. Any other number is offset to

//be added to noteNumber

for (i = 0; i < 4; i++)

{

manStops[i][0] = 1;

manStops[i][1] = 1;

manStops[i][2] = 1;

manStops[i][3] = 1;

}

//Clear pipe array: 0 = note is off

for (i = 0; i < 96; i++)

{

pipeArray[i] [0] = 0;

pipeArray[i] [1] = 0;

pipeArray[i] [2] = 0;

pipeArray[i] [3] = 0;

}

//Clear serial input buffer.

while (Serial.available() > 0)

{

Serial.read();

}

//Clear status buffer

statusBuffer = 0;

}

//Turn stops on as per top octave keys pressed ===================================

void processStop()

{

switch (noteNumberRx)

{

case 84: // C Gedeckt not used

manStops [3][0] = 1; break;

case 85: // C# Principal 2 2/3

manStops [2][1] = 7; break;

case 86: // D Principal not used

manStops [3][1] = 1; break;

case 87: // D# Blockflote 2 2/3

manStops [2][3] = -5; break;

case 88: // E Quintaton not used

manStops [3][2] = 1; break;

case 89: // F Gedeckt 8

manStops [0][0] = 0; break;

case 90: // F# Gedeckt 4

manStops [1][0] = 12; break;

case 91: // G Quintaton 8

manStops [0][2] = 0; break;

case 92: // G# Quintaton 4

manStops [1][2] = 12; break;

case 93: // A Principal 4

manStops [0][1] = 0; break;

case 94: // A# Principal 2

manStops [1][1] = 12; break;

case 95: // B Blockflote 2

manStops [0][3] = 0; break;

default: //Revert back to normal playing

setStops = 0;

}

}

//Turn note received On or Off ===========================================

void processNote ()

{

if ((noteStatusRx == 0x90) && // if 90

(noteVelocityRx != 0)) // and with non-zero note velocity

{

turnOnOff = 1; // turn on

noteVelocityTx = 64; // with medium velocity

}

else

{

turnOnOff = -1; // turn off (was 80, or 90 with zero velocity)

noteVelocityTx = 0; //by setting velocity to zero

}

for (j = 0; j < 4; j++)

{

if (manStops [j] [0] != 1) //Gedeckt

{

noteStatusTx = 0x96;

noteNumberTx = noteNumberRx + manStops [j][0];

if (noteNumberTx > 91) //If top note exceeded

{

noteNumberTx = noteNumberTx - 12; //play octave lower

}

pipeArray [noteNumberTx][0] = pipeArray [noteNumberTx][0] + turnOnOff;

if (((pipeArray [noteNumberTx][0] == 1) && (turnOnOff == 1)) ||

((pipeArray [noteNumberTx][0] == 0) && (turnOnOff == -1)))

{

Serial.write (noteStatusTx);

Serial.write (noteNumberTx);

Serial.write (noteVelocityTx);

}

}

if (manStops [j] [1] != 1) //Principal

{

noteStatusTx = 0x95;

noteNumberTx = noteNumberRx + manStops [j][1];

if (noteNumberTx > 91) //If top note exceeded

{

noteNumberTx = noteNumberTx - 12; //play octave lower

}

pipeArray [noteNumberTx][1] = pipeArray [noteNumberTx][1] + turnOnOff;

if (((pipeArray [noteNumberTx][1] == 1) && (turnOnOff == 1)) ||

((pipeArray [noteNumberTx][1] == 0) && (turnOnOff == -1)))

{

Serial.write (noteStatusTx);

Serial.write (noteNumberTx);

Serial.write (noteVelocityTx);

}

}

if (manStops [j] [2] != 1) //Quintaton

{

noteStatusTx = 0x94;

noteNumberTx = noteNumberRx + manStops [j][2];

if (noteNumberTx > 91) //If top note exceeded

{

noteNumberTx = noteNumberTx - 12; //play octave lower

}

if (noteNumberTx < 48) //Quintaton lacks bottom octave

{

noteStatusTx = 0x96; //Borrow bottom octave from Gedeckt

}

pipeArray [noteNumberTx][2] = pipeArray [noteNumberTx][2] + turnOnOff;

if (((pipeArray [noteNumberTx][2] == 1) && (turnOnOff == 1)) ||

((pipeArray [noteNumberTx][2] == 0) && (turnOnOff == -1)))

{

Serial.write (noteStatusTx);

Serial.write (noteNumberTx);

Serial.write (noteVelocityTx);

}

}

if (manStops [j] [3] != 1) //Blockflote

{

noteStatusTx = 0x93;

noteNumberTx = noteNumberRx + manStops [j][3];

if (noteNumberTx > 91) //If top note exceeded

{

noteNumberTx = noteNumberTx - 12; //play octave lower

}

pipeArray [noteNumberTx][3] = pipeArray [noteNumberTx][3] + turnOnOff;

if (((pipeArray [noteNumberTx][3] == 1) && (turnOnOff == 1)) ||

((pipeArray [noteNumberTx][3] == 0) && (turnOnOff == -1)))

{

Serial.write (noteStatusTx);

Serial.write (noteNumberTx);

Serial.write (noteVelocityTx);

}

}

}

}

// trace procedure

void trace (byte info)

{

Serial.write (0xF3);

Serial.write (info);

}