diff --git a/RP2040_Tracker.ino b/RP2040_Tracker.ino index ac1a189..94394d9 100644 --- a/RP2040_Tracker.ino +++ b/RP2040_Tracker.ino @@ -4,6 +4,7 @@ #include #include #include +#include // --- HARDWARE CONFIGURATION --- #define SCREEN_WIDTH 128 @@ -36,6 +37,11 @@ struct Step { }; Step sequence[NUM_STEPS]; +Step nextSequence[NUM_STEPS]; +volatile bool nextSequenceReady = false; +volatile bool needsPanic = false; + +mutex_t midiMutex; // --- STATE --- Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); @@ -59,20 +65,20 @@ const char* setupMenu[] = { "Back", "Channel", "Save", "Load" }; const int setupMenuCount = sizeof(setupMenu) / sizeof(char*); int menuSelection = 0; -int navigationSelection = 1; -int playbackStep = 0; -int midiChannel = 1; +volatile int navigationSelection = 1; +volatile int playbackStep = 0; +volatile int midiChannel = 1; int scaleNotes[12]; int numScaleNotes = 0; int melodySeed = 0; -int queuedTheme = -1; +volatile int queuedTheme = -1; const uint32_t EEPROM_MAGIC = 0x42424244; -bool isEditing = false; -int scrollOffset = 0; -bool isPlaying = false; +volatile bool isEditing = false; +volatile int scrollOffset = 0; +volatile bool isPlaying = false; unsigned long lastStepTime = 0; -int tempo = 120; // BPM +volatile int tempo = 120; // BPM // Encoder State @@ -140,9 +146,11 @@ void saveSequence() { EEPROM.put(addr, scaleNotes[i]); addr += sizeof(int); } + mutex_enter_blocking(&midiMutex); for (int i=0; i 127) newNote = 127; + mutex_enter_blocking(&midiMutex); sequence[stepIndex].note = newNote; + mutex_exit(&midiMutex); } else { // Move Cursor navigationSelection += (delta > 0 ? 1 : -1); @@ -349,6 +369,8 @@ void handleInput() { if (menuSelection >= 3) { // Themes if (isPlaying) { queuedTheme = menuSelection - 2; + generateSequenceData(queuedTheme, nextSequence); + nextSequenceReady = true; } else { generateTheme(menuSelection - 2); } @@ -386,7 +408,7 @@ void handleInput() { lastStepTime = millis(); // Reset timer to start immediately } else { // Send All Notes Off on stop (CC 123) - sendMidi(0xB0, 123, 0); + needsPanic = true; queuedTheme = -1; } } @@ -402,6 +424,8 @@ void handlePlayback() { if (millis() - lastStepTime > interval) { lastStepTime = millis(); + mutex_enter_blocking(&midiMutex); + // Determine if we are tying to the next note int nextStep = playbackStep + 1; if (nextStep >= NUM_STEPS) nextStep = 0; @@ -417,8 +441,10 @@ void handlePlayback() { playbackStep++; if (playbackStep >= NUM_STEPS) { playbackStep = 0; - if (queuedTheme != -1) { - generateTheme(queuedTheme); + if (nextSequenceReady) { + sendMidi(0xB0, 123, 0); // Panic / All Notes Off + memcpy(sequence, nextSequence, sizeof(sequence)); + nextSequenceReady = false; queuedTheme = -1; } } @@ -433,13 +459,8 @@ void handlePlayback() { if (isTied && prevNote != -1) { sendMidi(0x80, prevNote, 0); } - - // Auto-scroll navigation cursor if not editing - if (!isEditing) { - navigationSelection = playbackStep + 1; // +1 because 0 is menu - if (navigationSelection < scrollOffset) scrollOffset = navigationSelection; - if (navigationSelection >= scrollOffset + 6) scrollOffset = navigationSelection - 5; - } + + mutex_exit(&midiMutex); } } @@ -518,6 +539,7 @@ void drawTracker() { // Steps int y = 10; + mutex_enter_blocking(&midiMutex); for (int i = 0; i < 6; i++) { int itemIndex = i + scrollOffset; if (itemIndex > NUM_STEPS) break; @@ -535,9 +557,22 @@ void drawTracker() { display.print(F(">> MENU")); } else { int stepIndex = itemIndex - 1; + bool isPlayback = isPlaying && (stepIndex == playbackStep); + + if (isPlayback) { + if (itemIndex == navigationSelection) display.setTextColor(SSD1306_WHITE, SSD1306_BLACK); + else display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); + } + // Step Number if (stepIndex < 10) display.print(F("0")); display.print(stepIndex); + + if (isPlayback) { + if (itemIndex == navigationSelection) display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); + else display.setTextColor(SSD1306_WHITE); + } + display.print(F(" | ")); // Note Value @@ -554,6 +589,7 @@ void drawTracker() { y += 9; } + mutex_exit(&midiMutex); } void drawUI() { @@ -613,6 +649,7 @@ uint32_t getNoteColor(int note) { void updateLeds() { pixels.clear(); // Clear buffer + mutex_enter_blocking(&midiMutex); for (int s = 0; s < NUM_STEPS; s++) { int blockX = (s % 4) * 2; int blockY = (s / 4) * 2; @@ -654,13 +691,21 @@ void updateLeds() { pixels.setPixelColor(getPixelIndex(blockX, blockY + 1), colorBL); pixels.setPixelColor(getPixelIndex(blockX + 1, blockY + 1), colorBR); } + mutex_exit(&midiMutex); pixels.show(); } +void loop1() { + if (needsPanic) { + sendMidi(0xB0, 123, 0); + needsPanic = false; + } + handlePlayback(); +} + void loop() { handleInput(); - handlePlayback(); drawUI(); updateLeds(); delay(10); // Small delay to prevent screen tearing/excessive refresh