From 71583bdb4e5373e8fe1e4cdd889793d658cf130a Mon Sep 17 00:00:00 2001 From: Dejvino Date: Fri, 6 Mar 2026 21:35:21 +0100 Subject: [PATCH] Refactor: UIManager trimming --- LedMatrix.cpp | 168 ++++++++++++++++++++++++++++++++++++++ LedMatrix.h | 28 +++++++ UIManager.cpp | 222 ++++++-------------------------------------------- UIManager.h | 9 +- 4 files changed, 227 insertions(+), 200 deletions(-) create mode 100644 LedMatrix.cpp create mode 100644 LedMatrix.h diff --git a/LedMatrix.cpp b/LedMatrix.cpp new file mode 100644 index 0000000..d0cc206 --- /dev/null +++ b/LedMatrix.cpp @@ -0,0 +1,168 @@ +#include "LedMatrix.h" +#include + +// --- HARDWARE CONFIGURATION --- +#define PIN_NEOPIXEL 6 +#define NEOPIXELS_X 16 +#define NEOPIXELS_Y 8 +#define NUM_PIXELS 64*2 // 8x8 LEDs in 2 panels + +LedMatrix::LedMatrix() + : pixels(NUM_PIXELS, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800) +{ +} + +void LedMatrix::begin() { + pixels.setPin(PIN_NEOPIXEL); + pixels.begin(); + pixels.setBrightness(40); + pixels.clear(); + pixels.show(); +} + +void LedMatrix::setBrightness(uint8_t b) { + pixels.setBrightness(b); +} + +void LedMatrix::clear() { + pixels.clear(); +} + +void LedMatrix::show() { + pixels.show(); +} + +uint32_t LedMatrix::getNoteColor(int note, bool dim) { + if (note == -1) return 0; + uint16_t hue = 30000 + (note % 12) * 3628; + return Adafruit_NeoPixel::ColorHSV(hue, 255, dim ? 10 : 50); +} + +int LedMatrix::getPixelIndex(int x, int y) { + return NEOPIXELS_Y * x + (NEOPIXELS_Y - 1 - y); +} + +void LedMatrix::update(const Step sequence[][NUM_STEPS], int playbackStep, bool isPlaying, + UIState currentState, bool songModeEnabled, + int songRepeatsRemaining, bool sequenceChangeScheduled, PlayMode playMode, + int selectedTrack, const int* numSteps, const bool* trackMute) { + pixels.clear(); + + const uint32_t COLOR_PLAYHEAD = pixels.Color(0, 255, 0); + const uint32_t COLOR_PLAYHEAD_DIM = pixels.Color(0, 32, 0); + const uint32_t COLOR_MUTED_PLAYHEAD = pixels.Color(0, 0, 255); + const uint32_t COLOR_CURSOR = pixels.Color(255, 100, 100); + const uint32_t COLOR_CURSOR_DIM = pixels.Color(32, 0, 0); + + if(playMode == MODE_POLY) { + for(int t=0; t currentTrackSteps + if (s < currentTrackSteps) { + step_color = COLOR_PLAYHEAD_DIM; // It's a valid step + if (isPlaying && (s == (playbackStep % currentTrackSteps))) { + if (trackMute[t]) { + step_color = COLOR_MUTED_PLAYHEAD; + } else { + step_color = COLOR_PLAYHEAD; + } + } + } + pixels.setPixelColor(getPixelIndex(col, step_row), step_color); + } + } + } else { + // --- Mono Mode (original) --- + const Step* trackSequence = sequence[selectedTrack]; + for (int s = 0; s < NUM_STEPS; s++) { + if (s >= numSteps[selectedTrack]) continue; + + int x = s % NUM_STEPS; + int yBase = (s / NUM_STEPS) * 2; + uint32_t color = 0, dimColor = 0; + bool isCursorHere = (isPlaying && s == (playbackStep % numSteps[selectedTrack])); + if (trackSequence[s].note != -1) { + color = getNoteColor(trackSequence[s].note, trackSequence[s].tie); + dimColor = getNoteColor(trackSequence[s].note, true); + } + uint32_t c[4] = {0}; + if (trackSequence[s].note != -1) { + int octave = trackSequence[s].note / 12; + if (octave > 4) { c[0] = color; if (trackSequence[s].accent) c[1] = dimColor; } + else if (octave < 4) { c[2] = color; if (trackSequence[s].accent) c[1] = dimColor; } + else { c[1] = color; if (trackSequence[s].accent) { c[0] = dimColor; c[2] = dimColor; } } + } + uint32_t cursorColor = pixels.Color(0, 0, 50); + if (isPlaying) { + cursorColor = pixels.Color(0, 50, 0); + if (songModeEnabled && s >= NUM_STEPS) { + int repeats = min(songRepeatsRemaining, NUM_STEPS); + if (x >= (NUM_STEPS - repeats)) cursorColor = (songRepeatsRemaining == 1 && x == 7 && (millis()/250)%2) ? pixels.Color(255, 200, 0) : pixels.Color(100, 220, 40); + } + } + + if (cursorColor != 0) { + if (isCursorHere) { + for(int i=0; i<4; i++) { + if (c[i] == 0) c[i] = cursorColor; + } + } else { + uint8_t r = (uint8_t)(cursorColor >> 16), g = (uint8_t)(cursorColor >> 8), b = (uint8_t)cursorColor; + c[3] = pixels.Color(r/5, g/5, b/5); + } + } + for(int i=0; i<4; i++) pixels.setPixelColor(getPixelIndex(x, yBase + i), c[i]); + } + + // --- Overview of all tracks on bottom 4 rows --- + for(int t=0; t= currentTrackSteps) continue; + + uint32_t pixelColor = 0; + + // Background for active track + if (t == selectedTrack) { + pixelColor = COLOR_CURSOR_DIM; + } + else if (sequence[t][s].note != -1) // Note + { + pixelColor = getNoteColor(sequence[t][s].note, !sequence[t][s].accent); + } + + // Playhead + if (isPlaying && (s == (playbackStep % currentTrackSteps))) { + if (t == selectedTrack) { + pixelColor = COLOR_CURSOR; + } else if (trackMute[t]) { + pixelColor = COLOR_MUTED_PLAYHEAD; + } else { + pixelColor = COLOR_PLAYHEAD; + } + } + + pixels.setPixelColor(getPixelIndex(s, row), pixelColor); + } + } + } + if (sequenceChangeScheduled && (millis() / 125) % 2) pixels.setPixelColor(NUM_PIXELS - 1, pixels.Color(127, 50, 0)); + pixels.show(); +} diff --git a/LedMatrix.h b/LedMatrix.h new file mode 100644 index 0000000..8a6f6e6 --- /dev/null +++ b/LedMatrix.h @@ -0,0 +1,28 @@ +#ifndef LED_MATRIX_H +#define LED_MATRIX_H + +#include +#include "TrackerTypes.h" +#include "config.h" + +class LedMatrix { +public: + LedMatrix(); + void begin(); + void update(const Step sequence[][NUM_STEPS], int playbackStep, bool isPlaying, + UIState currentState, bool songModeEnabled, + int songRepeatsRemaining, bool sequenceChangeScheduled, PlayMode playMode, + int selectedTrack, const int* numSteps, const bool* trackMute); + + void setBrightness(uint8_t b); + void clear(); + void show(); + +private: + Adafruit_NeoPixel pixels; + + uint32_t getNoteColor(int note, bool dim); + int getPixelIndex(int x, int y); +}; + +#endif diff --git a/UIManager.cpp b/UIManager.cpp index fc7251c..a56796d 100644 --- a/UIManager.cpp +++ b/UIManager.cpp @@ -7,16 +7,11 @@ #define SCREEN_HEIGHT 64 #define OLED_RESET -1 #define SCREEN_ADDRESS 0x3C -#define PIN_NEOPIXEL 6 -#define NEOPIXELS_X 16 -#define NEOPIXELS_Y 8 -#define NUM_PIXELS 64*2 // 8x8 LEDs in 2 panels UIManager ui; UIManager::UIManager() - : display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET), - pixels(NUM_PIXELS, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800) + : display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET) { } @@ -31,11 +26,7 @@ void UIManager::begin() { display.display(); // Setup NeoPixel Matrix - pixels.setPin(PIN_NEOPIXEL); - pixels.begin(); - pixels.setBrightness(40); - pixels.clear(); - pixels.show(); + ledMatrix.begin(); } void UIManager::showMessage(const char* msg) { @@ -67,61 +58,22 @@ void UIManager::draw(UIState currentState, int menuSelection, drawMenu(menuSelection, currentState, midiChannel, tempo, currentStrategy->getName(), queuedTheme, currentThemeIndex, numScaleNotes, scaleNotes, melodySeed, currentTrackNumSteps, mutationEnabled, songModeEnabled, isPlaying, randomizeTrack, trackMute, trackIntensities); break; case UI_SETUP_CHANNEL_EDIT: - display.println(F("SET MIDI CHANNEL")); - display.drawLine(0, 8, 128, 8, SSD1306_WHITE); - display.setCursor(20, 25); - display.setTextSize(2); - display.print(F("CH: ")); - if (midiChannel < 10) display.print(F(" ")); - display.print(midiChannel); - display.setTextSize(1); - display.setCursor(0, 50); - display.println(F(" (Press to confirm)")); + drawEditScreen("SET MIDI CHANNEL", "CH: ", midiChannel); break; case UI_EDIT_TEMPO: - 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)")); + drawEditScreen("SET TEMPO", "BPM: ", tempo); break; case UI_EDIT_STEPS: - display.println(F("SET STEPS")); - display.drawLine(0, 8, 128, 8, SSD1306_WHITE); - display.setCursor(20, 25); - display.setTextSize(2); - display.print(F("LEN: ")); - display.print(currentTrackNumSteps); - display.setTextSize(1); - display.setCursor(0, 50); - display.println(F(" (Press to confirm)")); + drawEditScreen("SET STEPS", "LEN: ", currentTrackNumSteps); break; case UI_EDIT_FLAVOUR: - display.println(F("SET FLAVOUR")); - display.drawLine(0, 8, 128, 8, SSD1306_WHITE); - display.setCursor(20, 25); - display.setTextSize(2); - display.print(currentStrategy->getName()); - display.setTextSize(1); - display.setCursor(0, 50); - display.println(F(" (Press to confirm)")); + drawEditScreen("SET FLAVOUR", "", currentStrategy->getName()); break; case UI_EDIT_ROOT: - display.println(F("SET ROOT NOTE")); - display.drawLine(0, 8, 128, 8, SSD1306_WHITE); - display.setCursor(20, 25); - display.setTextSize(2); { const char* noteNames[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; - display.print(noteNames[currentRoot % 12]); + drawEditScreen("SET ROOT NOTE", "", noteNames[currentRoot % 12]); } - display.setTextSize(1); - display.setCursor(0, 50); - display.println(F(" (Press to confirm)")); break; case UI_EDIT_SCALE_TYPE: display.println(F("ENABLED SCALES")); @@ -147,15 +99,7 @@ void UIManager::draw(UIState currentState, int menuSelection, drawNumberEditor("SET INTENSITY", trackIntensities[randomizeTrack], 1, 10); break; case UI_RANDOMIZE_TRACK_EDIT: - display.println(F("SET TRACK")); - display.drawLine(0, 8, 128, 8, SSD1306_WHITE); - display.setCursor(20, 25); - display.setTextSize(2); - display.print(F("TRK: ")); - display.print(randomizeTrack + 1); - display.setTextSize(1); - display.setCursor(0, 50); - display.println(F(" (Press to confirm)")); + drawEditScreen("SET TRACK", "TRK: ", randomizeTrack + 1); break; case UI_SCALE_EDIT: case UI_SCALE_NOTE_EDIT: @@ -265,6 +209,24 @@ void UIManager::drawNumberEditor(const char* title, int value, int minVal, int m display.println(F(" (Press to confirm)")); } +void UIManager::drawEditScreen(const char* title, const char* label, const char* valueStr) { + display.println(title); + display.drawLine(0, 8, 128, 8, SSD1306_WHITE); + display.setCursor(20, 25); + display.setTextSize(2); + if (label && *label) display.print(label); + display.print(valueStr); + display.setTextSize(1); + display.setCursor(0, 50); + display.println(F(" (Press to confirm)")); +} + +void UIManager::drawEditScreen(const char* title, const char* label, int value) { + char buf[16]; + itoa(value, buf, 10); + drawEditScreen(title, label, buf); +} + void UIManager::drawMenu(int selection, UIState currentState, int midiChannel, int tempo, const char* flavourName, int queuedTheme, int currentThemeIndex, int numScaleNotes, const int* scaleNotes, int melodySeed, int currentTrackNumSteps, bool mutationEnabled, @@ -368,139 +330,9 @@ void UIManager::drawMenu(int selection, UIState currentState, int midiChannel, i } } -uint32_t UIManager::getNoteColor(int note, bool dim) { - if (note == -1) return 0; - uint16_t hue = 30000 + (note % 12) * 3628; - return Adafruit_NeoPixel::ColorHSV(hue, 255, dim ? 10 : 50); -} - -int UIManager::getPixelIndex(int x, int y) { - // [i] Here you can adjust the mapping from "logical" pixel coordinates - // to your physical NeoPixel layout. It depends on how you connected it. - return NEOPIXELS_Y * x + (NEOPIXELS_Y - 1 - y); -} - void UIManager::updateLeds(const Step sequence[][NUM_STEPS], int playbackStep, bool isPlaying, UIState currentState, bool songModeEnabled, int songRepeatsRemaining, bool sequenceChangeScheduled, PlayMode playMode, int selectedTrack, const int* numSteps, int numScaleNotes, const int* scaleNotes, const bool* trackMute) { - pixels.clear(); - - const uint32_t COLOR_PLAYHEAD = pixels.Color(0, 255, 0); - const uint32_t COLOR_PLAYHEAD_DIM = pixels.Color(0, 32, 0); - const uint32_t COLOR_MUTED_PLAYHEAD = pixels.Color(0, 0, 255); - const uint32_t COLOR_CURSOR = pixels.Color(255, 100, 100); - const uint32_t COLOR_CURSOR_DIM = pixels.Color(32, 0, 0); - - if(playMode == MODE_POLY) { - for(int t=0; t currentTrackSteps - if (s < currentTrackSteps) { - step_color = COLOR_PLAYHEAD_DIM; // It's a valid step - if (isPlaying && (s == (playbackStep % currentTrackSteps))) { - if (trackMute[t]) { - step_color = COLOR_MUTED_PLAYHEAD; - } else { - step_color = COLOR_PLAYHEAD; - } - } - } - pixels.setPixelColor(getPixelIndex(col, step_row), step_color); - } - } - } else { - // --- Mono Mode (original) --- - const Step* trackSequence = sequence[selectedTrack]; - for (int s = 0; s < NUM_STEPS; s++) { - if (s >= numSteps[selectedTrack]) continue; - - int x = s % NUM_STEPS; - int yBase = (s / NUM_STEPS) * 2; - uint32_t color = 0, dimColor = 0; - bool isCursorHere = (isPlaying && s == (playbackStep % numSteps[selectedTrack])); - if (trackSequence[s].note != -1) { - color = getNoteColor(trackSequence[s].note, trackSequence[s].tie); - dimColor = getNoteColor(trackSequence[s].note, true); - } - uint32_t c[4] = {0}; - if (trackSequence[s].note != -1) { - int octave = trackSequence[s].note / 12; - if (octave > 4) { c[0] = color; if (trackSequence[s].accent) c[1] = dimColor; } - else if (octave < 4) { c[2] = color; if (trackSequence[s].accent) c[1] = dimColor; } - else { c[1] = color; if (trackSequence[s].accent) { c[0] = dimColor; c[2] = dimColor; } } - } - uint32_t cursorColor = pixels.Color(0, 0, 50); - if (isPlaying) { - cursorColor = pixels.Color(0, 50, 0); - if (songModeEnabled && s >= NUM_STEPS) { - int repeats = min(songRepeatsRemaining, NUM_STEPS); - if (x >= (NUM_STEPS - repeats)) cursorColor = (songRepeatsRemaining == 1 && x == 7 && (millis()/250)%2) ? pixels.Color(255, 200, 0) : pixels.Color(100, 220, 40); - } - } - - if (cursorColor != 0) { - if (isCursorHere) { - for(int i=0; i<4; i++) { - if (c[i] == 0) c[i] = cursorColor; - } - } else { - uint8_t r = (uint8_t)(cursorColor >> 16), g = (uint8_t)(cursorColor >> 8), b = (uint8_t)cursorColor; - c[3] = pixels.Color(r/5, g/5, b/5); - } - } - for(int i=0; i<4; i++) pixels.setPixelColor(getPixelIndex(x, yBase + i), c[i]); - } - - // --- Overview of all tracks on bottom 4 rows --- - for(int t=0; t= currentTrackSteps) continue; - - uint32_t pixelColor = 0; - - // Background for active track - if (t == selectedTrack) { - pixelColor = COLOR_CURSOR_DIM; - } - else if (sequence[t][s].note != -1) // Note - { - pixelColor = getNoteColor(sequence[t][s].note, !sequence[t][s].accent); - } - - // Playhead - if (isPlaying && (s == (playbackStep % currentTrackSteps))) { - if (t == selectedTrack) { - pixelColor = COLOR_CURSOR; - } else if (trackMute[t]) { - pixelColor = COLOR_MUTED_PLAYHEAD; - } else { - pixelColor = COLOR_PLAYHEAD; - } - } - - pixels.setPixelColor(getPixelIndex(s, row), pixelColor); - } - } - } - if (sequenceChangeScheduled && (millis() / 125) % 2) pixels.setPixelColor(NUM_PIXELS - 1, pixels.Color(127, 50, 0)); - pixels.show(); + ledMatrix.update(sequence, playbackStep, isPlaying, currentState, songModeEnabled, songRepeatsRemaining, sequenceChangeScheduled, playMode, selectedTrack, numSteps, trackMute); } \ No newline at end of file diff --git a/UIManager.h b/UIManager.h index 2ed7807..d305910 100644 --- a/UIManager.h +++ b/UIManager.h @@ -3,9 +3,9 @@ #include #include -#include #include "TrackerTypes.h" #include "MelodyStrategy.h" +#include "LedMatrix.h" class UIManager { public: @@ -29,7 +29,7 @@ public: private: Adafruit_SSD1306 display; - Adafruit_NeoPixel pixels; + LedMatrix ledMatrix; void drawMenu(int selection, UIState currentState, int midiChannel, int tempo, const char* flavourName, int queuedTheme, int currentThemeIndex, @@ -37,9 +37,8 @@ private: bool mutationEnabled, bool songModeEnabled, bool isPlaying, int randomizeTrack, const bool* trackMute, const int* trackIntensities); void drawNumberEditor(const char* title, int value, int minVal, int maxVal); - - uint32_t getNoteColor(int note, bool dim); - int getPixelIndex(int x, int y); + void drawEditScreen(const char* title, const char* label, const char* valueStr); + void drawEditScreen(const char* title, const char* label, int value); }; extern UIManager ui;