diff --git a/ArpStrategy.h b/ArpStrategy.h index cca1051..2c253a2 100644 --- a/ArpStrategy.h +++ b/ArpStrategy.h @@ -10,24 +10,102 @@ public: randomSeed(seed); if (numScaleNotes == 0) return; - int currentNoteIndex = 0; - int octave = 4; + // 1. Select Subset + int subsetSize = random(2, numScaleNotes + 1); + if (subsetSize > 12) subsetSize = 12; + int subset[12]; + + // Create indices to shuffle + int indices[12]; + for(int i=0; i= numScaleNotes) { - currentNoteIndex = 0; - octave++; - if (octave > 5) octave = 3; + int mode = random(3); // 0: Up, 1: Down, 2: Up/Down + int numOctaves = 3; + int baseOctave = 3; + int totalNotes = subsetSize * numOctaves; + + int currentIndex = 0; + int direction = 1; + + if (mode == 1) { // Down + currentIndex = totalNotes - 1; + direction = -1; + } + + for (int i = 0; i < arpLength; i++) { + // Chance of rest (15%) + if (random(100) < 15) { + arpPattern[i].note = -1; + arpPattern[i].accent = false; + arpPattern[i].tie = false; + } else { + int octaveOffset = currentIndex / subsetSize; + int noteIndex = currentIndex % subsetSize; + int octave = baseOctave + octaveOffset; + + arpPattern[i].note = 12 * octave + subset[noteIndex]; + arpPattern[i].accent = (i % 4 == 0); // Accent on beat + arpPattern[i].tie = false; + } + + if (mode == 0) { // Up + currentIndex++; + if (currentIndex >= totalNotes) currentIndex = 0; + } else if (mode == 1) { // Down + currentIndex--; + if (currentIndex < 0) currentIndex = totalNotes - 1; + } else { // Up/Down + currentIndex += direction; + if (currentIndex >= totalNotes) { + currentIndex = max(0, totalNotes - 2); + direction = -1; + } else if (currentIndex < 0) { + currentIndex = min(totalNotes - 1, 1); + direction = 1; + } } } + + // 3. Fill Sequence + for (int i = 0; i < numSteps; i++) { + sequence[i] = arpPattern[i % arpLength]; + } randomSeed(micros()); } + void generateScale(int* scaleNotes, int& numScaleNotes) override { + numScaleNotes = random(3, 13); // 3 to 12 notes + for (int i = 0; i < 12; i++) { + scaleNotes[i] = i; // Fill with all notes + } + // Shuffle + for (int i = 0; i < 12; i++) { + int j = random(12); + int temp = scaleNotes[i]; + scaleNotes[i] = scaleNotes[j]; + scaleNotes[j] = temp; + } + sortArray(scaleNotes, numScaleNotes); + } + void mutate(Step* sequence, int numSteps, int* scaleNotes, int numScaleNotes) override { // Swap two notes int s1 = random(numSteps); diff --git a/LuckyStrategy.h b/LuckyStrategy.h index dd4f319..7815fee 100644 --- a/LuckyStrategy.h +++ b/LuckyStrategy.h @@ -19,6 +19,21 @@ public: randomSeed(micros()); } + void generateScale(int* scaleNotes, int& numScaleNotes) override { + numScaleNotes = random(3, 13); // 3 to 12 notes + for (int i = 0; i < 12; i++) { + scaleNotes[i] = i; // Fill with all notes + } + // Shuffle + for (int i = 0; i < 12; i++) { + int j = random(12); + int temp = scaleNotes[i]; + scaleNotes[i] = scaleNotes[j]; + scaleNotes[j] = temp; + } + sortArray(scaleNotes, numScaleNotes); + } + void mutate(Step* sequence, int numSteps, int* scaleNotes, int numScaleNotes) override { // Mutate 1 or 2 steps int count = random(1, 3); diff --git a/MelodyStrategy.h b/MelodyStrategy.h index fb5c54f..c00af2f 100644 --- a/MelodyStrategy.h +++ b/MelodyStrategy.h @@ -6,6 +6,7 @@ class MelodyStrategy { public: virtual void generate(Step* sequence, int numSteps, int* scaleNotes, int numScaleNotes, int seed) = 0; + virtual void generateScale(int* scaleNotes, int& numScaleNotes) = 0; virtual void mutate(Step* sequence, int numSteps, int* scaleNotes, int numScaleNotes) = 0; virtual const char* getName() = 0; virtual ~MelodyStrategy() {} diff --git a/RP2040_Tracker.ino b/RP2040_Tracker.ino index 790be42..25fce37 100644 --- a/RP2040_Tracker.ino +++ b/RP2040_Tracker.ino @@ -34,7 +34,7 @@ UIState currentState = UI_MENU_MAIN; const char* mainMenu[] = { "Tracker", "Randomize", "Setup" }; const int mainMenuCount = sizeof(mainMenu) / sizeof(char*); -const char* randomizeMenu[] = { "Back", "Scale", "Melody", "Flavour", "Tempo", "Mutation", "Song Mode", "Theme 1", "Theme 2", "Theme 3", "Theme 4", "Theme 5", "Theme 6", "Theme 7" }; +const char* randomizeMenu[] = { "Back", "Melody", "Flavour", "Scale", "Tempo", "Mutation", "Song Mode", "Theme 1", "Theme 2", "Theme 3", "Theme 4", "Theme 5", "Theme 6", "Theme 7" }; const int THEME_1_INDEX = 7; const int randomizeMenuCount = sizeof(randomizeMenu) / sizeof(char*); const char* setupMenu[] = { "Back", "Channel", "Save", "Load" }; @@ -100,18 +100,6 @@ void readEncoder() { } } -void sortArray(int arr[], int size) { - for (int i = 0; i < size - 1; i++) { - for (int j = 0; j < size - i - 1; j++) { - if (arr[j] > arr[j + 1]) { - int temp = arr[j]; - arr[j] = arr[j + 1]; - arr[j + 1] = temp; - } - } - } -} - void saveSequence(bool quiet = false) { int addr = 0; EEPROM.put(addr, EEPROM_MAGIC); addr += sizeof(EEPROM_MAGIC); @@ -194,18 +182,7 @@ void setup() { } void generateRandomScale() { - numScaleNotes = random(3, 13); // 3 to 12 notes - for (int i = 0; i < 12; i++) { - scaleNotes[i] = i; // Fill with all notes - } - // Shuffle - for (int i = 0; i < 12; i++) { - int j = random(12); - int temp = scaleNotes[i]; - scaleNotes[i] = scaleNotes[j]; - scaleNotes[j] = temp; - } - sortArray(scaleNotes, numScaleNotes); + strategies[currentStrategyIndex]->generateScale(scaleNotes, numScaleNotes); } void generateSequenceData(int themeType, Step* target) { @@ -333,17 +310,6 @@ void handleInput() { case UI_MENU_RANDOMIZE: if (menuSelection == 0) { currentState = UI_MENU_MAIN; menuSelection = 1; } if (menuSelection == 1) { - generateRandomScale(); // Scale - if (isPlaying) { - int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex; - midi.lock(); - generateSequenceData(theme, nextSequence); - sequenceChangeScheduled = true; - midi.unlock(); - } - saveSequence(true); - } - if (menuSelection == 2) { melodySeed = random(10000); // Melody if (isPlaying) { int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex; @@ -354,7 +320,18 @@ void handleInput() { } saveSequence(true); } - if (menuSelection == 3) currentState = UI_EDIT_FLAVOUR; + if (menuSelection == 2) currentState = UI_EDIT_FLAVOUR; // Flavour + if (menuSelection == 3) { // Scale + generateRandomScale(); + if (isPlaying) { + int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex; + midi.lock(); + generateSequenceData(theme, nextSequence); + sequenceChangeScheduled = true; + midi.unlock(); + } + saveSequence(true); + } if (menuSelection == 4) currentState = UI_EDIT_TEMPO; if (menuSelection == 5) mutationEnabled = !mutationEnabled; if (menuSelection == 6) { @@ -398,6 +375,13 @@ void handleInput() { break; case UI_EDIT_FLAVOUR: currentState = UI_MENU_RANDOMIZE; + if (isPlaying) { + queuedTheme = currentThemeIndex; + midi.lock(); + generateSequenceData(currentThemeIndex, nextSequence); + sequenceChangeScheduled = true; + midi.unlock(); + } saveSequence(true); break; } diff --git a/TrackerTypes.h b/TrackerTypes.h index f46c750..9e6f858 100644 --- a/TrackerTypes.h +++ b/TrackerTypes.h @@ -19,4 +19,16 @@ enum UIState { UI_EDIT_FLAVOUR }; +inline void sortArray(int arr[], int size) { + for (int i = 0; i < size - 1; i++) { + for (int j = 0; j < size - i - 1; j++) { + if (arr[j] > arr[j + 1]) { + int temp = arr[j]; + arr[j] = arr[j + 1]; + arr[j + 1] = temp; + } + } + } +} + #endif \ No newline at end of file diff --git a/UIManager.cpp b/UIManager.cpp index a9e0522..65a92e0 100644 --- a/UIManager.cpp +++ b/UIManager.cpp @@ -146,7 +146,11 @@ void UIManager::drawMenu(const char* title, const char* items[], int count, int display.print(F(" *")); } if (currentState == UI_MENU_RANDOMIZE) { - if (i == 1) { // Scale + if (i == 1) { // Melody + display.print(F(": ")); display.print(melodySeed); + } else if (i == 2) { // Flavour + display.print(F(": ")); display.print(flavourName); + } else if (i == 3) { // Scale display.print(F(": ")); if (numScaleNotes > 0) { const char* noteNames[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; @@ -155,9 +159,7 @@ void UIManager::drawMenu(const char* title, const char* items[], int count, int if (j < min(numScaleNotes, 6) - 1) display.print(F(" ")); } } - } else if (i == 2) { display.print(F(": ")); display.print(melodySeed); } - else if (i == 3) { display.print(F(": ")); display.print(flavourName); } - else if (i == 4) { display.print(F(": ")); display.print(tempo); } + } else if (i == 4) { display.print(F(": ")); display.print(tempo); } else if (i == 5) { display.print(F(": ")); display.print(mutationEnabled ? F("ON") : F("OFF")); } else if (i == 6) { display.print(F(": ")); display.print(songModeEnabled ? F("ON") : F("OFF")); } } @@ -245,23 +247,13 @@ void UIManager::updateLeds(const Step* sequence, int navSelection, int playbackS else { c[1] = color; if (sequence[s].accent) { c[0] = dimColor; c[2] = dimColor; } } } int stepNavIndex = navSelection - 1; - if (isPlaying) { - c[3] = pixels.Color(0, 50, 0); - if (songModeEnabled && s >= NUM_STEPS/2 && x >= (8 - min(songRepeatsRemaining, 8))) { - c[3] = (songRepeatsRemaining == 1 && x == 7 && (millis()/250)%2) ? pixels.Color(0, 250, 0) : pixels.Color(80, 100, 0); - } - } else if (currentState == UI_TRACKER && s == stepNavIndex) { - c[3] = isEditing ? pixels.Color(50, 0, 0) : pixels.Color(40, 40, 40); - } else if (c[3] == 0 && (isPlaying && s == playbackStep || (!isPlaying && currentState == UI_TRACKER && s == stepNavIndex))) { - // Cursor logic handled above, this block seems redundant or malformed in original logic translation, simplifying: - } // Apply cursor color logic from original code more strictly uint32_t cursorColor = 0; if (isPlaying) { cursorColor = pixels.Color(0, 50, 0); - if (songModeEnabled && s >= 56) { + if (songModeEnabled && s >= 8) { int repeats = min(songRepeatsRemaining, 8); - if (x >= (8 - repeats)) cursorColor = (songRepeatsRemaining == 1 && x == 7 && (millis()/250)%2) ? pixels.Color(255, 200, 0) : pixels.Color(154, 205, 50); + if (x >= (8 - repeats)) cursorColor = (songRepeatsRemaining == 1 && x == 7 && (millis()/250)%2) ? pixels.Color(255, 200, 0) : pixels.Color(100, 220, 40); } } else if (currentState == UI_TRACKER) { cursorColor = isEditing ? pixels.Color(50, 0, 0) : pixels.Color(40, 40, 40);