NickDomingoMusic
Ok, so now I had some time to look over your script and I have a few suggestions which you could implement in your script.
For connecting and working with the MIDI socket, I suggest reading this blog: https://www.instructables.com/Send-and-Receive-MIDI-with-Arduino/. Basically, you connect TX and RX to the pins and use "Serial Communication" to read and write MIDI messages.
My suggested changes to your script:
To check the buttons, i have some suggestions:
- don't use delay() because it freezes the arduino, instead check every press, if enough time has already passed
- each time the state of the button has changed, send a MIDI message
unsigned long buttons_time[NUM_BTNS] = {}; //stores the time a button has been pressed
unsigned long wait_time = 20; // your time to wait after each press, for debouncing.
// you propably could wait it even longer (if you experience problems, increase it)
void checkButtons() {
//instead of delaying after each press, we check if enough time has passed between presses
unsigned long time = millis(); //stores the current time in milliseconds
for (int i = 0; i < NUM_BTNS; i++) {
if ((time-buttons_time[i]) > wait_time){ // debouncing
btnState[i] = digitalRead(BTN_PINS[i]); // check each button
if (btnState[i] != oldBtnState[i]) { // if it changed
oldBtnState[i] = btnState[i]; // remember state for next time
CreateSendCC(i, btnState[i]); // and send midi message (control change)
buttons_time[i] = time; // store time when it was pressed
}
}
}
}
At each change of button-state, we create and send a MIDI message. You need to send on and off if you need long/short presses in Mobius.
- We send control change messages, so 176 < byte1 <= 180
- byte2 is just a controller value, byte2 >= 20
- velocity: 0 for off, 127 for on.
The code to send it would be:
void CreateSendCC(int button, int state){
//function takes button number and state to create a MIDI message
byte velocity = state*127;
if (button<4){
byte track = button;
byte byte2 = 20;
} else {
byte track = 0;
byte byte2 = 20+button;
}
Serial.write(177+track);
Serial.write(20+byte2);
Serial.write(velocity);
// you could also just use Serial.write(177); Serial.write(20+button); Serial.write(velocity),
// but this way, the feedback for the red led is the same as the messages from buttons 1-4;
// This makes it easier to use for other applications, if you ever need it.
}
In setup, you need to start serial communication.
void setup() {
// In your setup, you need to start serial communication
Serial.begin(31250);
....
}
Every iteration, you check if there are 3 bytes or more available. (MIDI messages contain 3 bytes, first is status, second controller, third velocity. For our script, we use the first as the track number, second as the action.
Some details:
- You read the first byte, only is between 177 to 180 (so track 1-4), you read the rest of the message.
- Byte 2 tells you what just has happened in Mobius (20 = Record, 21 = Mute, ...).
- Byte 3 tells you to turn on/off the led.
- It just is the backbone, you need to fill the rest of your code into it.
void loop() {
// first check midi messages, then buttons
if (Serial.available() >= 3){
// if at least 3 bytes are available, read them
// messages in Mobius: MidiOut("control" track byte2 byte3)
byte byte1 = Serial.read(); //byte1 = track + 176
byte track = byte1 - 177; //we use 0-based tracks instead, so track 1 in mobius track 0 here
if (track >= 0 && track <= 3){ //track 0,1,2,3
byte byte2 = Serial.read(); // use byte2 to encode record/mute/..., in range 20-31
byte byte3 = Serial.read(); // byte3 in range 0-127, if 0, turn off led, else turn on
//20 = record
//21 = mute/play
//22 = ...
//30 = reset, all off
if (byte2 ==20){
//record: turn red led nr. <track> on or off
if (byte3 == 127){
digitalWrite(RED_LED_PINS[track], HIGH);
} else if (byte3 == 0){
//led off
digitalWrite(RED_LED_PINS[track], LOW);
}
} else if (byte2 == 21){
//playback
if (byte3 == 127){
//play
digitalWrite(GRN_LED_PINS[track], HIGH);
} else if (byte3 == 0){
//mute (off)
digitalWrite(GRN_LED_PINS[track], LOW);
}
} else if(byte3 == ...){
//insert everything else you need here, e.g. mode button, clear
...
} else if (byte2 == 30){
// you can use 30 as a (global) reset message, it should turn everything off
// here, the track does not matter, just use between 1-4
// if you need individual reset messages, delete the loop and replace i with track,
// and make a seperate global reset message e.g. byte2 = 31
// Mobius: MidiOut("control" track 30 127)
if (byte3 == 127){
for (int i=0; i< NUM_LEDS; i++){
digitalWrite(RED_LED_PINS[i], LOW);
digitalWrite(GRN_LED_PINS[i], LOW);
//turn neopixel off
}
}
}
}
}
//check buttons
checkButtons();
}
An Event Script which handles Record, Play and Mute would look like this:
//print("Event" eventType eventTrack eventMode)
//Record
if eventMode == "record"{
if eventType == 'ModeStart' {
//record on
MidiOut("control" eventTrack 20 127)
} else if eventType == 'ModeEnd'{
//record off
MidiOut("control" eventTrack 20 0)
}
// Play/Mute
} else if eventMode == 'play' {
if eventType == 'ModeStart' {
//play on
MidiOut("control" eventTrack 21 127)
} else if eventType == 'ModeEnd'{
//play off (get's turned off if mute starts)
MidiOut("control" eventTrack 21 0)
}
// Reset
} else if eventMode == "reset" and eventType == 'ModeStart' {
MidiOut("control" eventTrack 30 127)
}
Unfortunately, I did not really test this script as I don't have too much time, but I hope it helps you. I something does not work, I will try to help you with it.