andreagregorini
Published © CC BY-NC

MIDI Merger and Patchbay

A MIDI merger and patchbay with MIDI expression capabilities for the ARKeytar Arduino MIDI controller or other devices, based on Mega 2560.

IntermediateShowcase (no instructions)1,795
MIDI Merger and Patchbay

Things used in this project

Hardware components

Toggle Switch, SPDT
Toggle Switch, SPDT
×6
3PDT STOMP FOOT / PEDAL SWITCH
TaydaElectronics 3PDT STOMP FOOT / PEDAL SWITCH
×3
Rotary Encoder with Push-Button
Rotary Encoder with Push-Button
×1
MIDI 5 pins connector DIN 41524
×4
Stereo jack socket
×2
Metal Enclosure, Electrical / Industrial
Metal Enclosure, Electrical / Industrial
×1
Arduino Mega 2560
Arduino Mega 2560
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free

Story

Read more

Schematics

MIDI ports on serial2

MIDI ports on serial3

Pedals

This scheme represents the wiring and setting of the interchangable digital/analog port. A switch is used to change between these two modes.

Shift registers

A single switch position is represented as an example for the connection that is repeated for all the 8 central pins of each shift register. The central pin of a switch is connected to 5V, the two remaining pins are connected to the shift registers as indicated in the example.

Code

MIDI merger v1.11.1

Arduino
/*
MIDI Merger v1.11.1
2022-10-16
*/

/* MIDI and ShiftIn libraries */
#include <MIDI.h>
#include <ShiftIn.h>
ShiftIn<2> shift;       // Two shift registers


/* A MIDI instance is created for each of the two serial ports used */
MIDI_CREATE_INSTANCE(HardwareSerial, Serial2, M1);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial3, M2);



/* Shift registers variables */
int num;
int incoming;
int newRegRead;

/* Switches state. The variable contains: switch number, first and second pin state */
int s1[3] = {0,0,0};  int s1Old[3] = {0,0,0};
int s2[3] = {1,0,0};  int s2Old[3] = {1,0,0};
int s3[2] = {2,0};    int s3Old[2] = {2,0};

int s4[3] = {3,0,0};  int s4Old[3] = {3,0,0};
int s5[3] = {4,0,0};  int s5Old[3] = {4,0,0};
int s6[2] = {5,0};    int s6Old[2] = {5,0};

/* Encoder and stomps state */
int e[4] = {6,0,0,0}; int eOld[4] = {6,0,0,0};
int f[4] = {7,0,0,0}; int fOld[4] = {7,0,0,0};

/* Leds */
const int ledR = 43;
const int ledG = 45;
const int ledB = 41;
const int ledPow = 39;
int ledList[4] = {ledB, ledR, ledG, ledPow};

/* Pedals variables */
const int p[2] = {3,2};
const int p2A = A4;
int susVal[2];
int susValOld[2];
int ccVal[2];
int sPedal[5] = {0,0,0,0,0};

int expVal[2];
int expValOld[2];
int expcc[2];
int expccOld[2];

int button = 0; int buttonOld = 0;

/* Settings list */
/* 	0. MIDI channel pedal 1, Digital			- Default: ch8
		1. MIDI channel pedal 2, Digital		  - Default: ch8
		2. MIDI channel pedal 2, Analog			  - Default: ch8
		3. CC pedal 1, Digital				 	      - Default: sustain
		4. CC pedal 2, Digital					      - Default: sustain
		5. CC pedal 2, Analog					        - Default: volume
*/
int setting[6] = {8, 8, 8, 1, 1, 2};
/* The settings are:  0.midi channel for pedal 1; 
                      1. midi channel for pedal 2, digital
                      2. midi channel for pedal 2, analog
                      3. index for listcc, cc destination of pedal 1
                      4. index for listcc, cc destination of pedal 2, digital
                      5. index for listcc, cc destination of pedal 2, analog*/
/* Control change number to be selected for expression pedal */
const int listCC[12] = {64, 7, 74, 71, 11, 1, 5, 73, 91, 93, 72, 10};




