#include <MIDI.h>
// #include "MIDIUSB.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
const int switchPins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
const int ledPins[] = {23, 25, 27, 29, 31, 33, 35, 37, 39, 41};
const int bankDownPin = 12;
const int bankUpPin = 13;
const unsigned long debounceDelay = 50;
bool showToBank = false;
bool inBankSwitchMode = false;
MIDI_CREATE_DEFAULT_INSTANCE();
int currentBank = 0;
int currentPreset = 0;
// Custom Bank Namen
String customBankNames[10] = {
"Bank 0", "Bank 1", "Bank 2", "Bank 3", "Bank 4", "Bank 5", "Bank 6", "Bank 7 ", "Bank 8", "Bank 9"
};
struct Preset {
bool useCustomPC1;
int programChange1;
bool useCustomPC2;
int programChange2;
bool sendCC;
int ccNumber;
int ccValue;
String customName;
};
int lastSentCCValue = -1;
// QuadCortex-Scene Selection
bool QCSceneSelect = false;
int currentCCValue = 0;
const int maxCCValue = 7;
const int fixedCCNumber = 43;
unsigned long buttonPressTime = 0;
char getQCSceneLetter(int value) {
return 'A' + value; // Konvertiert 0-7 in 'A'-'H'
}
String customQCSceneNames[8] = {
"Standard", "Tune -1", "Tune -2", "Tune -3", "Boosted", "Name 6", "Name 7", "Name 8"
}; // QC-Scenen Namen
Preset presets[100];
void setup() {
MIDI.begin();
// Initialisiere alle Presets mit Standardwerten
for (int i = 0; i < 100; i++) {
presets[i].useCustomPC1 = false;
presets[i].programChange1 = i;
presets[i].useCustomPC2 = false;
presets[i].programChange2 = -1;
presets[i].sendCC = false;
presets[i].ccNumber = 0;
presets[i].ccValue = 0;
presets[i].customName = "";
}
// spezielle Presets
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
for (;;);
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
for (int i = 0; i < 10; i++) {
pinMode(switchPins[i], INPUT_PULLUP);
pinMode(ledPins[i], OUTPUT);
digitalWrite(ledPins[i], LOW);
}
pinMode(bankUpPin, INPUT_PULLUP);
pinMode(bankDownPin, INPUT_PULLUP);
}
void loop() {
handleBankSwitch();
handlePresets();
QuardCortexSceneSelect();
updateDisplay();
}
void handleBankSwitch() {
if (QCSceneSelect) return; // Ignoriere, wenn im QC-Wert-Auswahlmodus
static bool lastBankUpState = HIGH; // Hinzugefügt für einmalige Auslösung
static bool lastBankDownState = HIGH; // Hinzugefügt für einmalige Auslösung
static unsigned long bankUpPressTime = 0;
static unsigned long bankDownPressTime = 0;
unsigned long currentMillis = millis();
// Bank Up Taster
bool currentBankUpState = digitalRead(bankUpPin);
if (currentBankUpState == LOW && lastBankUpState == HIGH) { // Taster gedrückt
bankUpPressTime = currentMillis; // Zeitstempel setzen
} else if (currentBankUpState == HIGH && lastBankUpState == LOW) { // Taster losgelassen
if (currentMillis - bankUpPressTime < 800) { // in 800 ms losgelassen
if (currentBank < 9) {
currentBank++;
currentPreset = currentBank * 10;
showToBank = true;
}
}
}
lastBankUpState = currentBankUpState;
// Bank Down Taster
bool currentBankDownState = digitalRead(bankDownPin);
if (currentBankDownState == LOW && lastBankDownState == HIGH) { // Taster gedrückt
bankDownPressTime = currentMillis; // Zeitstempel setzen
} else if (currentBankDownState == HIGH && lastBankDownState == LOW) { // Taster losgelassen
if (currentMillis - bankDownPressTime < 800) { // in 800 ms losgelassen
if (currentBank > 0) {
currentBank--;
currentPreset = currentBank * 10;
showToBank = true;
}
}
}
lastBankDownState = currentBankDownState;
}
void QuardCortexSceneSelect() {
if (inBankSwitchMode || showToBank) return; // Ignoriere, wenn im Bankwechsel-Modus
static bool lastBankDownState = HIGH;
static unsigned long lastDebounceTime = 0;
bool currentBankDownState = digitalRead(bankDownPin);
unsigned long currentMillis = millis();
// Wechsel in den Auswahlmodus
if (!QCSceneSelect) {
if (currentBankDownState == LOW) {
if (buttonPressTime == 0) {
buttonPressTime = currentMillis;
} else if (currentMillis - buttonPressTime >= 1500) {
QCSceneSelect = true;
updateDisplay();
currentCCValue = 0;
buttonPressTime = 0;
}
} else {
buttonPressTime = 0;
}
}
// Im Auswahlmodus
else {
bool currentBankUpState = digitalRead(bankUpPin);
static bool lastBankUpState = HIGH; // Hinzugefügt für einmalige Auslösung
if (currentBankUpState == LOW && lastBankUpState == HIGH) { // Nur einmal auslösen
currentCCValue = (currentCCValue + 1) % (maxCCValue + 1);
updateDisplay();
}
lastBankUpState = currentBankUpState;
// Bank Down Taster zum Senden der Nachricht und Verlassen des Auswahlmodus
if (currentBankDownState == LOW && lastBankDownState == HIGH && (currentMillis - lastDebounceTime) > debounceDelay) {
MIDI.sendControlChange(fixedCCNumber, currentCCValue, 1);
lastSentCCValue = fixedCCNumber;
QCSceneSelect = false;
lastDebounceTime = currentMillis;
updateDisplay();
}
}
lastBankDownState = currentBankDownState;
}
void handlePresets() {
if (QCSceneSelect) return;
static unsigned long lastDebounceTime[10] = {0};
static bool switchState[10] = {HIGH};
unsigned long currentMillis = millis();
for (int i = 0; i < 10; i++) {
int reading = digitalRead(switchPins[i]);
if (reading != switchState[i] && (currentMillis - lastDebounceTime[i]) > debounceDelay) {
lastDebounceTime[i] = currentMillis;
switchState[i] = reading;
if (switchState[i] == LOW) { // Nur beim Drücken des Schalters
currentPreset = currentBank * 10 + i;
sendPresetMessages(currentPreset);
showToBank = false;
// Setze lastSentCCValue zurück, wenn kein CC gesendet wurde
if (!presets[currentPreset].sendCC) {
lastSentCCValue = -1; // Setze auf -1, um "--" anzuzeigen
}
updateLEDs(i);
}
}
}
}
void sendPresetMessages(int presetNumber) {
Preset& preset = presets[presetNumber];
if (preset.useCustomPC1 && preset.programChange1 != -1) {
MIDI.sendProgramChange(preset.programChange1, 1);
} else if (!preset.useCustomPC1) {
MIDI.sendProgramChange(presetNumber, 1);
}
if (preset.useCustomPC2 && preset.programChange2 != -1) {
MIDI.sendProgramChange(preset.programChange2, 2);
}
if (preset.sendCC) {
MIDI.sendControlChange(preset.ccNumber, preset.ccValue, 1);
lastSentCCValue = preset.ccNumber;
}
}
void updateLEDs(int activeSwitch) {
for (int i = 0; i < 10; i++) {
digitalWrite(ledPins[i], (i == activeSwitch) ? HIGH : LOW);
}
}
void updateDisplay() {
static unsigned long lastBlinkTime = 0;
static bool blinkState = false;
unsigned long currentMillis = millis();
// Blinklogik für Bankwechsel und CC-Wert Auswahl
if (currentMillis - lastBlinkTime > 100) {
blinkState = !blinkState;
lastBlinkTime = currentMillis;
}
display.clearDisplay();
display.setCursor(0, 0);
if (showToBank) {
display.setTextSize(2);
if (blinkState) {
display.setTextSize(1);
display.println("change to Presets:");
display.println();
display.setTextSize(2);
display.print(currentBank * 10 < 10 ? "0" + String(currentBank * 10) : String(currentBank * 10));
display.print("-");
display.println((currentBank * 10 + 10 - 1) < 10 ? "0" + String(currentBank * 10 + 10 - 1) : String(currentBank * 10 + 10 - 1));
display.println();
display.println(customBankNames[currentBank]);
}
} else if (QCSceneSelect) {
if (blinkState) {
display.setTextSize(1);
display.println("change QC Scene:");
display.println();
display.setTextSize(2);
display.print("Scene ");
display.println(getQCSceneLetter(currentCCValue));
display.setTextSize(2);
display.println();
display.println(customQCSceneNames[currentCCValue]);
}
} else {
display.setTextSize(1);
display.print("PC1:");
display.print(presets[currentPreset].useCustomPC1 ?
(presets[currentPreset].programChange1 < 10 ? "0" + String(presets[currentPreset].programChange1) : String(presets[currentPreset].programChange1)) :
(currentPreset < 10 ? "0" + String(currentPreset) : String(currentPreset)));
display.print(" PC2:");
display.print(presets[currentPreset].useCustomPC2 ?
(presets[currentPreset].programChange2 < 10 ? "0" + String(presets[currentPreset].programChange2) : String(presets[currentPreset].programChange2)) :
"--");
display.print(" CC:");
display.println(lastSentCCValue != -1 ?
(lastSentCCValue < 10 ? "0" + String(lastSentCCValue) : String(lastSentCCValue)) :
"--");
display.println();
display.setTextSize(2);
display.print("Preset: ");
display.println(currentPreset < 10 ? "0" + String(currentPreset) : currentPreset);
display.println();
display.println(presets[currentPreset].customName);
}
display.display();
}