diff --git a/SequenceGenerator.cpp b/SequenceGenerator.cpp index 12a3d4e..88de16f 100644 --- a/SequenceGenerator.cpp +++ b/SequenceGenerator.cpp @@ -75,7 +75,11 @@ void SequenceGenerator::updateScale() { midi.unlock(); } -void SequenceGenerator::pickRandomScaleType() { +void SequenceGenerator::pickRandomScaleType(int themeType) { + unsigned long seed = themeType * 9999; + for(int i=0; i= 4) startIdx = menuSelection - 3; - - int y = 12; - for (int i = startIdx; i < totalItems; i++) { - if (y > 54) break; - - if (i == menuSelection) { - display.fillRect(0, y, 75, 9, SSD1306_WHITE); - display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); - } else { - display.setTextColor(SSD1306_WHITE); - } - display.setCursor(2, y + 1); - - if (i == 0) { - display.print(F("Back")); - } else if (i == 1) { - display.print(F("Randomize")); - } else if (i <= numScaleNotes + 1) { - int noteIdx = i - 2; - const char* noteNames[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; - display.print(noteNames[scaleNotes[noteIdx]]); - if (currentState == UI_SCALE_NOTE_EDIT && i == menuSelection) { - display.print(F(" <")); - } - } else if (i == numScaleNotes + 2) { - display.print(F("Transpose")); - if (currentState == UI_SCALE_TRANSPOSE) { - display.print(F(" < >")); - } - } else if (i == numScaleNotes + 3) { - display.print(F("Add Note")); - } else if (i == numScaleNotes + 4) { - display.print(F("Remove Note")); - } - y += 9; - } - - // Piano Roll Preview - int px = 82; - int py = 20; - int wk_w = 5; - int wk_h = 20; - int bk_w = 4; - int bk_h = 12; - - // White keys: C, D, E, F, G, A, B - int whiteNotes[] = {0, 2, 4, 5, 7, 9, 11}; - for (int k = 0; k < 7; k++) { - bool active = false; - for (int j = 0; j < numScaleNotes; j++) { - if (scaleNotes[j] == whiteNotes[k]) { active = true; break; } - } - if (active) display.fillRect(px + k*6, py, wk_w, wk_h, SSD1306_WHITE); - else display.drawRect(px + k*6, py, wk_w, wk_h, SSD1306_WHITE); - } - - // Black keys: C#, D#, F#, G#, A# - int blackNotes[] = {1, 3, 6, 8, 10}; - int blackOffsets[] = {3, 9, 21, 27, 33}; - for (int k = 0; k < 5; k++) { - bool active = false; - for (int j = 0; j < numScaleNotes; j++) { - if (scaleNotes[j] == blackNotes[k]) { active = true; break; } - } - int bx = px + blackOffsets[k]; - display.fillRect(bx - 1, py - 1, bk_w + 2, bk_h + 2, SSD1306_BLACK); - if (active) display.fillRect(bx, py, bk_w, bk_h, SSD1306_WHITE); - else display.drawRect(bx, py, bk_w, bk_h, SSD1306_WHITE); - } - break; } display.display(); } @@ -268,6 +169,18 @@ void UIManager::drawMenu(int selection, UIState currentState, int midiChannel, i } display.print(menuItems[i].label); + + // Render checkbox for scale types + if (menuItems[i].id >= MENU_ID_SCALE_TYPE_CHROMATIC && menuItems[i].id <= MENU_ID_SCALE_TYPE_CHORD_7) { + int typeIndex = menuItems[i].id - MENU_ID_SCALE_TYPE_CHROMATIC; + bool enabled = (enabledScaleTypes & (1 << typeIndex)); + int boxX = x - 10; + display.drawRect(boxX, y + 1, 7, 7, (i == selection) ? SSD1306_BLACK : SSD1306_WHITE); + if (enabled) display.fillRect(boxX + 2, y + 3, 3, 3, (i == selection) ? SSD1306_BLACK : SSD1306_WHITE); + if (typeIndex == currentScaleType) { + display.print(F(" *")); + } + } MenuItemID id = menuItems[i].id; @@ -279,24 +192,11 @@ void UIManager::drawMenu(int selection, UIState currentState, int midiChannel, i if (id == MENU_ID_PLAYBACK) { display.print(F(": ")); display.print(isPlaying ? F("ON") : F("OFF")); } else if (id == MENU_ID_MELODY) { display.print(F(": ")); display.print(melodySeed); - } else if (id == MENU_ID_SCALE) { - display.print(F(": ")); - if (numScaleNotes > 0) { - const char* noteNames[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; - for (int j = 0; j < min(numScaleNotes, 6); j++) { - display.print(noteNames[scaleNotes[j]]); - if (j < min(numScaleNotes, 6) - 1) display.print(F(" ")); - } - } } else if (id == MENU_ID_TEMPO) { display.print(F(": ")); display.print(tempo); } else if (id == MENU_ID_ROOT) { const char* noteNames[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; display.print(F(": ")); display.print(noteNames[currentRoot % 12]); } - else if (id == MENU_ID_SCALE_TYPE) { - const char* typeNames[] = {"Chrom", "Major", "Minor", "H.Min", "P.Maj", "P.Min", "ChdMaj", "ChdMin", "ChdDim", "Chd7"}; - display.print(F(": ")); if (currentScaleType >= 0 && currentScaleType < 10) display.print(typeNames[currentScaleType]); - } else if (id == MENU_ID_STEPS) { display.print(F(": ")); display.print(currentTrackNumSteps); } else if (id == MENU_ID_SONG_MODE) { display.print(F(": ")); display.print(songModeEnabled ? F("ON") : F("OFF")); } else if (id == MENU_ID_TRACK_SELECT) { display.print(F(": ")); display.print(randomizeTrack + 1); } diff --git a/UIThread.cpp b/UIThread.cpp index 4332938..9f552e9 100644 --- a/UIThread.cpp +++ b/UIThread.cpp @@ -12,9 +12,6 @@ static Step local_sequence[NUM_TRACKS][NUM_STEPS]; static void handleInput(); -static int scaleEditSelection = 0; -static int scaleTypeEditIndex = 0; -static int scaleEditNoteIndex = 0; static void drawUI(); static void updateLeds(); @@ -35,7 +32,7 @@ void generateRandomScale() { } void generateTheme(int themeType) { - SequenceGenerator::pickRandomScaleType(); + SequenceGenerator::pickRandomScaleType(themeType); SequenceGenerator::generateSequenceData(themeType, local_sequence); Serial.println(F("Generating theme.")); @@ -106,11 +103,6 @@ static void handleInput() { SequenceGenerator::generateSequenceData((queuedTheme != -1) ? queuedTheme : currentThemeIndex, nextSequence); } break; - case UI_EDIT_SCALE_TYPE: - scaleTypeEditIndex += delta; - if (scaleTypeEditIndex < 0) scaleTypeEditIndex = 10; - if (scaleTypeEditIndex > 10) scaleTypeEditIndex = 0; - break; case UI_EDIT_FLAVOUR: { currentStrategyIndices[randomizeTrack] += (delta > 0 ? 1 : -1); @@ -127,38 +119,6 @@ static void handleInput() { trackIntensity[randomizeTrack] = current; } break; - case UI_SCALE_EDIT: - scaleEditSelection += (delta > 0 ? 1 : -1); - if (scaleEditSelection < 0) scaleEditSelection = numScaleNotes + 4; - if (scaleEditSelection > numScaleNotes + 4) scaleEditSelection = 0; - break; - case UI_SCALE_NOTE_EDIT: - scaleNotes[scaleEditNoteIndex] += (delta > 0 ? 1 : -1); - if (scaleNotes[scaleEditNoteIndex] < 0) scaleNotes[scaleEditNoteIndex] = 11; - if (scaleNotes[scaleEditNoteIndex] > 11) scaleNotes[scaleEditNoteIndex] = 0; - midi.lock(); - midi.sendNoteOn(60 + scaleNotes[scaleEditNoteIndex], 100, midiChannels[randomizeTrack]); - midi.unlock(); - delay(50); - midi.lock(); - midi.sendNoteOff(60 + scaleNotes[scaleEditNoteIndex], midiChannels[randomizeTrack]); - midi.unlock(); - break; - case UI_SCALE_TRANSPOSE: - if (delta != 0) { - int shift = delta % 12; - if (shift < 0) shift += 12; - for(int i=0; i 0 ? 1 : -1); @@ -225,16 +185,7 @@ static void handleInput() { saveSequence(true); break; - case MENU_ID_SCALE: - currentState = UI_SCALE_EDIT; - scaleEditSelection = 0; - break; - case MENU_ID_ROOT: currentState = UI_EDIT_ROOT; break; - case MENU_ID_SCALE_TYPE: - currentState = UI_EDIT_SCALE_TYPE; - scaleTypeEditIndex = 0; - break; case MENU_ID_TEMPO: currentState = UI_EDIT_TEMPO; break; case MENU_ID_STEPS: currentState = UI_EDIT_STEPS; break; @@ -256,10 +207,17 @@ static void handleInput() { case MENU_ID_RESET: factoryReset(); break; default: + if (menuItems[menuSelection].id >= MENU_ID_SCALE_TYPE_CHROMATIC && menuItems[menuSelection].id <= MENU_ID_SCALE_TYPE_CHORD_7) { + int typeIndex = menuItems[menuSelection].id - MENU_ID_SCALE_TYPE_CHROMATIC; + enabledScaleTypes ^= (1 << typeIndex); + if (enabledScaleTypes == 0) enabledScaleTypes = (1 << typeIndex); // Prevent disabling all + break; + } if (menuItems[menuSelection].id >= MENU_ID_THEME_1 && menuItems[menuSelection].id <= MENU_ID_THEME_7) { const int selectedTheme = menuItems[menuSelection].id - MENU_ID_THEME_1 + 1; if (isPlaying) { queuedTheme = selectedTheme; + SequenceGenerator::pickRandomScaleType(queuedTheme); midi.lock(); SequenceGenerator::generateSequenceData(queuedTheme, nextSequence); sequenceChangeScheduled = true; @@ -298,15 +256,6 @@ static void handleInput() { currentState = UI_MENU_MAIN; saveSequence(true); break; - case UI_EDIT_SCALE_TYPE: - if (scaleTypeEditIndex == 10) { - currentState = UI_MENU_MAIN; - saveSequence(true); - } else { - enabledScaleTypes ^= (1 << scaleTypeEditIndex); - if (enabledScaleTypes == 0) enabledScaleTypes = (1 << scaleTypeEditIndex); // Prevent disabling all - } - break; case UI_EDIT_FLAVOUR: currentState = UI_MENU_MAIN; if (isPlaying) { @@ -339,71 +288,6 @@ static void handleInput() { currentState = UI_MENU_MAIN; saveSequence(true); break; - case UI_SCALE_EDIT: - if (scaleEditSelection == 0) { - currentState = UI_MENU_MAIN; - saveSequence(true); - } else if (scaleEditSelection == 1) { - generateRandomScale(); - if (isPlaying) { - int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex; - midi.lock(); - SequenceGenerator::generateSequenceData(theme, nextSequence); - sequenceChangeScheduled = true; - midi.unlock(); - } - saveSequence(true); - } else if (scaleEditSelection <= numScaleNotes + 1) { - scaleEditNoteIndex = scaleEditSelection - 2; - currentState = UI_SCALE_NOTE_EDIT; - } else if (scaleEditSelection == numScaleNotes + 2) { - currentState = UI_SCALE_TRANSPOSE; - } else if (scaleEditSelection == numScaleNotes + 3) { - if (numScaleNotes < 12) { - int next = (numScaleNotes > 0) ? (scaleNotes[numScaleNotes-1] + 1) % 12 : 0; - scaleNotes[numScaleNotes] = next; - numScaleNotes++; - scaleEditSelection--; // Move cursor to the new note - if (isPlaying) { - int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex; - midi.lock(); - SequenceGenerator::generateSequenceData(theme, nextSequence); - sequenceChangeScheduled = true; - midi.unlock(); - } - saveSequence(true); - } - } else { - if (numScaleNotes > 1) { - numScaleNotes--; - scaleEditSelection--; - if (isPlaying) { - int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex; - midi.lock(); - SequenceGenerator::generateSequenceData(theme, nextSequence); - sequenceChangeScheduled = true; - midi.unlock(); - } - saveSequence(true); - } - } - break; - case UI_SCALE_NOTE_EDIT: - sortArray(scaleNotes, numScaleNotes); - if (isPlaying) { - int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex; - midi.lock(); - SequenceGenerator::generateSequenceData(theme, nextSequence); - sequenceChangeScheduled = true; - midi.unlock(); - } - currentState = UI_SCALE_EDIT; - saveSequence(true); - break; - case UI_SCALE_TRANSPOSE: - currentState = UI_SCALE_EDIT; - saveSequence(true); - break; } } } @@ -444,13 +328,7 @@ static void drawUI() { local_randomizeTrack = randomizeTrack; local_currentState = currentState; - if (local_currentState == UI_SCALE_EDIT || local_currentState == UI_SCALE_NOTE_EDIT || local_currentState == UI_SCALE_TRANSPOSE) { - local_menuSelection = scaleEditSelection; - } else if (local_currentState == UI_EDIT_SCALE_TYPE) { - local_menuSelection = scaleTypeEditIndex; - } else { - local_menuSelection = menuSelection; - } + local_menuSelection = menuSelection; local_midiChannel = midiChannels[local_randomizeTrack]; local_tempo = tempo; local_currentTrackNumSteps = numSteps[local_randomizeTrack]; @@ -517,7 +395,7 @@ static void updateLeds() { // It's a TRACK section item (Track, Mute, Flavour, Mutation, Themes) ledDisplayMode = MODE_MONO; } - } else if (local_currentState == UI_EDIT_STEPS || local_currentState == UI_EDIT_FLAVOUR || local_currentState == UI_RANDOMIZE_TRACK_EDIT || local_currentState == UI_SCALE_EDIT || local_currentState == UI_SCALE_NOTE_EDIT || local_currentState == UI_SCALE_TRANSPOSE) { + } else if (local_currentState == UI_EDIT_STEPS || local_currentState == UI_EDIT_FLAVOUR || local_currentState == UI_RANDOMIZE_TRACK_EDIT) { // These are entered from TRACK section items ledDisplayMode = MODE_MONO; } @@ -534,6 +412,7 @@ void loopUI() { int nextTheme = random(1, 8); // Themes 1-7 int repeats = random(1, 9); // 1-8 repeats + SequenceGenerator::pickRandomScaleType(nextTheme); SequenceGenerator::generateSequenceData(nextTheme, nextSequence); queuedTheme = nextTheme; nextSongRepeats = repeats;