Midi clock and tempo

This commit is contained in:
Dejvino 2026-02-16 21:24:21 +01:00
parent 98fc15cef2
commit 5f7f241e82

View File

@ -52,16 +52,17 @@ enum UIState {
UI_MENU_MAIN,
UI_MENU_RANDOMIZE,
UI_MENU_SETUP,
UI_SETUP_CHANNEL_EDIT
UI_SETUP_CHANNEL_EDIT,
UI_SETUP_TEMPO_EDIT
};
UIState currentState = UI_TRACKER;
UIState currentState = UI_MENU_MAIN;
const char* mainMenu[] = { "Tracker", "Randomize", "Setup" };
const int mainMenuCount = sizeof(mainMenu) / sizeof(char*);
const char* randomizeMenu[] = { "Back", "Scale", "Melody", "Theme 1", "Theme 2", "Theme 3", "Theme 4", "Theme 5", "Theme 6", "Theme 7" };
const int randomizeMenuCount = sizeof(randomizeMenu) / sizeof(char*);
const char* setupMenu[] = { "Back", "Channel", "Save", "Load" };
const char* setupMenu[] = { "Back", "Channel", "Tempo", "Save", "Load" };
const int setupMenuCount = sizeof(setupMenu) / sizeof(char*);
int menuSelection = 0;
@ -77,8 +78,9 @@ const uint32_t EEPROM_MAGIC = 0x42424244;
volatile bool isEditing = false;
volatile int scrollOffset = 0;
volatile bool isPlaying = false;
unsigned long lastStepTime = 0;
volatile int tempo = 120; // BPM
volatile unsigned long lastClockTime = 0;
volatile int clockCount = 0;
// Encoder State
@ -238,6 +240,12 @@ void sendMidi(uint8_t status, uint8_t note, uint8_t velocity) {
Serial1.write(velocity);
}
void sendMidiRealtime(uint8_t status) {
mutex_enter_blocking(&midiMutex);
Serial1.write(status);
mutex_exit(&midiMutex);
}
void generateRandomScale() {
numScaleNotes = random(3, 13); // 3 to 12 notes
for (int i = 0; i < 12; i++) {
@ -325,6 +333,11 @@ void handleInput() {
if (midiChannel < 1) midiChannel = 16;
if (midiChannel > 16) midiChannel = 1;
break;
case UI_SETUP_TEMPO_EDIT:
tempo += delta;
if (tempo < 40) tempo = 40;
if (tempo > 240) tempo = 240;
break;
}
}
@ -379,11 +392,12 @@ void handleInput() {
case UI_MENU_SETUP:
if (menuSelection == 0) { currentState = UI_MENU_MAIN; menuSelection = 2; }
if (menuSelection == 1) currentState = UI_SETUP_CHANNEL_EDIT;
if (menuSelection == 2) {
if (menuSelection == 2) currentState = UI_SETUP_TEMPO_EDIT;
if (menuSelection == 3) {
saveSequence();
currentState = UI_MENU_MAIN;
}
if (menuSelection == 3) {
if (menuSelection == 4) {
if (loadSequence()) showMessage("LOADED!");
currentState = UI_MENU_MAIN;
}
@ -391,6 +405,9 @@ void handleInput() {
case UI_SETUP_CHANNEL_EDIT:
currentState = UI_MENU_SETUP;
break;
case UI_SETUP_TEMPO_EDIT:
currentState = UI_MENU_SETUP;
break;
}
}
}
@ -405,10 +422,13 @@ void handleInput() {
Serial.print(F("Playback: ")); Serial.println(isPlaying ? F("ON") : F("OFF"));
if (isPlaying) {
playbackStep = navigationSelection > 0 ? navigationSelection - 1 : 0;
lastStepTime = millis(); // Reset timer to start immediately
clockCount = 0;
lastClockTime = micros();
sendMidiRealtime(0xFA); // MIDI Start
} else {
// Send All Notes Off on stop (CC 123)
needsPanic = true;
sendMidiRealtime(0xFC); // MIDI Stop
queuedTheme = -1;
}
}
@ -420,10 +440,18 @@ void handleInput() {
void handlePlayback() {
if (!isPlaying) return;
unsigned long interval = 15000 / tempo; // 16th notes (60000 / tempo / 4)
if (millis() - lastStepTime > interval) {
lastStepTime = millis();
unsigned long currentMicros = micros();
unsigned long clockInterval = 2500000 / tempo; // 60s * 1000000us / (tempo * 24ppqn)
if (currentMicros - lastClockTime >= clockInterval) {
lastClockTime += clockInterval;
sendMidiRealtime(0xF8); // MIDI Clock
clockCount++;
if (clockCount < 6) return; // 24 ppqn / 4 = 6 pulses per 16th note
clockCount = 0;
mutex_enter_blocking(&midiMutex);
// Determine if we are tying to the next note
@ -492,6 +520,11 @@ void drawMenu(const char* title, const char* items[], int count, int selection)
display.print(F(": "));
display.print(midiChannel);
}
// Special case for tempo display
if (currentState == UI_MENU_SETUP && i == 2) {
display.print(F(": "));
display.print(tempo);
}
// Special case for queued theme
if (currentState == UI_MENU_RANDOMIZE && i >= 3 && queuedTheme == (i - 2)) {
@ -623,6 +656,17 @@ void drawUI() {
display.setCursor(0, 50);
display.println(F(" (Press to confirm)"));
break;
case UI_SETUP_TEMPO_EDIT:
display.println(F("SET TEMPO"));
display.drawLine(0, 8, 128, 8, SSD1306_WHITE);
display.setCursor(20, 25);
display.setTextSize(2);
display.print(F("BPM: "));
display.print(tempo);
display.setTextSize(1);
display.setCursor(0, 50);
display.println(F(" (Press to confirm)"));
break;
}
display.display();