//************LIBRARIES USED**************
// 'include the Bounce library for 'de-bouncing' switches -- removing electrical chatter as contacts settle'
#include <Bounce.h>
//'usbMIDI.h library is added automatically when code is compiled as a MIDI device'
// ******CONSTANT VALUES********
//********** PIN DEFINITIONS
const int D_PINS = 5; // number of Digital PINS
const int DIGITAL_PINS[D_PINS] = {0,1,2,3,4};
const int RED_LED_PINS[D_PINS] = {6,19,8,10,17};
const int GREEN_LED_PINS[D_PINS] = {7,20,9,16,18};
const int MODE_TAP_PIN = 5;
const int MODE_TAP_LED_PIN = 11;
//********** MIDI DEFINITIONS
const int channel = 1; // MIDI channel
const int MODE_COUNT = 3; // number of rows of banks
//CC configuration matrix!!
const int MIDI_CC_NUMS[MODE_COUNT][D_PINS] = { //rows are banks up to MODE_COUNT
{60,61,62,63,64},
{65,66,67,68,69},
{70,71,72,73,74}
};
const int TAP_CC = 15;
const int ON_Value = 127; // note-one velocity sent from buttons (should be 65 to 127)
//********** PHYSICAL DEFINITIONS
const int BOUNCE_TIME = 30; // 5 ms is usually sufficient
const int modeThreshold = 600; // how long to hold before mode changes
const int flashOnTime = 150; // how long flashed LED is on
const int flashOffTime = 250; // how long flashed LED is off
const bool LED_ON = LOW; // LOW for active LOW wiring
const bool LED_OFF = HIGH; // HIGH for active LOW wiring
//******VARIABLES***********
// a data array to remember the current state of each switch
boolean state[MODE_COUNT][D_PINS];
elapsedMillis modeTimer,flashTimer;
boolean modeSelectActive = false;
int bank = 0 ;
int flashcount;
int shiftUp; // keeps track of whether the mode change needs to be handled (true) or was (false)
int modeLED; // keeps track of whether LED is on without testing it..
int ClockCount; // for tempo tracking
boolean bankSelect = false;
//************INITIALIZE LIBRARY OBJECTS**************
// initialize the bounce objects
Bounce digital[] = {
Bounce(DIGITAL_PINS[0],BOUNCE_TIME),
Bounce(DIGITAL_PINS[1], BOUNCE_TIME),
Bounce(DIGITAL_PINS[2], BOUNCE_TIME),
Bounce(DIGITAL_PINS[3], BOUNCE_TIME),
Bounce(DIGITAL_PINS[4], BOUNCE_TIME)
};
Bounce modeTap = Bounce(MODE_TAP_PIN, BOUNCE_TIME);
//************SETUP**************
void setup() {
//'set a handle for returning MIDI messages'
usbMIDI.setHandleControlChange(OnControlChange);
usbMIDI.setHandleSongPosition(onSongPosition);
usbMIDI.setHandleClock(onClock);
usbMIDI.setHandleStart(onStart);
//'loop to configure input pins and internal pullup resisters for digital section'
for (int i=0;i<D_PINS;i++){
pinMode(DIGITAL_PINS[i], INPUT_PULLUP);
pinMode(RED_LED_PINS[i], OUTPUT);
pinMode(GREEN_LED_PINS[i], OUTPUT);
}
pinMode(MODE_TAP_PIN, INPUT_PULLUP);
pinMode(MODE_TAP_LED_PIN, OUTPUT);
digitalWrite(MODE_TAP_LED_PIN, LED_OFF);
for (int i=0;i<D_PINS;i++){
digitalWrite(GREEN_LED_PINS[i], LED_OFF); // - GREEN OFF
digitalWrite(RED_LED_PINS[i], LED_ON); // - RED ON
}
}
//************LOOP**************
void loop() {
getDigitalData();
getModeTap();
while (usbMIDI.read()) {
//' controllers must call .read() to keep the queue clear even if they are not responding to MIDI'
}
}
//************DIGITAL SECTION**************
void getDigitalData(){
for (int i=0;i<D_PINS;i++){
digital[i].update();
if (digital[i].fallingEdge() || digital[i].risingEdge()) {
if (state[bank][i]) {
usbMIDI.sendControlChange(MIDI_CC_NUMS[bank][i], 0, channel);
digitalWrite(RED_LED_PINS[i], LED_ON);
digitalWrite(GREEN_LED_PINS[i], LED_OFF);
}else{
usbMIDI.sendControlChange(MIDI_CC_NUMS[bank][i], ON_Value, channel);
digitalWrite(RED_LED_PINS[i], LED_OFF);
digitalWrite(GREEN_LED_PINS[i], LED_ON);
}
state[bank][i] = !state[bank][i] ;
}
}
}
//************MODE/TAP SECTION**************
void getModeTap(){
modeTap.update();
if (modeTap.fallingEdge()) {
usbMIDI.sendControlChange(TAP_CC, ON_Value, channel); // can we send any D2 for Tap?
modeTimer = 0;
shiftUp = true;
}
if (modeTap.risingEdge()){
shiftUp = false;
}
if (modeTimer>modeThreshold && shiftUp) {
shiftUp = false;
bankSelect = true;
bank++;
bank = bank%MODE_COUNT;
for (int i = 0; i < D_PINS ; i++){
digitalWrite(GREEN_LED_PINS[i], !state[bank][i]);
digitalWrite(RED_LED_PINS[i], state[bank][i]);
}
flashcount = bank + 1;
flashTimer = 0 ;
// set counter of flashes 'owed' -- count them down after main part
}
// if flashcount > 0 flash and start counter
if (flashcount){
if (flashTimer>(flashOnTime+flashOffTime)){
flashcount-- ;// decrement flashcount
flashTimer = 0;
if (modeLED){
modeLED = false;
digitalWrite(MODE_TAP_LED_PIN, LED_OFF);
}
}else{
if (modeLED == false && flashTimer>flashOnTime){
modeLED = true;
digitalWrite(MODE_TAP_LED_PIN, LED_ON);
}
}
}else{
//bankSelect = false;
}
}
void OnControlChange(byte rcvChannel, byte controller, byte value) {
if (rcvChannel == channel){
for (int i = 0; i < D_PINS ; i++){
if (MIDI_CC_NUMS[bank][i] == controller) {
if (value >= 64) {
digitalWrite(GREEN_LED_PINS[i], LED_ON);
digitalWrite(RED_LED_PINS[i], LED_OFF); //'receiving >64 turns green on and red off'
state[bank][i] = true;
}else{
digitalWrite(GREEN_LED_PINS[i], LED_OFF);
digitalWrite(RED_LED_PINS[i], LED_ON); //'receiving <64 turns red on and green off'
state[bank][i] = false;
} // 'if not the controller for i then skip this loop'
}
}
}
}
void onClock() {
if (!bankSelect){
if (ClockCount<=3){
digitalWrite(MODE_TAP_LED_PIN, LED_ON);
}else{
digitalWrite(MODE_TAP_LED_PIN, LED_OFF);
}
}
++ClockCount;
ClockCount%= 24;
}
void onStart(){
ClockCount = 0;
}
void onSongPosition(uint16_t semiQ){
ClockCount= semiQ*6 ;
}