Poly mode

This commit is contained in:
Dejvino
2026-02-18 14:50:03 +01:00
parent 4180bb6a12
commit 5ea0070283
9 changed files with 496 additions and 219 deletions
+251 -104
View File
@@ -8,26 +8,10 @@
#include "EuclideanStrategy.h"
#include "MidiDriver.h"
#include "UIManager.h"
#include "config.h"
// --- HARDWARE CONFIGURATION ---
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C // See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
// Pin Definitions for Raspberry Pi Pico (RP2040)
#define PIN_SDA 4
#define PIN_SCL 5
#define ENC_CLK 12
#define ENC_DT 13
#define ENC_SW 14
// --- TRACKER DATA ---
#define NUM_STEPS 16
Step sequence[NUM_STEPS];
Step nextSequence[NUM_STEPS];
Step sequence[NUM_TRACKS][NUM_STEPS];
Step nextSequence[NUM_TRACKS][NUM_STEPS];
volatile bool sequenceChangeScheduled = false;
volatile bool needsPanic = false;
@@ -35,34 +19,43 @@ UIState currentState = UI_MENU_MAIN;
const char* mainMenu[] = { "Tracker", "Randomize", "Setup" };
const int mainMenuCount = sizeof(mainMenu) / sizeof(char*);
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", "Factory Reset" };
const char* randomizeMenuMono[] = { "Back", "Melody", "Flavour", "Scale", "Tempo", "Mutation", "Song Mode", "Theme 1", "Theme 2", "Theme 3", "Theme 4", "Theme 5", "Theme 6", "Theme 7" };
const int randomizeMenuMonoCount = sizeof(randomizeMenuMono) / sizeof(char*);
const int THEME_1_INDEX_MONO = 7;
const char* randomizeMenuPoly[] = { "Back", "Track", "Mute", "Melody", "Flavour", "Scale", "Tempo", "Mutation", "Song Mode", "Theme 1", "Theme 2", "Theme 3", "Theme 4", "Theme 5", "Theme 6", "Theme 7" };
const int randomizeMenuPolyCount = sizeof(randomizeMenuPoly) / sizeof(char*);
const int THEME_1_INDEX_POLY = 9;
const char* setupMenu[] = { "Back", "Play Mode", "Channel", "Factory Reset" };
const int setupMenuCount = sizeof(setupMenu) / sizeof(char*);
int menuSelection = 0;
volatile int navigationSelection = 1;
volatile int currentTrack = 0;
volatile bool trackMute[NUM_TRACKS];
int randomizeTrack = 0;
volatile int playbackStep = 0;
int midiChannel = 1;
volatile int shMidiChannel = midiChannel;
volatile int midiChannels[NUM_TRACKS];
int scaleNotes[12];
int numScaleNotes = 0;
int melodySeed = 0;
int melodySeeds[NUM_TRACKS];
volatile int queuedTheme = -1;
volatile int currentThemeIndex = 1;
const uint32_t EEPROM_MAGIC = 0x42424247;
const uint32_t EEPROM_MAGIC = 0x4242424A;
MelodyStrategy* strategies[] = { new LuckyStrategy(), new ArpStrategy(), new EuclideanStrategy() };
const int numStrategies = 3;
int currentStrategyIndex = 0;
int currentStrategyIndices[NUM_TRACKS];
volatile PlayMode playMode = MODE_MONO;
volatile bool mutationEnabled = false;
volatile bool songModeEnabled = false;
volatile int songRepeatsRemaining = 0;
volatile int nextSongRepeats = 0;
volatile bool songModeNeedsNext = false;
volatile bool isEditing = false;
volatile EditMode editMode = NAV_STEP;
volatile int scrollOffset = 0;
volatile bool isPlaying = false;
volatile int tempo = 120; // BPM
@@ -70,6 +63,11 @@ volatile unsigned long lastClockTime = 0;
volatile int clockCount = 0;
// Watchdog
volatile unsigned long lastLoop0Time = 0;
volatile unsigned long lastLoop1Time = 0;
volatile bool watchdogActive = false;
// Encoder State
volatile int encoderDelta = 0;
static uint8_t prevNextCode = 0;
@@ -104,10 +102,16 @@ void readEncoder() {
void saveSequence(bool quiet = false) {
int addr = 0;
EEPROM.put(addr, EEPROM_MAGIC); addr += sizeof(EEPROM_MAGIC);
EEPROM.put(addr, midiChannel); addr += sizeof(midiChannel);
EEPROM.put(addr, melodySeed); addr += sizeof(melodySeed);
EEPROM.put(addr, currentStrategyIndex); addr += sizeof(currentStrategyIndex);
int channels[NUM_TRACKS];
for(int i=0; i<NUM_TRACKS; i++) channels[i] = midiChannels[i];
EEPROM.put(addr, channels); addr += sizeof(channels);
EEPROM.put(addr, melodySeeds); addr += sizeof(melodySeeds);
EEPROM.put(addr, currentStrategyIndices); addr += sizeof(currentStrategyIndices);
bool mutes[NUM_TRACKS];
for(int i=0; i<NUM_TRACKS; i++) mutes[i] = trackMute[i];
EEPROM.put(addr, mutes); addr += sizeof(mutes);
EEPROM.put(addr, (int)tempo); addr += sizeof(int);
EEPROM.put(addr, (int)playMode); addr += sizeof(int);
EEPROM.put(addr, numScaleNotes); addr += sizeof(numScaleNotes);
for (int i=0; i<12; i++) {
@@ -115,9 +119,7 @@ void saveSequence(bool quiet = false) {
}
midi.lock();
for (int i=0; i<NUM_STEPS; i++) {
EEPROM.put(addr, sequence[i]); addr += sizeof(Step);
}
EEPROM.put(addr, sequence); addr += sizeof(sequence);
midi.unlock();
EEPROM.commit();
if (!quiet) ui.showMessage("SAVED!");
@@ -129,13 +131,19 @@ bool loadSequence() {
EEPROM.get(addr, magic); addr += sizeof(magic);
if (magic != EEPROM_MAGIC) return false;
EEPROM.get(addr, midiChannel); addr += sizeof(midiChannel);
shMidiChannel = midiChannel;
EEPROM.get(addr, melodySeed); addr += sizeof(melodySeed);
EEPROM.get(addr, currentStrategyIndex); addr += sizeof(currentStrategyIndex);
int channels[NUM_TRACKS];
EEPROM.get(addr, channels); addr += sizeof(channels);
for(int i=0; i<NUM_TRACKS; i++) midiChannels[i] = channels[i];
EEPROM.get(addr, melodySeeds); addr += sizeof(melodySeeds);
EEPROM.get(addr, currentStrategyIndices); addr += sizeof(currentStrategyIndices);
bool mutes[NUM_TRACKS];
EEPROM.get(addr, mutes); addr += sizeof(mutes);
for(int i=0; i<NUM_TRACKS; i++) trackMute[i] = mutes[i];
int t;
EEPROM.get(addr, t); addr += sizeof(int);
tempo = t;
EEPROM.get(addr, t); addr += sizeof(int);
playMode = (PlayMode)t;
EEPROM.get(addr, numScaleNotes); addr += sizeof(numScaleNotes);
for (int i=0; i<12; i++) {
@@ -143,9 +151,7 @@ bool loadSequence() {
}
midi.lock();
for (int i=0; i<NUM_STEPS; i++) {
EEPROM.get(addr, sequence[i]); addr += sizeof(Step);
}
EEPROM.get(addr, sequence); addr += sizeof(sequence);
midi.unlock();
return true;
}
@@ -179,25 +185,42 @@ void setup() {
// 5. Init Sequence
randomSeed(micros());
generateRandomScale();
melodySeed = random(10000);
EEPROM.begin(512);
if (!loadSequence()) {
generateRandomScale();
for(int i=0; i<NUM_TRACKS; i++) {
midiChannels[i] = i + 1;
melodySeeds[i] = random(10000);
currentStrategyIndices[i] = 0;
trackMute[i] = false;
}
generateTheme(1);
}
isPlaying = false; // Don't start playing on boot
Serial.println(F("Started."));
// Enable Watchdog
lastLoop0Time = millis();
lastLoop1Time = millis();
watchdogActive = true;
}
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);
}
void generateRandomScale() {
strategies[currentStrategyIndex]->generateScale(scaleNotes, numScaleNotes);
// All tracks share the same scale for now
strategies[currentStrategyIndices[0]]->generateScale(scaleNotes, numScaleNotes);
}
void generateSequenceData(int themeType, Step* target) {
randomSeed(melodySeed + themeType * 12345); // Deterministic seed for this theme
strategies[currentStrategyIndex]->generate(target, NUM_STEPS, scaleNotes, numScaleNotes, melodySeed + themeType * 12345);
void generateSequenceData(int themeType, Step (*target)[NUM_STEPS]) {
for(int i=0; i<NUM_TRACKS; i++) {
generateTrackData(i, themeType, target);
}
}
void generateTheme(int themeType) {
@@ -214,8 +237,12 @@ void generateTheme(int themeType) {
isPlaying = true;
}
void mutateSequence(Step* target) {
strategies[currentStrategyIndex]->mutate(target, NUM_STEPS, scaleNotes, numScaleNotes);
void mutateSequence(Step (*target)[NUM_STEPS]) {
if (playMode == MODE_POLY) {
for(int i=0; i<NUM_TRACKS; i++) strategies[currentStrategyIndices[i]]->mutate(target, i, NUM_STEPS, scaleNotes, numScaleNotes);
} else {
strategies[currentStrategyIndices[0]]->mutate(target, 0, NUM_STEPS, scaleNotes, numScaleNotes);
}
}
void handleInput() {
@@ -229,14 +256,20 @@ void handleInput() {
if (delta != 0) {
switch(currentState) {
case UI_TRACKER:
if (isEditing && navigationSelection > 0) {
if (editMode == EDIT_NOTE && navigationSelection > 0) {
// Change Note
int stepIndex = navigationSelection - 1;
int newNote = sequence[stepIndex].note + delta;
int newNote = sequence[currentTrack][stepIndex].note + delta;
if (newNote < -1) newNote = -1;
if (newNote > 127) newNote = 127;
midi.lock();
sequence[stepIndex].note = newNote;
sequence[currentTrack][stepIndex].note = newNote;
midi.unlock();
} else if (editMode == NAV_TRACK) {
midi.lock();
currentTrack += (delta > 0 ? 1 : -1);
if(currentTrack < 0) currentTrack = NUM_TRACKS - 1;
if(currentTrack >= NUM_TRACKS) currentTrack = 0;
midi.unlock();
} else {
// Move Cursor
@@ -255,9 +288,12 @@ void handleInput() {
if (menuSelection >= mainMenuCount) menuSelection = 0;
break;
case UI_MENU_RANDOMIZE:
menuSelection += (delta > 0 ? 1 : -1);
if (menuSelection < 0) menuSelection = randomizeMenuCount - 1;
if (menuSelection >= randomizeMenuCount) menuSelection = 0;
{
menuSelection += (delta > 0 ? 1 : -1);
int count = (playMode == MODE_POLY) ? randomizeMenuPolyCount : randomizeMenuMonoCount;
if (menuSelection < 0) menuSelection = count - 1;
if (menuSelection >= count) menuSelection = 0;
}
break;
case UI_MENU_SETUP:
menuSelection += (delta > 0 ? 1 : -1);
@@ -265,10 +301,12 @@ void handleInput() {
if (menuSelection >= setupMenuCount) menuSelection = 0;
break;
case UI_SETUP_CHANNEL_EDIT:
midiChannel += (delta > 0 ? 1 : -1);
if (midiChannel < 1) midiChannel = 16;
if (midiChannel > 16) midiChannel = 1;
shMidiChannel = midiChannel;
{
int trackToEdit = (playMode == MODE_POLY) ? currentTrack : 0;
midiChannels[trackToEdit] += (delta > 0 ? 1 : -1);
if (midiChannels[trackToEdit] < 1) midiChannels[trackToEdit] = 16;
if (midiChannels[trackToEdit] > 16) midiChannels[trackToEdit] = 1;
}
break;
case UI_EDIT_TEMPO:
tempo += delta;
@@ -276,11 +314,25 @@ void handleInput() {
if (tempo > 240) tempo = 240;
break;
case UI_EDIT_FLAVOUR:
currentStrategyIndex += (delta > 0 ? 1 : -1);
if (currentStrategyIndex < 0) currentStrategyIndex = numStrategies - 1;
if (currentStrategyIndex >= numStrategies) currentStrategyIndex = 0;
{
int trackToEdit = playMode == MODE_POLY ? randomizeTrack : 0;
currentStrategyIndices[trackToEdit] += (delta > 0 ? 1 : -1);
if (currentStrategyIndices[trackToEdit] < 0) currentStrategyIndices[trackToEdit] = numStrategies - 1;
if (currentStrategyIndices[trackToEdit] >= numStrategies) currentStrategyIndices[trackToEdit] = 0;
}
break;
case UI_SETUP_PLAYMODE_EDIT:
playMode = (playMode == MODE_MONO) ? MODE_POLY : MODE_MONO;
// Reset edit mode when switching
editMode = NAV_STEP;
break;
}
if (currentState == UI_RANDOMIZE_TRACK_EDIT) {
randomizeTrack += (delta > 0 ? 1 : -1);
if (randomizeTrack < 0) randomizeTrack = NUM_TRACKS - 1;
if (randomizeTrack >= NUM_TRACKS) randomizeTrack = 0;
}
}
// Handle Button
@@ -309,49 +361,72 @@ void handleInput() {
currentState = UI_MENU_MAIN;
menuSelection = 0;
} else { // A step is selected
isEditing = !isEditing;
editMode = (EditMode)((editMode + 1) % 3);
// In mono mode, skip track navigation
if(playMode == MODE_MONO && editMode == NAV_TRACK) {
editMode = EDIT_NOTE;
}
if(editMode == NAV_STEP) { // Cycled back to start
// No action needed
}
}
break;
case UI_MENU_MAIN:
if (menuSelection == 0) currentState = UI_TRACKER;
if (menuSelection == 1) { currentState = UI_MENU_RANDOMIZE; menuSelection = 0; }
if (menuSelection == 2) { currentState = UI_MENU_SETUP; menuSelection = 0; }
if (menuSelection == 0) { currentState = UI_TRACKER; break; }
if (menuSelection == 1) { currentState = UI_MENU_RANDOMIZE; menuSelection = 0; break; }
if (menuSelection == 2) { currentState = UI_MENU_SETUP; menuSelection = 0; break; }
break;
case UI_MENU_RANDOMIZE:
if (menuSelection == 0) { currentState = UI_MENU_MAIN; menuSelection = 1; }
if (menuSelection == 1) {
melodySeed = random(10000); // Melody
{
int track_offset = (playMode == MODE_POLY) ? 2 : 0;
int theme_1_index = (playMode == MODE_POLY) ? THEME_1_INDEX_POLY : THEME_1_INDEX_MONO;
if (menuSelection == 0) { currentState = UI_MENU_MAIN; menuSelection = 1; break; }
if (playMode == MODE_POLY) {
if (menuSelection == 1) { currentState = UI_RANDOMIZE_TRACK_EDIT; break; }
if (menuSelection == 2) { trackMute[randomizeTrack] = !trackMute[randomizeTrack]; break; }
}
if (menuSelection == 1 + track_offset) { // Melody
int track = playMode == MODE_POLY ? randomizeTrack : 0;
midi.lock();
melodySeeds[track] = random(10000);
if (isPlaying) {
int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex;
midi.lock();
generateSequenceData(theme, nextSequence);
if (!sequenceChangeScheduled) {
memcpy(nextSequence, sequence, sizeof(sequence));
}
generateTrackData(track, theme, nextSequence);
sequenceChangeScheduled = true;
midi.unlock();
}
midi.unlock();
saveSequence(true);
break;
}
if (menuSelection == 2) currentState = UI_EDIT_FLAVOUR; // Flavour
if (menuSelection == 3) { // Scale
if (menuSelection == 2 + track_offset) { currentState = UI_EDIT_FLAVOUR; break; } // Flavour
if (menuSelection == 3 + track_offset) { // Scale
generateRandomScale();
if (isPlaying) {
int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex;
midi.lock();
// Regenerate all tracks with new scale
generateSequenceData(theme, nextSequence);
sequenceChangeScheduled = true;
midi.unlock();
}
saveSequence(true);
break;
}
if (menuSelection == 4) currentState = UI_EDIT_TEMPO;
if (menuSelection == 5) mutationEnabled = !mutationEnabled;
if (menuSelection == 6) {
if (menuSelection == 4 + track_offset) { currentState = UI_EDIT_TEMPO; break; }
if (menuSelection == 5 + track_offset) { mutationEnabled = !mutationEnabled; break; }
if (menuSelection == 6 + track_offset) {
songModeEnabled = !songModeEnabled;
if (songModeEnabled) {
songModeNeedsNext = true;
}
break;
}
if (menuSelection >= THEME_1_INDEX) { // Themes
const int selectedTheme = menuSelection - THEME_1_INDEX + 1;
if (menuSelection >= theme_1_index) { // Themes
const int selectedTheme = menuSelection - theme_1_index + 1;
if (isPlaying) {
queuedTheme = selectedTheme;
midi.lock();
@@ -361,12 +436,15 @@ void handleInput() {
} else {
generateTheme(selectedTheme);
}
break;
}
}
break;
case UI_MENU_SETUP:
if (menuSelection == 0) { currentState = UI_MENU_MAIN; menuSelection = 2; }
if (menuSelection == 1) currentState = UI_SETUP_CHANNEL_EDIT;
if (menuSelection == 2) factoryReset();
if (menuSelection == 0) { currentState = UI_MENU_MAIN; menuSelection = 2; break; }
if (menuSelection == 1) { currentState = UI_SETUP_PLAYMODE_EDIT; break; }
if (menuSelection == 2) { currentState = UI_SETUP_CHANNEL_EDIT; break; }
if (menuSelection == 3) { factoryReset(); break; }
break;
case UI_SETUP_CHANNEL_EDIT:
currentState = UI_MENU_SETUP;
@@ -379,14 +457,26 @@ void handleInput() {
case UI_EDIT_FLAVOUR:
currentState = UI_MENU_RANDOMIZE;
if (isPlaying) {
queuedTheme = currentThemeIndex;
int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex;
int track = playMode == MODE_POLY ? randomizeTrack : 0;
midi.lock();
generateSequenceData(currentThemeIndex, nextSequence);
if (!sequenceChangeScheduled) {
memcpy(nextSequence, sequence, sizeof(sequence));
}
generateTrackData(track, theme, nextSequence);
sequenceChangeScheduled = true;
midi.unlock();
}
saveSequence(true);
break;
case UI_SETUP_PLAYMODE_EDIT:
currentState = UI_MENU_SETUP;
saveSequence(true);
break;
case UI_RANDOMIZE_TRACK_EDIT:
currentState = UI_MENU_RANDOMIZE;
saveSequence(true);
break;
}
}
}
@@ -421,6 +511,7 @@ void handlePlayback() {
unsigned long currentMicros = micros();
unsigned long clockInterval = 2500000 / tempo; // 60s * 1000000us / (tempo * 24ppqn)
int tracksToPlay = (playMode == MODE_POLY) ? NUM_TRACKS : 1;
if (currentMicros - lastClockTime >= clockInterval) {
lastClockTime += clockInterval;
@@ -433,16 +524,21 @@ void handlePlayback() {
midi.lock();
// Determine if we are tying to the next note
int nextStep = playbackStep + 1;
if (nextStep >= NUM_STEPS) nextStep = 0;
bool isTied = sequence[playbackStep].tie && (sequence[nextStep].note != -1);
int prevNote = sequence[playbackStep].note;
// Note Off for previous step (if NOT tied)
if (!isTied && prevNote != -1) {
midi.sendNoteOff(prevNote, shMidiChannel);
for(int t=0; t<tracksToPlay; t++) {
int trackChannel = playMode == MODE_POLY ? midiChannels[t] : midiChannels[0];
int nextStep = playbackStep + 1;
if (nextStep >= NUM_STEPS) nextStep = 0;
// Determine if we are tying to the next note
bool isTied = sequence[t][playbackStep].tie && (sequence[t][nextStep].note != -1);
int prevNote = sequence[t][playbackStep].note;
// Note Off for previous step (if NOT tied)
if (!isTied && prevNote != -1) {
midi.sendNoteOff(prevNote, trackChannel);
}
}
playbackStep++;
@@ -465,7 +561,7 @@ void handlePlayback() {
sequenceChangeScheduled = true;
}
midi.panic(shMidiChannel); // Panic / All Notes Off
for (int i=0; i<tracksToPlay; i++) midi.panic(midiChannels[i]);
if (sequenceChangeScheduled) {
memcpy(sequence, nextSequence, sizeof(sequence));
@@ -490,14 +586,20 @@ void handlePlayback() {
}
// Note On for new step
if (sequence[playbackStep].note != -1) {
uint8_t velocity = sequence[playbackStep].accent ? 127 : 100;
midi.sendNoteOn(sequence[playbackStep].note, velocity, shMidiChannel);
}
for(int t=0; t<tracksToPlay; t++) {
int trackChannel = playMode == MODE_POLY ? midiChannels[t] : midiChannels[0];
if (!trackMute[t] && sequence[t][playbackStep].note != -1) {
uint8_t velocity = sequence[t][playbackStep].accent ? 127 : 100;
midi.sendNoteOn(sequence[t][playbackStep].note, velocity, trackChannel);
}
// Note Off for previous step (if tied - delayed Note Off)
if (isTied && prevNote != -1) {
midi.sendNoteOff(prevNote, shMidiChannel);
int prevStep = (playbackStep == 0) ? NUM_STEPS - 1 : playbackStep - 1;
bool wasTied = sequence[t][prevStep].tie && (sequence[t][playbackStep].note != -1);
int prevNote = sequence[t][prevStep].note;
// Note Off for previous step (if tied - delayed Note Off)
if (wasTied && prevNote != -1) {
midi.sendNoteOff(prevNote, trackChannel);
}
}
midi.unlock();
@@ -506,20 +608,58 @@ void handlePlayback() {
void drawUI() {
midi.lock();
ui.draw(currentState, menuSelection, navigationSelection, isEditing, midiChannel, tempo, strategies[currentStrategyIndex], queuedTheme, currentThemeIndex, numScaleNotes, scaleNotes, melodySeed, mutationEnabled, songModeEnabled, sequence, scrollOffset, playbackStep, isPlaying, mainMenu, mainMenuCount, randomizeMenu, randomizeMenuCount, setupMenu, setupMenuCount, THEME_1_INDEX);
const char **randMenu;
int randMenuCount;
int themeIndex;
if (playMode == MODE_POLY) {
randMenu = randomizeMenuPoly;
randMenuCount = randomizeMenuPolyCount;
themeIndex = THEME_1_INDEX_POLY;
} else {
randMenu = randomizeMenuMono;
randMenuCount = randomizeMenuMonoCount;
themeIndex = THEME_1_INDEX_MONO;
}
int ui_track = 0;
if (playMode == MODE_POLY) {
if (currentState == UI_MENU_RANDOMIZE || currentState == UI_EDIT_FLAVOUR || currentState == UI_EDIT_TEMPO || currentState == UI_RANDOMIZE_TRACK_EDIT) {
ui_track = randomizeTrack;
} else { // UI_TRACKER, UI_MENU_SETUP, UI_SETUP_CHANNEL_EDIT etc.
ui_track = currentTrack;
}
}
ui.draw(currentState, menuSelection, navigationSelection, editMode,
midiChannels[ui_track], tempo, strategies[currentStrategyIndices[ui_track]],
queuedTheme, currentThemeIndex, numScaleNotes, scaleNotes, melodySeeds[ui_track],
mutationEnabled, songModeEnabled, sequence, scrollOffset, playbackStep, isPlaying,
mainMenu, mainMenuCount, randMenu, randMenuCount, setupMenu, setupMenuCount,
themeIndex, playMode, currentTrack, randomizeTrack, (const bool*)trackMute);
midi.unlock();
}
void updateLeds() {
midi.lock();
ui.updateLeds(sequence, navigationSelection, playbackStep, isPlaying, currentState, isEditing, songModeEnabled, songRepeatsRemaining, sequenceChangeScheduled);
ui.updateLeds(sequence, navigationSelection, playbackStep, isPlaying, currentState, editMode, songModeEnabled, songRepeatsRemaining, sequenceChangeScheduled, playMode, currentTrack, numScaleNotes, scaleNotes, (const bool*)trackMute);
midi.unlock();
}
void loop1() {
unsigned long now = millis();
lastLoop1Time = now;
if (watchdogActive && (now - lastLoop0Time > 1000)) {
Serial.println("Core 0 Freeze detected");
rp2040.reboot();
}
if (needsPanic) {
midi.lock();
midi.panic(shMidiChannel);
if (playMode == MODE_POLY) {
for (int i=0; i<NUM_TRACKS; i++) midi.panic(midiChannels[i]);
} else {
midi.panic(midiChannels[0]);
}
midi.unlock();
needsPanic = false;
}
@@ -527,6 +667,13 @@ void loop1() {
}
void loop() {
unsigned long now = millis();
lastLoop0Time = now;
if (watchdogActive && (now - lastLoop1Time > 1000)) {
Serial.println("Core 1 Freeze detected");
rp2040.reboot();
}
// Handle Song Mode Generation in UI Thread
if (songModeNeedsNext) {
int nextTheme = random(1, 8); // Themes 1-7