void setup() {
	/* turn on the "power on led" */
	pinMode(ledPow, OUTPUT);
	digitalWrite(ledPow, HIGH);
	
	M1.begin(MIDI_CHANNEL_OMNI);
	M2.begin(MIDI_CHANNEL_OMNI);
	Serial.begin(9600);

	/* Shift registers setup */
	shift.begin(4, 5, 6, 7);
	if(shift.update())
	incoming = readShiftReg();
	saveShiftReg();
	e[1] = 0;

	/* LEDs pins */
	pinMode(ledR, OUTPUT);
	pinMode(ledG, OUTPUT);
	pinMode(ledB, OUTPUT);
	

	/* pedals */
	pinMode(p2A, INPUT_PULLUP);
	pinMode(p[0], INPUT_PULLUP);

	/* Defines digital/analog pedal behaviour */
	/* Digital */
	if (s3[1] == 0) {
		pinMode(p[1], INPUT_PULLUP);
	}
	/* Analog */
	if (s3[1] == 1) {
		pinMode(p[1], OUTPUT);
		digitalWrite(p[1], HIGH);
	}
	
	/* Blink the leds after setup is done */
	for (int ii=0; ii<=2; ii++) {
		digitalWrite(ledList[ii],HIGH);
		delay(250);
		digitalWrite(ledList[ii],LOW);
	}
}




void loop() {

	shiftReg();
	
	merger();
	merger();
	merger();
	
	pedals();
	
	merger();
	merger();
	merger();

}





void shiftReg() {

	if(shift.update()) {newRegRead = 1;} else {newRegRead = 0;} 
	incoming = readShiftReg();
  
	if(newRegRead){
		saveShiftReg();
	}
}

int readShiftReg() {

	for(int i = 0; i < shift.getDataWidth(); i++){
		bitWrite(num, i, shift.state(i));
	}

	int incomingNumber = 0;
	for (int bitNumber = 8; bitNumber <= 15; bitNumber++) {
		bitWrite(incoming, incomingNumber, bitRead(num,bitNumber));
		incomingNumber++;
	}
	for (int bitNumber = 0; bitNumber <= 7; bitNumber++) {
		bitWrite(incoming, incomingNumber, bitRead(num,bitNumber));
		incomingNumber++;
	}
	merger();
	return incoming;
}



void saveShiftReg() {
	/* Assign shift register values to variables */
	e[1] = bitRead(incoming,0);
	e[2] = bitRead(incoming,1);
	e[3] = bitRead(incoming,2);
	f[1] = bitRead(incoming,3);
	f[3] = bitRead(incoming,4);
	f[2] = bitRead(incoming,5);
	s3[1] = bitRead(incoming,6);
	s6[1] = bitRead(incoming,7);
	
	merger();
	
	s2[2] = bitRead(incoming,8);
	s2[1] = bitRead(incoming,9);
	s1[2] = bitRead(incoming,10);
	s1[1] = bitRead(incoming,11);
	s4[2] = bitRead(incoming,12);
	s4[1] = bitRead(incoming,13);
	s5[2] = bitRead(incoming,14);
	s5[1] = bitRead(incoming,15);
	
  sPedal[1] = s4[2];
  sPedal[2] = s4[1];
  sPedal[3] = s5[2];
  sPedal[4] = s5[1];
	
	
	if (s1[1] == 1) {
		M1.turnThruOn();
	}
	if (s1[2] == 1) {
		M1.turnThruOff();
	}
	if (s1[1] == 0 && s1[2] == 0) {
		M1.turnThruOn();
	}
	if (s2[2] == 1) {
		M2.turnThruOn();
  }
	if (s2[1] == 1) {
		M2.turnThruOff();
	}
	if (s2[1] == 0 && s2[2] == 0) {
		M2.turnThruOff();
	}
	merger();
}



