diff --git a/PlaybackThread.cpp b/PlaybackThread.cpp index 76b27a6..29f278c 100644 --- a/PlaybackThread.cpp +++ b/PlaybackThread.cpp @@ -47,7 +47,7 @@ static void handlePlayback() { for(int t=0; t= NUM_STEPS) nextStep = 0; + if (nextStep >= numSteps) nextStep = 0; // Determine if we are tying to the next note bool isTied = local_sequence[t][playbackStep].tie && (local_sequence[t][nextStep].note != -1); @@ -60,7 +60,7 @@ static void handlePlayback() { } playbackStep++; - if (playbackStep >= NUM_STEPS) { + if (playbackStep >= numSteps) { playbackStep = 0; // Theme change @@ -117,7 +117,7 @@ static void handlePlayback() { midi.sendNoteOn(local_sequence[t][playbackStep].note, velocity, trackChannel); } - int prevStep = (playbackStep == 0) ? NUM_STEPS - 1 : playbackStep - 1; + int prevStep = (playbackStep == 0) ? numSteps - 1 : playbackStep - 1; bool wasTied = local_sequence[t][prevStep].tie && (local_sequence[t][playbackStep].note != -1); int prevNote = local_sequence[t][prevStep].note; // Note Off for previous step (if tied - delayed Note Off) diff --git a/RP2040_Tracker.ino b/RP2040_Tracker.ino index a0f9eb5..31a5579 100644 --- a/RP2040_Tracker.ino +++ b/RP2040_Tracker.ino @@ -58,15 +58,19 @@ void setup() { // 5. Init Sequence randomSeed(micros()); + Serial.println(F("Loading sequence.")); EEPROM.begin(512); if (!loadSequence()) { - generateRandomScale(); + Serial.println(F("Starting fresh instead.")); + numSteps = NUM_STEPS; + for(int i=0; i 1000)) { diff --git a/SharedState.cpp b/SharedState.cpp index 5be1abf..accf3e2 100644 --- a/SharedState.cpp +++ b/SharedState.cpp @@ -20,6 +20,7 @@ MenuItem menuItems[] = { { "Playback", MENU_ID_PLAYBACK, false, false, 1 }, { "Melody", MENU_ID_MELODY, false, false, 1 }, { "Scale", MENU_ID_SCALE, false, false, 1 }, + { "Steps", MENU_ID_STEPS, false, false, 1 }, { "Tempo", MENU_ID_TEMPO, false, false, 1 }, { "Song Mode", MENU_ID_SONG_MODE, false, false, 1 }, { "Track", MENU_ID_GROUP_TRACK, true, true, 0 }, @@ -53,6 +54,7 @@ bool isItemVisible(int index) { int menuSelection = 0; volatile bool trackMute[NUM_TRACKS]; int randomizeTrack = 0; +volatile int numSteps = NUM_STEPS; volatile int playbackStep = 0; volatile int midiChannels[NUM_TRACKS]; int scaleNotes[12]; @@ -60,7 +62,7 @@ int numScaleNotes = 0; int melodySeeds[NUM_TRACKS]; volatile int queuedTheme = -1; volatile int currentThemeIndex = 1; -extern const uint32_t EEPROM_MAGIC = 0x4242424B; +extern const uint32_t EEPROM_MAGIC = 0x4242424D; MelodyStrategy* strategies[] = { new LuckyStrategy(), new ArpStrategy(), new EuclideanStrategy(), diff --git a/SharedState.h b/SharedState.h index 9b18064..e51db92 100644 --- a/SharedState.h +++ b/SharedState.h @@ -19,6 +19,7 @@ enum MenuItemID { MENU_ID_PLAYBACK, MENU_ID_MELODY, MENU_ID_SCALE, + MENU_ID_STEPS, MENU_ID_TEMPO, MENU_ID_SONG_MODE, @@ -55,6 +56,7 @@ bool isItemVisible(int index); extern int menuSelection; extern volatile bool trackMute[NUM_TRACKS]; extern int randomizeTrack; +extern volatile int numSteps; extern volatile int playbackStep; extern volatile int midiChannels[NUM_TRACKS]; extern int scaleNotes[12]; diff --git a/TrackerTypes.h b/TrackerTypes.h index 309a451..e836568 100644 --- a/TrackerTypes.h +++ b/TrackerTypes.h @@ -19,6 +19,7 @@ enum UIState { UI_MENU_MAIN, UI_SETUP_CHANNEL_EDIT, UI_EDIT_TEMPO, + UI_EDIT_STEPS, UI_EDIT_FLAVOUR, UI_SETUP_PLAYMODE_EDIT, UI_RANDOMIZE_TRACK_EDIT diff --git a/UIManager.cpp b/UIManager.cpp index b72874f..4a48194 100644 --- a/UIManager.cpp +++ b/UIManager.cpp @@ -49,7 +49,7 @@ void UIManager::showMessage(const char* msg) { void UIManager::draw(UIState currentState, int menuSelection, int midiChannel, int tempo, MelodyStrategy* currentStrategy, int queuedTheme, int currentThemeIndex, - int numScaleNotes, const int* scaleNotes, int melodySeed, + int numScaleNotes, const int* scaleNotes, int melodySeed, int numSteps, bool mutationEnabled, bool songModeEnabled, const Step sequence[][NUM_STEPS], int playbackStep, bool isPlaying, int randomizeTrack, const bool* trackMute) { @@ -61,7 +61,7 @@ void UIManager::draw(UIState currentState, int menuSelection, switch(currentState) { case UI_MENU_MAIN: - drawMenu(menuSelection, currentState, midiChannel, tempo, currentStrategy->getName(), queuedTheme, currentThemeIndex, numScaleNotes, scaleNotes, melodySeed, mutationEnabled, songModeEnabled, isPlaying, randomizeTrack, trackMute); + drawMenu(menuSelection, currentState, midiChannel, tempo, currentStrategy->getName(), queuedTheme, currentThemeIndex, numScaleNotes, scaleNotes, melodySeed, numSteps, mutationEnabled, songModeEnabled, isPlaying, randomizeTrack, trackMute); break; case UI_SETUP_CHANNEL_EDIT: display.println(F("SET MIDI CHANNEL")); @@ -86,6 +86,17 @@ void UIManager::draw(UIState currentState, int menuSelection, display.setCursor(0, 50); display.println(F(" (Press to confirm)")); 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(numSteps); + display.setTextSize(1); + display.setCursor(0, 50); + display.println(F(" (Press to confirm)")); + break; case UI_EDIT_FLAVOUR: display.println(F("SET FLAVOUR")); display.drawLine(0, 8, 128, 8, SSD1306_WHITE); @@ -113,7 +124,7 @@ void UIManager::draw(UIState currentState, int menuSelection, 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, bool mutationEnabled, + const int* scaleNotes, int melodySeed, int numSteps, bool mutationEnabled, bool songModeEnabled, bool isPlaying, int randomizeTrack, const bool* trackMute) { // Calculate visual cursor position and scroll offset @@ -173,6 +184,7 @@ void UIManager::drawMenu(int selection, UIState currentState, int midiChannel, i } } } else if (id == MENU_ID_TEMPO) { display.print(F(": ")); display.print(tempo); } + else if (id == MENU_ID_STEPS) { display.print(F(": ")); display.print(numSteps); } 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); } else if (id == MENU_ID_MUTE) { display.print(F(": ")); display.print(trackMute[randomizeTrack] ? F("YES") : F("NO")); } @@ -205,7 +217,7 @@ int UIManager::getPixelIndex(int x, int 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, int numScaleNotes, const int* scaleNotes, const bool* trackMute) { + int selectedTrack, int numSteps, int numScaleNotes, const int* scaleNotes, const bool* trackMute) { pixels.clear(); const uint32_t COLOR_PLAYHEAD = pixels.Color(0, 255, 0); @@ -217,6 +229,8 @@ void UIManager::updateLeds(const Step sequence[][NUM_STEPS], int playbackStep, b if(playMode == MODE_POLY) { for(int t=0; t= numSteps) continue; + int row = t * 2 + (s / 8); int col = s % 8; @@ -242,6 +256,8 @@ void UIManager::updateLeds(const Step sequence[][NUM_STEPS], int playbackStep, b // --- Mono Mode (original) --- const Step* trackSequence = sequence[selectedTrack]; for (int s = 0; s < NUM_STEPS; s++) { + if (s >= numSteps) continue; + int x = s % 8; int yBase = (s / 8) * 4; uint32_t color = 0, dimColor = 0; diff --git a/UIManager.h b/UIManager.h index 31a00b2..a9bd06f 100644 --- a/UIManager.h +++ b/UIManager.h @@ -17,7 +17,7 @@ public: void draw(UIState currentState, int menuSelection, int midiChannel, int tempo, MelodyStrategy* currentStrategy, int queuedTheme, int currentThemeIndex, - int numScaleNotes, const int* scaleNotes, int melodySeed, + int numScaleNotes, const int* scaleNotes, int melodySeed, int numSteps, bool mutationEnabled, bool songModeEnabled, const Step sequence[][NUM_STEPS], int playbackStep, bool isPlaying, int randomizeTrack, const bool* trackMute); @@ -25,7 +25,7 @@ public: void updateLeds(const Step sequence[][NUM_STEPS], int playbackStep, bool isPlaying, UIState currentState, bool songModeEnabled, int songRepeatsRemaining, bool sequenceChangeScheduled, PlayMode playMode, - int selectedTrack, int numScaleNotes, const int* scaleNotes, const bool* trackMute); + int selectedTrack, int numSteps, int numScaleNotes, const int* scaleNotes, const bool* trackMute); private: Adafruit_SSD1306 display; @@ -34,7 +34,7 @@ private: void drawMenu(int selection, UIState currentState, int midiChannel, int tempo, const char* flavourName, int queuedTheme, int currentThemeIndex, - int numScaleNotes, const int* scaleNotes, int melodySeed, + int numScaleNotes, const int* scaleNotes, int melodySeed, int numSteps, bool mutationEnabled, bool songModeEnabled, bool isPlaying, int randomizeTrack, const bool* trackMute); uint32_t getNoteColor(int note, bool dim); diff --git a/UIThread.cpp b/UIThread.cpp index 977932d..2c1d6c5 100644 --- a/UIThread.cpp +++ b/UIThread.cpp @@ -36,6 +36,7 @@ void saveSequence(bool quiet) { for(int i=0; i= NUM_STEPS) numSteps = NUM_STEPS; EEPROM.get(addr, numScaleNotes); addr += sizeof(numScaleNotes); for (int i = 0; i<12; i++) { @@ -88,15 +95,17 @@ void factoryReset() { static void generateTrackData(int track, int themeType, Step (*target)[NUM_STEPS]) { randomSeed(melodySeeds[track] + themeType * 12345); - strategies[currentStrategyIndices[track]]->generate(target, track, NUM_STEPS, scaleNotes, numScaleNotes, melodySeeds[track] + themeType * 12345); + strategies[currentStrategyIndices[track]]->generate(target, track, numSteps, scaleNotes, numScaleNotes, melodySeeds[track] + themeType * 12345); } void generateRandomScale() { + Serial.println(F("Generating new scale.")); // All tracks share the same scale for now strategies[currentStrategyIndices[0]]->generateScale(scaleNotes, numScaleNotes); } static void generateSequenceData(int themeType, Step (*target)[NUM_STEPS]) { + Serial.println(F("Generating sequence.")); for(int i=0; imutate(target, i, NUM_STEPS, scaleNotes, numScaleNotes); + for(int i=0; imutate(target, i, numSteps, scaleNotes, numScaleNotes); } static void handleInput() { @@ -156,6 +167,11 @@ static void handleInput() { if (tempo < 40) tempo = 40; if (tempo > 240) tempo = 240; break; + case UI_EDIT_STEPS: + numSteps += delta; + if (numSteps < 1) numSteps = 1; + if (numSteps > NUM_STEPS) numSteps = NUM_STEPS; + break; case UI_EDIT_FLAVOUR: { currentStrategyIndices[randomizeTrack] += (delta > 0 ? 1 : -1); @@ -239,6 +255,7 @@ static void handleInput() { break; case MENU_ID_TEMPO: currentState = UI_EDIT_TEMPO; break; + case MENU_ID_STEPS: currentState = UI_EDIT_STEPS; break; case MENU_ID_SONG_MODE: songModeEnabled = !songModeEnabled; @@ -280,6 +297,10 @@ static void handleInput() { currentState = UI_MENU_MAIN; saveSequence(true); break; + case UI_EDIT_STEPS: + currentState = UI_MENU_MAIN; + saveSequence(true); + break; case UI_EDIT_FLAVOUR: currentState = UI_MENU_MAIN; if (isPlaying) { @@ -325,7 +346,7 @@ static void drawUI() { // to avoid holding the lock during slow display operations. UIState local_currentState; int local_menuSelection, local_randomizeTrack, local_tempo, local_currentThemeIndex, local_queuedTheme, local_numScaleNotes; - int local_melodySeed; + int local_melodySeed, local_numSteps; bool local_mutationEnabled, local_songModeEnabled, local_isPlaying; bool local_trackMute[NUM_TRACKS]; int local_midiChannel; @@ -340,6 +361,7 @@ static void drawUI() { local_menuSelection = menuSelection; local_midiChannel = midiChannels[local_randomizeTrack]; local_tempo = tempo; + local_numSteps = numSteps; local_strategy = strategies[currentStrategyIndices[local_randomizeTrack]]; local_queuedTheme = queuedTheme; local_currentThemeIndex = currentThemeIndex; @@ -356,7 +378,7 @@ static void drawUI() { ui.draw(local_currentState, local_menuSelection, local_midiChannel, local_tempo, local_strategy, - local_queuedTheme, local_currentThemeIndex, local_numScaleNotes, local_scaleNotes, local_melodySeed, + local_queuedTheme, local_currentThemeIndex, local_numScaleNotes, local_scaleNotes, local_melodySeed, local_numSteps, local_mutationEnabled, local_songModeEnabled, (const Step (*)[NUM_STEPS])local_sequence, local_playbackStep, local_isPlaying, local_randomizeTrack, (const bool*)local_trackMute); } @@ -371,7 +393,7 @@ static void updateLeds() { int local_songRepeatsRemaining; bool local_sequenceChangeScheduled; PlayMode local_playMode; - int local_numScaleNotes; + int local_numScaleNotes, local_numSteps; int local_scaleNotes[12]; bool local_trackMute[NUM_TRACKS]; int local_randomizeTrack; @@ -387,6 +409,7 @@ static void updateLeds() { local_sequenceChangeScheduled = sequenceChangeScheduled; local_playMode = playMode; local_numScaleNotes = numScaleNotes; + local_numSteps = numSteps; local_randomizeTrack = randomizeTrack; memcpy(local_scaleNotes, scaleNotes, sizeof(local_scaleNotes)); memcpy(local_trackMute, (const void*)trackMute, sizeof(local_trackMute)); @@ -408,7 +431,7 @@ static void updateLeds() { ui.updateLeds((const Step (*)[NUM_STEPS])local_sequence, local_playbackStep, local_isPlaying, local_currentState, local_songModeEnabled, local_songRepeatsRemaining, - local_sequenceChangeScheduled, ledDisplayMode, local_randomizeTrack, local_numScaleNotes, + local_sequenceChangeScheduled, ledDisplayMode, local_randomizeTrack, local_numSteps, local_numScaleNotes, local_scaleNotes, (const bool*)local_trackMute); } diff --git a/config.h b/config.h index e7fc66f..4290d88 100644 --- a/config.h +++ b/config.h @@ -16,7 +16,7 @@ #define ENC_SW 14 // --- TRACKER DATA --- -#define NUM_STEPS 16 +#define NUM_STEPS 8 #define NUM_TRACKS 4 #endif \ No newline at end of file