A Simple VPO

/*

MIDI merger

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

This MIDI merge sketch is designed to scan a 32 pedal board and

output the corresponding MIDI codes via serial port 0 on channel 1 (0).

It also receives MIDI input via serial ports 0 (Gt.), 1 (Sw.) and 2 (Pos.).

The data received are assigned to MIDI channels 2 (1), 3 (2) and 4 (3)

respectively and are then merged and transmitted out through port 0.

The 32 organ pedal switches are arranged in a 4x8 matrix and are diode isolated.

Columns are connected to, pins 10-13. Scanning is accomplished by driving

these pins low (negative logic). Input is sampled via pins 2-9.

These inputs are normally high by using internal pullup resistors.

Low C is at pin 2x10, C# at pin 3x10 etc.

Equipment: Arduino Mega 2560 with three MIDI shields to accomodate three manuals.

created 2015 OCT 10

modified 2015 OCT 10

by John Coenraads

*/

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

//Counters (old Fortran habit)

int i, j, k;

int colCount, rowCount; //matrix column (pins 2-5) and row (pins 6-13) count

byte inputBit; //input data from port D

byte noteNumber; //noteNumber for pedal scan, low C = 36

const byte debounceCount = 5; //Note ON if count = 4, OFF if count = 0

byte debounceArray [73]; //holds debounce count for each pedal switch

byte onOffArray [73]; //HIGH if note is on, LOW if note is off

//Receive variables

byte noteStatusRx;

byte noteNumberRx;

byte noteVelocityRx;

//Flag to keep track of last valid status (0x90,0x80)

byte statusBuffer = 0, statusBuffer1 = 0, statusBuffer2 = 0;

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

void setup()

{

delay (2000); // 2 second delay to let inputs stabilize

// Set MIDI baud rate:

Serial.begin(31250); Serial1.begin(31250); Serial2.begin(31250);

//Clear serial input buffers

while (Serial.available() > 0)

{Serial.read();}

while (Serial1.available() > 0)

{Serial1.read();}

while (Serial2.available() > 0)

{Serial2.read();}

//Initialize Pins 10 to 13 for output (normally high)

for (i = 10; i < 14; i++)

{

pinMode (i, OUTPUT);

digitalWrite (i, HIGH);

}

//Initialize Pins 2 to 9 for input (default). Normally high (via internal pullups)

for (i = 2; i < 10; i++)

{ pinMode (i, INPUT_PULLUP);}

//Initialize debounce count array to zero

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

{debounceArray[i] = 0; onOffArray [i] = LOW;}

}

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

void loop()

{

if (Serial.available ())

{processSerial();}

if (Serial1.available ())

{processSerial1();}

if (Serial2.available ())

{processSerial2();}

scanPedal();

}

//Scan pedal input, convert to MIDI and output via port 0, channel 1.

void scanPedal ()

{

noteNumber = 36; //start at low C

for (colCount = 10; colCount < 14; colCount++)

{

digitalWrite (colCount, LOW);

for (rowCount = 2; rowCount < 10 ; rowCount++)

{

inputBit = digitalRead(rowCount); //read bit

if (inputBit == HIGH) //bit HIGH, switch open

{ turnNoteOFF(); }

else //bit LOW, switch closed

{ turnNoteON(); }

noteNumber++; //move on to next note

}

digitalWrite (colCount, HIGH);

}

}

//Turn note on. Debouncing is achieved by requiring that several turnNoteON requests

//are received before sending out noteOn MIDI message

void turnNoteON ()

{

if (debounceArray[noteNumber] < debounceCount)

{

debounceArray [noteNumber] = debounceArray [noteNumber] + 1;

if ((debounceArray[noteNumber] == debounceCount) && (onOffArray[noteNumber] == LOW))

{

onOffArray[noteNumber] = HIGH;

Serial.write (0x90); //note ON, channel 1

Serial.write (noteNumber);

Serial.write (0x7f); //medium velocity

}

}

}

//Turn note off. Debouncing is achieved by requiring that several turnNoteOFF requests

//are received before sending out noteOff MIDI message

void turnNoteOFF ()

{

if (debounceArray[noteNumber] > 0)

{

debounceArray [noteNumber] = debounceArray [noteNumber] - 1;

if ((debounceArray[noteNumber] == 0) && (onOffArray[noteNumber] == HIGH))

{

onOffArray[noteNumber] = LOW;

Serial.write (0x90); //note ON, channel 1

Serial.write (noteNumber);

Serial.write (0); //zero velocity = turn OFF note

}

}

}

//Process data received for serial port 0 (Great): status, note number, velocity

void processSerial ()

{

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, port 0

noteNumberRx = Serial.read ();

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

noteVelocityRx = Serial.read ();

if (noteStatusRx == 0x80) { noteVelocityRx = 0;}

Serial.write (0x91); //output on channel 2, port 0

Serial.write (noteNumberRx);

Serial.write (noteVelocityRx);

}

else //not 0x90 or 0x80

{

statusBuffer = 0;

}

}

else //is data byte

{

if (statusBuffer != 0)

{

noteNumberRx = noteStatusRx;

noteStatusRx = statusBuffer;

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

noteVelocityRx = Serial.read ();

if (noteStatusRx == 0x80) { noteVelocityRx = 0;}

Serial.write (0x91); //output on channel 2, port 0

Serial.write (noteNumberRx);

Serial.write (noteVelocityRx);

}

}

}

//Process data received for serial port 1 (Swell): status, note number, velocity

void processSerial1 ()

{

noteStatusRx = Serial1.read ();

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

{

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

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

{

statusBuffer1 = noteStatusRx;

while (!Serial1.available()) {} //wait for serial data, port 1

noteNumberRx = Serial1.read ();

while (!Serial1.available()) {} //wait for serial data, port 1

noteVelocityRx = Serial1.read ();

if (noteStatusRx == 0x80) { noteVelocityRx = 0;}

Serial.write (0x92); //output on channel 3, port 0

Serial.write (noteNumberRx);

Serial.write (noteVelocityRx);

}

else //not 0x90 or 0x80

{

statusBuffer1 = 0;

}

}

else //is data byte

{

if (statusBuffer1 != 0)

{

noteNumberRx = noteStatusRx;

noteStatusRx = statusBuffer1;

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

noteVelocityRx = Serial1.read ();

if (noteStatusRx == 0x80) { noteVelocityRx = 0;}

Serial.write (0x92); //output on channel 3, port 0

Serial.write (noteNumberRx);

Serial.write (noteVelocityRx);

}

}

}

//Process data received for serial port 2 (Positive): status, note number, velocity

void processSerial2 ()

{

noteStatusRx = Serial2.read ();

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

{

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

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

{

statusBuffer2 = noteStatusRx;

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

noteNumberRx = Serial2.read ();

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

noteVelocityRx = Serial2.read ();

if (noteStatusRx == 0x80) { noteVelocityRx = 0;}

Serial.write (0x93); //output on channel 4, port 0

Serial.write (noteNumberRx);

Serial.write (noteVelocityRx);

}

else //not 90 or 80

{

statusBuffer2 = 0;

}

}

else //is data byte

{

if (statusBuffer2 != 0)

{

noteNumberRx = noteStatusRx;

noteStatusRx = statusBuffer2;

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

noteVelocityRx = Serial2.read ();

if (noteStatusRx == 0x80) { noteVelocityRx = 0;}

Serial.write (0x93); //output on channel 4, port 0

Serial.write (noteNumberRx);

Serial.write (noteVelocityRx);

}

}

}

// trace procedure

void trace (byte info)

{

Serial.write (0xF3);

Serial.write (info);

}