void merger() {
	/* Manages the MIDI routing */
	if (M1.read()) {
		if (s1[2] == 1) {
			M2.send(M1.getType(), M1.getData1(), M1.getData2(), M1.getChannel());
		}
		if (s1[1] == 0 && s1[2] == 0) {
			M2.send(M1.getType(), M1.getData1(), M1.getData2(), M1.getChannel());
		} 
	}

	if (M2.read()) {
		if (s2[1] == 1) {
			M1.send(M2.getType(), M2.getData1(), M2.getData2(), M2.getChannel());
		} 
	}	
	switchLed();
}



void switchLed() {
  /* LED color based on the MIDI message type */
	switch(M1.getType()) {
		case midi::NoteOn:
			ledOn(ledG);
			break;
		case midi::NoteOff:
			ledOn(ledR);
			break;
		case midi::PitchBend:
			ledOn(ledB);
			break;
		case midi::ControlChange:
			ledOn(ledR);
			break;
	}
	switch(M2.getType()) {
		case midi::NoteOn:
			ledOn(ledG);
			break;
		case midi::NoteOff:
			ledOn(ledR);
			break;
		case midi::PitchBend:
			ledOn(ledB);
			break;
		case midi::ControlChange:
			ledOn(ledR);
			break;
  }
	
}



void ledOn(int ledPin) {
	/* Turns on the led having pin number equal to the argument of the function.
	Otherwise, it turns the LED off */
	for (int ii = ledList[0]; ii <= ledList[2]+2; ii = ii+2) {
		if (ledPin == ii) {
			digitalWrite(ledPin, HIGH);
		} else {
			digitalWrite(ledPin, LOW);
		}
	}     
}



void pedals() {
	/* Manages the pedals */

	if (s3[1] != s3Old[1]) {
		if (s3[1] == 1) {
			pinMode(p[1], INPUT_PULLUP);
		} else if (s3[1] == 0) {
			pinMode(p[1], OUTPUT);
			digitalWrite(p[1], HIGH);
		}
		s3Old[1] = s3[1];
	}

	for (int ii = 0; ii <= 1; ii++) {
		
		/* Always proceed with pedal 1 */
		/* Proceed with pedal two if it is set on digital/switch */
		if (ii == 0 || ((s3[1] == 1) && (ii == 1))) {
			susVal[ii] = digitalRead(p[ii]);
			if (susVal[ii] != susValOld[ii]) {
				if (susVal[ii] == 1) {
					ccVal[ii] = 127;
				}
				if (susVal[ii] == 0) {
					ccVal[ii] = 0;
				}
				
				if (sPedal[ii*2+1] == 1) {
					M1.sendControlChange(listCC[setting[ii+3]],ccVal[ii],setting[ii]);
				}
				if (sPedal[ii*2+2] == 1) {
					M2.sendControlChange(listCC[setting[ii+3]],ccVal[ii],setting[ii]);
				}
				if (sPedal[ii*2+1] == 0 && sPedal[ii*2+2] == 0) {
					M1.sendControlChange(listCC[setting[ii+3]],ccVal[ii],setting[ii]);
					M2.sendControlChange(listCC[setting[ii+3]],ccVal[ii],setting[ii]);
				}

				susValOld[ii] = susVal[ii];
				merger();
			}
		}
		
		if (ii == 1 && s3[1] == 0) {
			expVal[1] = analogRead(p2A);
			expcc[1] = map(expVal[1],30,1023,0,127);
			
			if (expcc[1] != expccOld[1] && abs(expccOld[1]-expcc[1])>= 5) {
				if (s5[1] == 1) {
					M1.sendControlChange(listCC[setting[2+3]],expcc[1],setting[2]);
				}
				if (s5[2] == 1) {
					M2.sendControlChange(listCC[setting[2+3]],expcc[1],setting[2]);
				}
				if (s5[1] == 0 && s5[2] == 0) {
					M1.sendControlChange(listCC[setting[2+3]],expcc[1],setting[2]);
					M2.sendControlChange(listCC[setting[2+3]],expcc[1],setting[2]);
				}
				

				expccOld[1] = expcc[1];
			}
		}
		merger();
		
	}

}

Credits

andreagregorini
2 projects • 3 followers
Mechanical engineer based in Milan. Free time musician and keyboard player for The Cinema Show prog band. Photography enthusiast.

Comments