diff --git a/PlaybackThread.cpp b/PlaybackThread.cpp index e4f987f..41f7ffc 100644 --- a/PlaybackThread.cpp +++ b/PlaybackThread.cpp @@ -4,6 +4,7 @@ #include "config.h" #include "SharedState.h" +static int local_numSteps[NUM_TRACKS]; static Step local_sequence[NUM_TRACKS][NUM_STEPS]; static Step local_nextSequence[NUM_TRACKS][NUM_STEPS]; @@ -42,16 +43,24 @@ static void handlePlayback() { midi.lock(); memcpy(local_sequence, sequence, sizeof(local_sequence)); memcpy(local_nextSequence, nextSequence, sizeof(local_nextSequence)); + for (int i = 0; i < NUM_TRACKS; i++) local_numSteps[i] = numSteps[i]; midi.unlock(); + int master_len = 0; + for(int i=0; i master_len) master_len = local_numSteps[i]; + } + if (master_len == 0) master_len = NUM_STEPS; + for(int t=0; t= numSteps) nextStep = 0; + int track_len = local_numSteps[t]; + int current_step = playbackStep % track_len; + int next_step = (playbackStep + 1) % track_len; // Determine if we are tying to the next note - bool isTied = local_sequence[t][playbackStep].tie && (local_sequence[t][nextStep].note != -1); - int prevNote = local_sequence[t][playbackStep].note; + bool isTied = local_sequence[t][current_step].tie && (local_sequence[t][next_step].note != -1); + int prevNote = local_sequence[t][current_step].note; // Note Off for previous step (if NOT tied) if (!isTied && prevNote != -1) { @@ -61,9 +70,8 @@ static void handlePlayback() { playbackStep++; bool justPanicked = false; - if (playbackStep >= numSteps) { - playbackStep = 0; - + // When the global step counter completes a full cycle of the longest track + if ((playbackStep > 0) && ((playbackStep % master_len) == 0)) { for (int i=0; igetName(), queuedTheme, currentThemeIndex, numScaleNotes, scaleNotes, melodySeed, numSteps, mutationEnabled, songModeEnabled, isPlaying, randomizeTrack, trackMute, trackIntensities); + 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")); @@ -95,7 +95,7 @@ void UIManager::draw(UIState currentState, int menuSelection, display.setCursor(20, 25); display.setTextSize(2); display.print(F("LEN: ")); - display.print(numSteps); + display.print(currentTrackNumSteps); display.setTextSize(1); display.setCursor(0, 50); display.println(F(" (Press to confirm)")); @@ -233,8 +233,8 @@ void UIManager::drawNumberEditor(const char* title, int value, int minVal, int m } 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 numSteps, bool mutationEnabled, + int queuedTheme, int currentThemeIndex, int numScaleNotes, + const int* scaleNotes, int melodySeed, int currentTrackNumSteps, bool mutationEnabled, bool songModeEnabled, bool isPlaying, int randomizeTrack, const bool* trackMute, const int* trackIntensities) { // Calculate visual cursor position and scroll offset @@ -294,7 +294,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_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); } else if (id == MENU_ID_MUTE) { display.print(F(": ")); display.print(trackMute[randomizeTrack] ? F("YES") : F("NO")); } @@ -342,7 +342,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 numSteps, int numScaleNotes, const int* scaleNotes, const bool* trackMute) { + 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); @@ -354,7 +354,7 @@ void UIManager::updateLeds(const Step sequence[][NUM_STEPS], int playbackStep, b if(playMode == MODE_POLY) { for(int t=0; t= numSteps) continue; + if (s >= numSteps[t]) continue; int row = t * 2 + (s / NUM_STEPS); int col = s % NUM_STEPS; @@ -366,7 +366,7 @@ void UIManager::updateLeds(const Step sequence[][NUM_STEPS], int playbackStep, b color = getNoteColor(note, !sequence[t][s].accent); } - if (isPlaying && s == playbackStep) { + if (isPlaying && s == (playbackStep % numSteps[t])) { if (trackMute[t]) { color = COLOR_MUTED_PLAYHEAD; } else { @@ -381,12 +381,12 @@ 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; + 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); + 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); diff --git a/UIManager.h b/UIManager.h index f1fa112..2ed7807 100644 --- a/UIManager.h +++ b/UIManager.h @@ -15,9 +15,9 @@ public: void showMessage(const char* msg); void draw(UIState currentState, int menuSelection, - int midiChannel, int tempo, MelodyStrategy* currentStrategy, + int midiChannel, int tempo, MelodyStrategy* currentStrategy, int queuedTheme, int currentThemeIndex, - int numScaleNotes, const int* scaleNotes, int melodySeed, int numSteps, + int numScaleNotes, const int* scaleNotes, int melodySeed, int currentTrackNumSteps, bool mutationEnabled, bool songModeEnabled, const Step sequence[][NUM_STEPS], int playbackStep, bool isPlaying, int randomizeTrack, const bool* trackMute, const int* trackIntensities); @@ -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 numSteps, int numScaleNotes, const int* scaleNotes, const bool* trackMute); + int selectedTrack, const int* numSteps, int numScaleNotes, const int* scaleNotes, const bool* trackMute); private: Adafruit_SSD1306 display; @@ -33,7 +33,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 numSteps, + int numScaleNotes, const int* scaleNotes, int melodySeed, int currentTrackNumSteps, 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); diff --git a/UIThread.cpp b/UIThread.cpp index 105904e..fd238c6 100644 --- a/UIThread.cpp +++ b/UIThread.cpp @@ -44,9 +44,11 @@ void saveSequence(bool quiet) { EEPROM.put(addr, mutes); addr += sizeof(mutes); EEPROM.put(addr, (int)tempo); addr += sizeof(int); int intensities[NUM_TRACKS]; - for(int i=0; i 10) trackIntensity[i] = 10; } - EEPROM.get(addr, t); addr += sizeof(int); - numSteps = t; - if (numSteps <= 0 || numSteps >= NUM_STEPS) numSteps = NUM_STEPS; + int steps[NUM_TRACKS]; + EEPROM.get(addr, steps); addr += sizeof(steps); + for(int i=0; i NUM_STEPS) { + numSteps[i] = NUM_STEPS; + } + } EEPROM.get(addr, numScaleNotes); addr += sizeof(numScaleNotes); if (numScaleNotes < 0 || numScaleNotes > 12) numScaleNotes = 0; @@ -124,7 +131,9 @@ static void savePatch(int bank, int slot) { } EEPROM.put(addr, currentStrategyIndices); addr += sizeof(currentStrategyIndices); EEPROM.put(addr, melodySeeds); addr += sizeof(melodySeeds); - EEPROM.put(addr, (int)numSteps); addr += sizeof(int); + int steps[NUM_TRACKS]; + for(int i=0; i= numStrategies) currentStrategyIndices[i] = 0; } EEPROM.get(addr, melodySeeds); addr += sizeof(melodySeeds); - int t; - EEPROM.get(addr, t); addr += sizeof(int); - numSteps = t; - if (numSteps <= 0 || numSteps >= NUM_STEPS) numSteps = NUM_STEPS; + int steps[NUM_TRACKS]; + EEPROM.get(addr, steps); addr += sizeof(steps); + for(int i=0; i NUM_STEPS) { + numSteps[i] = NUM_STEPS; + } + } bool mutes[NUM_TRACKS]; EEPROM.get(addr, mutes); addr += sizeof(mutes); @@ -195,7 +208,7 @@ void factoryReset() { static void generateTrackData(int track, int themeType, Step (*target)[NUM_STEPS]) { randomSeed(melodySeeds[track] + themeType * 12345); - strategies[currentStrategyIndices[track]]->generate(target, track, numSteps, scaleNotes, numScaleNotes, melodySeeds[track] + themeType * 12345, trackIntensity[track]); + strategies[currentStrategyIndices[track]]->generate(target, track, numSteps[track], scaleNotes, numScaleNotes, melodySeeds[track] + themeType * 12345, trackIntensity[track]); } void generateRandomScale() { @@ -229,7 +242,7 @@ void generateTheme(int themeType) { } void mutateSequence(Step (*target)[NUM_STEPS]) { - for(int i=0; imutate(target, i, numSteps, scaleNotes, numScaleNotes, trackIntensity[i]); + for(int i=0; imutate(target, i, numSteps[i], scaleNotes, numScaleNotes, trackIntensity[i]); } static void handleInput() { @@ -268,9 +281,9 @@ static void handleInput() { if (tempo > 240) tempo = 240; break; case UI_EDIT_STEPS: - numSteps += delta; - if (numSteps < 1) numSteps = 1; - if (numSteps > NUM_STEPS) numSteps = NUM_STEPS; + numSteps[randomizeTrack] += delta; + if (numSteps[randomizeTrack] < 1) numSteps[randomizeTrack] = 1; + if (numSteps[randomizeTrack] > NUM_STEPS) numSteps[randomizeTrack] = NUM_STEPS; break; case UI_EDIT_FLAVOUR: { @@ -570,7 +583,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, local_numSteps; + int local_melodySeed, local_currentTrackNumSteps; bool local_mutationEnabled, local_songModeEnabled, local_isPlaying; bool local_trackMute[NUM_TRACKS]; int local_midiChannel; @@ -590,7 +603,7 @@ static void drawUI() { } local_midiChannel = midiChannels[local_randomizeTrack]; local_tempo = tempo; - local_numSteps = numSteps; + local_currentTrackNumSteps = numSteps[local_randomizeTrack]; local_strategy = strategies[currentStrategyIndices[local_randomizeTrack]]; local_queuedTheme = queuedTheme; local_currentThemeIndex = currentThemeIndex; @@ -608,7 +621,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_numSteps, + local_queuedTheme, local_currentThemeIndex, local_numScaleNotes, local_scaleNotes, local_melodySeed, local_currentTrackNumSteps, local_mutationEnabled, local_songModeEnabled, (const Step (*)[NUM_STEPS])local_sequence, local_playbackStep, local_isPlaying, local_randomizeTrack, (const bool*)local_trackMute, (const int*)local_trackIntensities); } @@ -623,8 +636,8 @@ static void updateLeds() { int local_songRepeatsRemaining; bool local_sequenceChangeScheduled; PlayMode local_playMode; - int local_numScaleNotes, local_numSteps; - int local_scaleNotes[12]; + int local_numScaleNotes; + int local_scaleNotes[12], local_numSteps[NUM_TRACKS]; bool local_trackMute[NUM_TRACKS]; int local_randomizeTrack; @@ -639,7 +652,7 @@ static void updateLeds() { local_sequenceChangeScheduled = sequenceChangeScheduled; local_playMode = playMode; local_numScaleNotes = numScaleNotes; - local_numSteps = numSteps; + for (int i = 0; i < NUM_TRACKS; i++) local_numSteps[i] = numSteps[i]; local_randomizeTrack = randomizeTrack; memcpy(local_scaleNotes, scaleNotes, sizeof(local_scaleNotes)); memcpy(local_trackMute, (const void*)trackMute, sizeof(local_trackMute)); @@ -661,7 +674,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_numSteps, local_numScaleNotes, + local_sequenceChangeScheduled, ledDisplayMode, local_randomizeTrack, local_numSteps, local_numScaleNotes, local_scaleNotes, (const bool*)local_trackMute); }