diff --git a/RP2040_Tracker.ino b/RP2040_Tracker.ino index 94394d9..17d6f2e 100644 --- a/RP2040_Tracker.ino +++ b/RP2040_Tracker.ino @@ -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();