Code:
#include <MIDI.h>
MIDI_CREATE_INSTANCE(HardwareSerial, Serial3, MIDI);
//*****************************************************
#include "EncoderTool.h"
#define NUM_ENCS 8
using namespace EncoderTool;
constexpr unsigned QH_A = 17; //output pin QH of shift register B - Encoder B
constexpr unsigned QH_B = 16; //output pin QH of shift register A - Encoder A
constexpr unsigned pinLD = 4; //load pin for all shift registers)
constexpr unsigned pinCLK = 5; //clock pin for all shift registers
#define NUM_ENCS 8
EncPlex74165 encoders(NUM_ENCS, pinLD, pinCLK, QH_A, QH_B );
//****************************************************
struct EncData {
uint16_t CurVal; // Could be a Sysex byte, CC or NRPN value
uint16_t OldVal; // Could be a Sysex byte, CC or NRPN value
uint16_t MinVal;
uint16_t MaxVal;
uint16_t ControlNum; // For CC#, NRPN#. Relevant bits are masked in the MidiSend() function
byte MidiChannel;
int CommandType; // 0 = disabled, 1 = CC, 2= CC14bit, 3 = NRPN, 4 = Roland Single and 5 = Roland Two byte SYSEX messages
byte Address4; // Sysex address
byte Address3; //
byte Address2; //
byte Address1; //
};
EncData EncData[8] = {
{0, 0, 1, 127, 7, 1, 1, 0, 0, 0, 0},
{0, 0, 1, 127, 11, 2, 1, 0, 0, 0, 0},
{0, 0, 1, 255, 25, 3, 2, 0, 0, 0, 0},
{0, 0, 1, 511, 270, 4, 3, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 4, 0x00, 0x00, 0x00, 0x00}, // Panel Mode
{0, 0, 0, 4, 0, 1, 4, 0x30, 0x00, 0x10, 0x50}, // Tone 1 Filter Type
{0, 0, 0, 127, 0, 1, 4, 0x03, 0x00, 0x10, 0x51}, // Tone 1 Filter freq
{0, 0, 1, 255, 0, 1, 5, 0x00, 0x00, 0x00, 0x04}, // Patch Number
};
//************************************************************************************
// Roland JV series specific. These two are kind of templates. The first 5 bytes are sent verbatim
// and address the Synth.
// The next four bytes are the Parameter address, MSB - LSB and the following byte contains the 7-bit parameter value
byte JV_7BitSysex[12] = {0xF0, 0x41, 0x10, 0x6A, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7};
// This one has two parameter value bytes, each with a range 0x0 - 0xF allowing param value range 0-255.
byte JV_8BitSysex[13] = {0xF0, 0x41, 0x10, 0x6A, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7};
// The second last byte of both the above contain the Checksum value.
// Address, Value and Checksum are inserted in the MidiSend() function.
uint16_t sum;
uint16_t idx;
uint16_t checkSum;
//************************************************************************************
int Cable = 0;
elapsedMillis stopwatch = 0;
void setup() {
delay(1000);
MIDI.begin(MIDI_CHANNEL_OMNI);
MIDI.turnThruOff();
encoders.begin(CountMode::half);
for (unsigned i = 0; i < NUM_ENCS; i++) {
encoders[i].setLimits(EncData[i].MinVal, EncData[i].MaxVal);
encoders[i].setValue(EncData[i].CurVal);
EncData[i].OldVal = EncData[i].CurVal;
}
}
void loop() {
ReadEncoders();
SendMidi();
}
void sendMidi2() {
for (unsigned i = 0; i < NUM_ENCS; i++) {
if (EncData[i].CurVal != EncData[i].OldVal) {
EncData[i].OldVal = EncData[i].CurVal;
usbMIDI.sendControlChange(EncData[i].ControlNum, EncData[i].CurVal, EncData[i].MidiChannel);
MIDI.sendControlChange(EncData[i].ControlNum, EncData[i].CurVal, EncData[i].MidiChannel);
}
}
}
//********************************************************************
void SendMidi() {
for (int i = 0; i < NUM_ENCS; i++) {
if (EncData[i].CommandType != 0) {
if (EncData[i].CurVal != EncData[i].OldVal) {
EncData[i].OldVal = EncData[i].CurVal;
uint16_t CurrentValue = EncData[i].CurVal;
uint16_t _Control = EncData[i].ControlNum;
uint16_t value14 = (CurrentValue & 0b00000000000000000011111111111111);
uint16_t value21 = (CurrentValue & 0b00000000000111111111111111111111);
byte valueLSB = (CurrentValue & 0b00000000000000000000000001111111);
byte valueMSB = (CurrentValue & 0b00000000000000000011111110000000) >> 7;
uint16_t control14 = (_Control & 0b00000000000000000011111111111111);
byte controlLSB = (_Control & 0b00000000000000000000000001111111);
byte controlMSB = (_Control & 0b00000000000000000011111110000000) >> 7;
byte Channel = EncData[i].MidiChannel;
switch (EncData[i].CommandType) {
case 0:
// Do nothing.
break;
case 1:
usbMIDI.sendControlChange (controlLSB, valueLSB, Channel, Cable);
MIDI.sendControlChange (controlLSB, valueLSB, Channel);
break;
case 2:
usbMIDI.sendControlChange (controlLSB, valueMSB, Channel, Cable);
usbMIDI.sendControlChange (controlLSB + 32, valueLSB, Channel, Cable);
MIDI.sendControlChange (controlLSB, valueMSB, Channel);
MIDI.sendControlChange (controlLSB + 32, valueLSB, Channel);
break;
case 3:
usbMIDI.beginNrpn(control14, Channel, Cable);
usbMIDI.sendNrpnValue(valueLSB, Channel, Cable);
usbMIDI.endRpn(Channel, Cable);
MIDI.beginNrpn(control14, Channel);
MIDI.sendNrpnValue(valueLSB, Channel);
MIDI.endNrpn(Channel);
break;
case 4:
JV_7BitSysex[9] = valueLSB & 0b01111111;
JV_7BitSysex[8] = EncData[i].Address1 & 0b01111111;
JV_7BitSysex[7] = EncData[i].Address2 & 0b01111111;
JV_7BitSysex[6] = EncData[i].Address3 & 0b01111111;
JV_7BitSysex[5] = EncData[i].Address4 & 0b01111111;
sum = 0;
idx = 5;
checkSum = 0;
for (sum = 0; idx < 10; ++idx) {
sum += (byte)JV_7BitSysex[idx];
}
checkSum = 128 - ((sum % 128) & 0b01111111);
checkSum = (checkSum & 0b01111111);
JV_7BitSysex[10] = checkSum;
usbMIDI.sendSysEx(12, JV_7BitSysex, true, Cable);
MIDI.sendSysEx(12, JV_7BitSysex, true);
break;
case 5:
JV_8BitSysex[10] = EncData[i].CurVal & 0b00000000000000000000000000001111;
JV_8BitSysex[9] = (EncData[i].CurVal & 0b00000000000000000000000011110000) >> 4;
JV_8BitSysex[8] = EncData[i].Address1 & 0b01111111;
JV_8BitSysex[7] = EncData[i].Address2 & 0b01111111;
JV_8BitSysex[6] = EncData[i].Address3 & 0b01111111;
JV_8BitSysex[5] = EncData[i].Address4 & 0b01111111;
sum = 0;
idx = 5;
checkSum = 0;
for (sum = 0; idx < 11; ++idx) {
sum += (byte)JV_8BitSysex[idx];
}
checkSum = 128 - ((sum % 128) & 0b01111111);
checkSum = (checkSum & 0b01111111);
JV_8BitSysex[11] = (byte)checkSum;
usbMIDI.sendSysEx(13, JV_8BitSysex, true, Cable);
MIDI.sendSysEx(13, JV_8BitSysex, true);
break;
}
break;
}
}
}
}
//**************************************************************************************
void ReadEncoders() {
encoders.tick();
if (stopwatch > 10) // Look at sending Midi values every 10 ms
{
for (unsigned i = 0; i < NUM_ENCS; i++)
{
EncData[i].CurVal = encoders[i].getValue();
}
stopwatch = 0;
}
}