Compare commits

..

No commits in common. "46ecc57cdebf4da655edb311f739bb4c0aae219f" and "4ec306b8b967308592e1b9af07ed634932bd2d76" have entirely different histories.

9 changed files with 229 additions and 301 deletions

View File

@ -11,11 +11,12 @@ bool wasPlaying = false;
static void handlePlayback() { static void handlePlayback() {
bool nowPlaying = isPlaying; bool nowPlaying = isPlaying;
int tracksToPlay = (playMode == MODE_POLY) ? NUM_TRACKS : 1;
if (!wasPlaying && nowPlaying) { if (!wasPlaying && nowPlaying) {
midi.sendRealtime(0xFA); // MIDI Start midi.sendRealtime(0xFA); // MIDI Start
} else if (wasPlaying && !nowPlaying) { } else if (wasPlaying && !nowPlaying) {
midi.sendRealtime(0xFC); // MIDI Stop midi.sendRealtime(0xFC); // MIDI Stop
for (int i=0; i<NUM_TRACKS; i++) midi.panic(midiChannels[i]); for (int i=0; i<tracksToPlay; i++) midi.panic(midiChannels[i]);
} }
wasPlaying = nowPlaying; wasPlaying = nowPlaying;
@ -44,10 +45,10 @@ static void handlePlayback() {
memcpy(local_nextSequence, nextSequence, sizeof(local_nextSequence)); memcpy(local_nextSequence, nextSequence, sizeof(local_nextSequence));
midi.unlock(); midi.unlock();
for(int t=0; t<NUM_TRACKS; t++) { for(int t=0; t<tracksToPlay; t++) {
int trackChannel = midiChannels[t]; int trackChannel = playMode == MODE_POLY ? midiChannels[t] : midiChannels[0];
int nextStep = playbackStep + 1; int nextStep = playbackStep + 1;
if (nextStep >= numSteps) nextStep = 0; if (nextStep >= NUM_STEPS) nextStep = 0;
// Determine if we are tying to the next note // Determine if we are tying to the next note
bool isTied = local_sequence[t][playbackStep].tie && (local_sequence[t][nextStep].note != -1); bool isTied = local_sequence[t][playbackStep].tie && (local_sequence[t][nextStep].note != -1);
@ -60,7 +61,7 @@ static void handlePlayback() {
} }
playbackStep++; playbackStep++;
if (playbackStep >= numSteps) { if (playbackStep >= NUM_STEPS) {
playbackStep = 0; playbackStep = 0;
// Theme change // Theme change
@ -82,7 +83,7 @@ static void handlePlayback() {
midi.unlock(); midi.unlock();
} }
for (int i=0; i<NUM_TRACKS; i++) midi.panic(midiChannels[i]); for (int i=0; i<tracksToPlay; i++) midi.panic(midiChannels[i]);
if (sequenceChangeScheduled) { if (sequenceChangeScheduled) {
memcpy(local_sequence, local_nextSequence, sizeof(local_sequence)); memcpy(local_sequence, local_nextSequence, sizeof(local_sequence));
@ -110,14 +111,14 @@ static void handlePlayback() {
} }
// Note On for new step // Note On for new step
for(int t=0; t<NUM_TRACKS; t++) { for(int t=0; t<tracksToPlay; t++) {
int trackChannel = midiChannels[t]; int trackChannel = playMode == MODE_POLY ? midiChannels[t] : midiChannels[0];
if (!trackMute[t] && local_sequence[t][playbackStep].note != -1) { if (!trackMute[t] && local_sequence[t][playbackStep].note != -1) {
uint8_t velocity = local_sequence[t][playbackStep].accent ? 127 : 100; uint8_t velocity = local_sequence[t][playbackStep].accent ? 127 : 100;
midi.sendNoteOn(local_sequence[t][playbackStep].note, velocity, trackChannel); midi.sendNoteOn(local_sequence[t][playbackStep].note, velocity, trackChannel);
} }
int prevStep = (playbackStep == 0) ? numSteps - 1 : playbackStep - 1; int prevStep = (playbackStep == 0) ? NUM_STEPS - 1 : playbackStep - 1;
bool wasTied = local_sequence[t][prevStep].tie && (local_sequence[t][playbackStep].note != -1); bool wasTied = local_sequence[t][prevStep].tie && (local_sequence[t][playbackStep].note != -1);
int prevNote = local_sequence[t][prevStep].note; int prevNote = local_sequence[t][prevStep].note;
// Note Off for previous step (if tied - delayed Note Off) // Note Off for previous step (if tied - delayed Note Off)
@ -130,8 +131,10 @@ static void handlePlayback() {
void loopPlayback() { void loopPlayback() {
if (needsPanic) { if (needsPanic) {
for (int i=0; i<NUM_TRACKS; i++) { if (playMode == MODE_POLY) {
midi.panic(midiChannels[i]); for (int i=0; i<NUM_TRACKS; i++) midi.panic(midiChannels[i]);
} else {
midi.panic(midiChannels[0]);
} }
needsPanic = false; needsPanic = false;
} }

View File

@ -58,19 +58,15 @@ void setup() {
// 5. Init Sequence // 5. Init Sequence
randomSeed(micros()); randomSeed(micros());
Serial.println(F("Loading sequence."));
EEPROM.begin(512); EEPROM.begin(512);
if (!loadSequence()) { if (!loadSequence()) {
Serial.println(F("Starting fresh instead.")); generateRandomScale();
numSteps = NUM_STEPS;
for(int i=0; i<NUM_TRACKS; i++) { for(int i=0; i<NUM_TRACKS; i++) {
midiChannels[i] = i + 1; midiChannels[i] = i + 1;
melodySeeds[i] = random(10000); melodySeeds[i] = random(10000);
currentStrategyIndices[i] = 0; currentStrategyIndices[i] = 0;
trackMute[i] = false; trackMute[i] = false;
} }
generateRandomScale();
generateTheme(1); generateTheme(1);
} }
isPlaying = false; // Don't start playing on boot isPlaying = false; // Don't start playing on boot
@ -84,10 +80,6 @@ void setup() {
} }
void loop1() { void loop1() {
if (!watchdogActive) {
delay(100);
return;
}
unsigned long now = millis(); unsigned long now = millis();
lastLoop1Time = now; lastLoop1Time = now;
if (watchdogActive && (now - lastLoop0Time > 1000)) { if (watchdogActive && (now - lastLoop0Time > 1000)) {

View File

@ -12,49 +12,26 @@ Step nextSequence[NUM_TRACKS][NUM_STEPS];
volatile bool sequenceChangeScheduled = false; volatile bool sequenceChangeScheduled = false;
volatile bool needsPanic = false; volatile bool needsPanic = false;
UIState currentState = UI_MENU_MAIN; UIState currentState = UI_MENU_RANDOMIZE;
// Menus // Menus
MenuItem menuItems[] = { const char* mainMenu[] = { "Randomize", "Setup" };
{ "Main", MENU_ID_GROUP_MAIN, true, true, 0 }, extern const int mainMenuCount = sizeof(mainMenu) / sizeof(char*);
{ "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 },
{ "Track", MENU_ID_TRACK_SELECT, false, false, 1 },
{ "Mute", MENU_ID_MUTE, false, false, 1 },
{ "Flavour", MENU_ID_FLAVOUR, false, false, 1 },
{ "Mutation", MENU_ID_MUTATION, false, false, 1 },
{ "Theme 1", MENU_ID_THEME_1, false, false, 1 },
{ "Theme 2", MENU_ID_THEME_2, false, false, 1 },
{ "Theme 3", MENU_ID_THEME_3, false, false, 1 },
{ "Theme 4", MENU_ID_THEME_4, false, false, 1 },
{ "Theme 5", MENU_ID_THEME_5, false, false, 1 },
{ "Theme 6", MENU_ID_THEME_6, false, false, 1 },
{ "Theme 7", MENU_ID_THEME_7, false, false, 1 },
{ "Setup", MENU_ID_GROUP_SETUP, true, false, 0 },
{ "Channel", MENU_ID_CHANNEL, false, false, 1 },
{ "Reset", MENU_ID_RESET, false, false, 1 }
};
extern const int menuItemsCount = sizeof(menuItems) / sizeof(MenuItem);
bool isItemVisible(int index) { const char* randomizeMenuMono[] = { "Setup", "Melody", "Flavour", "Scale", "Tempo", "Mutation", "Song Mode", "Theme 1", "Theme 2", "Theme 3", "Theme 4", "Theme 5", "Theme 6", "Theme 7" };
if (menuItems[index].indentLevel == 0) return true; extern const int randomizeMenuMonoCount = sizeof(randomizeMenuMono) / sizeof(char*);
for (int i = index - 1; i >= 0; i--) { extern const int THEME_1_INDEX_MONO = 7;
if (menuItems[i].indentLevel < menuItems[index].indentLevel) {
return menuItems[i].expanded; const char* randomizeMenuPoly[] = { "Setup", "Track", "Mute", "Melody", "Flavour", "Scale", "Tempo", "Mutation", "Song Mode", "Theme 1", "Theme 2", "Theme 3", "Theme 4", "Theme 5", "Theme 6", "Theme 7" };
} extern const int randomizeMenuPolyCount = sizeof(randomizeMenuPoly) / sizeof(char*);
} extern const int THEME_1_INDEX_POLY = 9;
return true;
} const char* setupMenu[] = { "Back", "Play Mode", "Channel", "Factory Reset" };
extern const int setupMenuCount = sizeof(setupMenu) / sizeof(char*);
int menuSelection = 0; int menuSelection = 0;
volatile bool trackMute[NUM_TRACKS]; volatile bool trackMute[NUM_TRACKS];
int randomizeTrack = 0; int randomizeTrack = 0;
volatile int numSteps = NUM_STEPS;
volatile int playbackStep = 0; volatile int playbackStep = 0;
volatile int midiChannels[NUM_TRACKS]; volatile int midiChannels[NUM_TRACKS];
int scaleNotes[12]; int scaleNotes[12];
@ -62,7 +39,7 @@ int numScaleNotes = 0;
int melodySeeds[NUM_TRACKS]; int melodySeeds[NUM_TRACKS];
volatile int queuedTheme = -1; volatile int queuedTheme = -1;
volatile int currentThemeIndex = 1; volatile int currentThemeIndex = 1;
extern const uint32_t EEPROM_MAGIC = 0x4242424D; extern const uint32_t EEPROM_MAGIC = 0x4242424B;
MelodyStrategy* strategies[] = { MelodyStrategy* strategies[] = {
new LuckyStrategy(), new ArpStrategy(), new EuclideanStrategy(), new LuckyStrategy(), new ArpStrategy(), new EuclideanStrategy(),
@ -70,7 +47,7 @@ MelodyStrategy* strategies[] = {
extern const int numStrategies = 6; extern const int numStrategies = 6;
int currentStrategyIndices[NUM_TRACKS]; int currentStrategyIndices[NUM_TRACKS];
volatile PlayMode playMode = MODE_POLY; volatile PlayMode playMode = MODE_MONO;
volatile bool mutationEnabled = false; volatile bool mutationEnabled = false;
volatile bool songModeEnabled = false; volatile bool songModeEnabled = false;
volatile int songRepeatsRemaining = 0; volatile int songRepeatsRemaining = 0;

View File

@ -14,49 +14,20 @@ extern volatile bool needsPanic;
extern UIState currentState; extern UIState currentState;
// Menus // Menus
enum MenuItemID { extern const char* mainMenu[];
MENU_ID_GROUP_MAIN, extern const int mainMenuCount;
MENU_ID_PLAYBACK, extern const char* randomizeMenuMono[];
MENU_ID_MELODY, extern const int randomizeMenuMonoCount;
MENU_ID_SCALE, extern const int THEME_1_INDEX_MONO;
MENU_ID_STEPS, extern const char* randomizeMenuPoly[];
MENU_ID_TEMPO, extern const int randomizeMenuPolyCount;
MENU_ID_SONG_MODE, extern const int THEME_1_INDEX_POLY;
extern const char* setupMenu[];
MENU_ID_GROUP_TRACK, extern const int setupMenuCount;
MENU_ID_TRACK_SELECT,
MENU_ID_MUTE,
MENU_ID_FLAVOUR,
MENU_ID_MUTATION,
MENU_ID_THEME_1,
MENU_ID_THEME_2,
MENU_ID_THEME_3,
MENU_ID_THEME_4,
MENU_ID_THEME_5,
MENU_ID_THEME_6,
MENU_ID_THEME_7,
MENU_ID_GROUP_SETUP,
MENU_ID_CHANNEL,
MENU_ID_RESET
};
struct MenuItem {
const char* label;
MenuItemID id;
bool isGroup;
bool expanded;
int indentLevel;
};
extern MenuItem menuItems[];
extern const int menuItemsCount;
bool isItemVisible(int index);
extern int menuSelection; extern int menuSelection;
extern volatile bool trackMute[NUM_TRACKS]; extern volatile bool trackMute[NUM_TRACKS];
extern int randomizeTrack; extern int randomizeTrack;
extern volatile int numSteps;
extern volatile int playbackStep; extern volatile int playbackStep;
extern volatile int midiChannels[NUM_TRACKS]; extern volatile int midiChannels[NUM_TRACKS];
extern int scaleNotes[12]; extern int scaleNotes[12];

View File

@ -17,9 +17,10 @@ enum PlayMode {
enum UIState { enum UIState {
UI_MENU_MAIN, UI_MENU_MAIN,
UI_MENU_RANDOMIZE,
UI_MENU_SETUP,
UI_SETUP_CHANNEL_EDIT, UI_SETUP_CHANNEL_EDIT,
UI_EDIT_TEMPO, UI_EDIT_TEMPO,
UI_EDIT_STEPS,
UI_EDIT_FLAVOUR, UI_EDIT_FLAVOUR,
UI_SETUP_PLAYMODE_EDIT, UI_SETUP_PLAYMODE_EDIT,
UI_RANDOMIZE_TRACK_EDIT UI_RANDOMIZE_TRACK_EDIT

View File

@ -1,6 +1,5 @@
#include "UIManager.h" #include "UIManager.h"
#include "config.h" #include "config.h"
#include "SharedState.h"
// --- HARDWARE CONFIGURATION --- // --- HARDWARE CONFIGURATION ---
#define SCREEN_WIDTH 128 #define SCREEN_WIDTH 128
@ -49,10 +48,13 @@ void UIManager::showMessage(const char* msg) {
void UIManager::draw(UIState currentState, int menuSelection, void UIManager::draw(UIState currentState, int menuSelection,
int midiChannel, int tempo, MelodyStrategy* currentStrategy, int midiChannel, int tempo, MelodyStrategy* currentStrategy,
int queuedTheme, int currentThemeIndex, int queuedTheme, int currentThemeIndex,
int numScaleNotes, const int* scaleNotes, int melodySeed, int numSteps, int numScaleNotes, const int* scaleNotes, int melodySeed,
bool mutationEnabled, bool songModeEnabled, bool mutationEnabled, bool songModeEnabled,
const Step sequence[][NUM_STEPS], int playbackStep, bool isPlaying, const Step sequence[][NUM_STEPS], int playbackStep, bool isPlaying,
int randomizeTrack, const bool* trackMute) { const char* mainMenu[], int mainMenuCount,
const char* randomizeMenu[], int randomizeMenuCount,
const char* setupMenu[], int setupMenuCount, int theme1Index,
PlayMode playMode, int randomizeTrack, const bool* trackMute) {
display.clearDisplay(); display.clearDisplay();
display.setTextSize(1); display.setTextSize(1);
@ -61,7 +63,13 @@ void UIManager::draw(UIState currentState, int menuSelection,
switch(currentState) { switch(currentState) {
case UI_MENU_MAIN: case UI_MENU_MAIN:
drawMenu(menuSelection, currentState, midiChannel, tempo, currentStrategy->getName(), queuedTheme, currentThemeIndex, numScaleNotes, scaleNotes, melodySeed, numSteps, mutationEnabled, songModeEnabled, isPlaying, randomizeTrack, trackMute); drawMenu("MAIN MENU", mainMenu, mainMenuCount, menuSelection, currentState, midiChannel, tempo, currentStrategy->getName(), queuedTheme, currentThemeIndex, numScaleNotes, scaleNotes, melodySeed, mutationEnabled, songModeEnabled, theme1Index, playMode, randomizeTrack, trackMute);
break;
case UI_MENU_RANDOMIZE:
drawMenu("PLAY", randomizeMenu, randomizeMenuCount, menuSelection, currentState, midiChannel, tempo, currentStrategy->getName(), queuedTheme, currentThemeIndex, numScaleNotes, scaleNotes, melodySeed, mutationEnabled, songModeEnabled, theme1Index, playMode, randomizeTrack, trackMute);
break;
case UI_MENU_SETUP:
drawMenu("SETUP", setupMenu, setupMenuCount, menuSelection, currentState, midiChannel, tempo, currentStrategy->getName(), queuedTheme, currentThemeIndex, numScaleNotes, scaleNotes, melodySeed, mutationEnabled, songModeEnabled, theme1Index, playMode, randomizeTrack, trackMute);
break; break;
case UI_SETUP_CHANNEL_EDIT: case UI_SETUP_CHANNEL_EDIT:
display.println(F("SET MIDI CHANNEL")); display.println(F("SET MIDI CHANNEL"));
@ -86,17 +94,6 @@ void UIManager::draw(UIState currentState, int menuSelection,
display.setCursor(0, 50); display.setCursor(0, 50);
display.println(F(" (Press to confirm)")); display.println(F(" (Press to confirm)"));
break; 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: case UI_EDIT_FLAVOUR:
display.println(F("SET FLAVOUR")); display.println(F("SET FLAVOUR"));
display.drawLine(0, 8, 128, 8, SSD1306_WHITE); display.drawLine(0, 8, 128, 8, SSD1306_WHITE);
@ -107,6 +104,16 @@ void UIManager::draw(UIState currentState, int menuSelection,
display.setCursor(0, 50); display.setCursor(0, 50);
display.println(F(" (Press to confirm)")); display.println(F(" (Press to confirm)"));
break; break;
case UI_SETUP_PLAYMODE_EDIT:
display.println(F("SET PLAY MODE"));
display.drawLine(0, 8, 128, 8, SSD1306_WHITE);
display.setCursor(20, 25);
display.setTextSize(2);
display.print(playMode == MODE_MONO ? "Mono" : "Poly");
display.setTextSize(1);
display.setCursor(0, 50);
display.println(F(" (Press to confirm)"));
break;
case UI_RANDOMIZE_TRACK_EDIT: case UI_RANDOMIZE_TRACK_EDIT:
display.println(F("SET TRACK")); display.println(F("SET TRACK"));
display.drawLine(0, 8, 128, 8, SSD1306_WHITE); display.drawLine(0, 8, 128, 8, SSD1306_WHITE);
@ -122,59 +129,55 @@ void UIManager::draw(UIState currentState, int menuSelection,
display.display(); display.display();
} }
void UIManager::drawMenu(int selection, UIState currentState, int midiChannel, int tempo, const char* flavourName, void UIManager::drawMenu(const char* title, const char* items[], int count, int selection,
UIState currentState, int midiChannel, int tempo, const char* flavourName,
int queuedTheme, int currentThemeIndex, int numScaleNotes, int queuedTheme, int currentThemeIndex, int numScaleNotes,
const int* scaleNotes, int melodySeed, int numSteps, bool mutationEnabled, const int* scaleNotes, int melodySeed, bool mutationEnabled,
bool songModeEnabled, bool isPlaying, int randomizeTrack, const bool* trackMute) { bool songModeEnabled, int theme1Index, PlayMode playMode, int randomizeTrack, const bool* trackMute) {
display.println(title);
display.drawLine(0, 8, 128, 8, SSD1306_WHITE);
// Calculate visual cursor position and scroll offset int start = 0;
int visualCursor = 0; if (selection >= 5) start = selection - 4;
for(int i=0; i<selection; i++) {
if(isItemVisible(i)) visualCursor++;
}
const int MAX_LINES = 7; // No title, so we have more space int y = 10;
int startVisualIndex = 0; for (int i = start; i < count; i++) {
if (visualCursor >= MAX_LINES) { if (y > 55) break;
startVisualIndex = visualCursor - (MAX_LINES - 1);
}
int currentVisualIndex = 0;
int y = 0;
for (int i = 0; i < menuItemsCount; i++) {
if (!isItemVisible(i)) continue;
if (currentVisualIndex >= startVisualIndex) {
if (y > 55) break;
if (i == selection) { if (i == selection) {
display.fillRect(0, y, 128, 9, SSD1306_WHITE); display.fillRect(0, y, 128, 8, SSD1306_WHITE);
display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); display.setTextColor(SSD1306_BLACK, SSD1306_WHITE);
} else { } else {
display.setTextColor(SSD1306_WHITE); display.setTextColor(SSD1306_WHITE);
} }
display.setCursor(2, y);
display.print(items[i]);
int x = 2 + (menuItems[i].indentLevel * 6); if (currentState == UI_MENU_SETUP && i == 1) {
display.setCursor(x, y + 1); display.print(F(": ")); display.print(playMode == MODE_MONO ? "Mono" : "Poly");
if (menuItems[i].isGroup) {
display.print(menuItems[i].expanded ? F("v ") : F("> "));
} }
if (currentState == UI_MENU_SETUP && i == 2) {
display.print(menuItems[i].label);
MenuItemID id = menuItems[i].id;
if (id == MENU_ID_CHANNEL) {
display.print(F(": ")); display.print(midiChannel); display.print(F(": ")); display.print(midiChannel);
} }
if (currentState == UI_MENU_RANDOMIZE && playMode == MODE_POLY && i == 1) {
// Dynamic values display.print(F(": ")); display.print(randomizeTrack + 1);
if (id == MENU_ID_PLAYBACK) { display.print(F(": ")); display.print(isPlaying ? F("ON") : F("OFF")); } }
else if (id == MENU_ID_MELODY) { if (currentState == UI_MENU_RANDOMIZE && playMode == MODE_POLY && i == 2) {
display.print(F(": ")); display.print(trackMute[randomizeTrack] ? F("YES") : F("NO"));
}
if (currentState == UI_MENU_RANDOMIZE && i >= theme1Index && queuedTheme == (i - theme1Index + 1)) {
display.print(F(" [NEXT]"));
}
if (currentState == UI_MENU_RANDOMIZE && i >= theme1Index && currentThemeIndex == (i - theme1Index + 1)) {
display.print(F(" *"));
}
if (currentState == UI_MENU_RANDOMIZE) {
int track_offset = (playMode == MODE_POLY) ? 2 : 0;
if (i == 1 + track_offset) { // Melody
display.print(F(": ")); display.print(melodySeed); display.print(F(": ")); display.print(melodySeed);
} else if (id == MENU_ID_SCALE) { } else if (i == 2 + track_offset) { // Flavour
display.print(F(": ")); display.print(flavourName);
} else if (i == 3 + track_offset) { // Scale
display.print(F(": ")); display.print(F(": "));
if (numScaleNotes > 0) { if (numScaleNotes > 0) {
const char* noteNames[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; const char* noteNames[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};
@ -183,24 +186,11 @@ void UIManager::drawMenu(int selection, UIState currentState, int midiChannel, i
if (j < min(numScaleNotes, 6) - 1) display.print(F(" ")); if (j < min(numScaleNotes, 6) - 1) display.print(F(" "));
} }
} }
} else if (id == MENU_ID_TEMPO) { display.print(F(": ")); display.print(tempo); } } else if (i == 4 + track_offset) { display.print(F(": ")); display.print(tempo); }
else if (id == MENU_ID_STEPS) { display.print(F(": ")); display.print(numSteps); } else if (i == 5 + track_offset) { display.print(F(": ")); display.print(mutationEnabled ? F("ON") : F("OFF")); }
else if (id == MENU_ID_SONG_MODE) { display.print(F(": ")); display.print(songModeEnabled ? F("ON") : F("OFF")); } else if (i == 6 + track_offset) { 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")); }
else if (id == MENU_ID_FLAVOUR) { display.print(F(": ")); display.print(flavourName); }
else if (id == MENU_ID_MUTATION) { display.print(F(": ")); display.print(mutationEnabled ? F("ON") : F("OFF")); }
if (id >= MENU_ID_THEME_1 && id <= MENU_ID_THEME_7) {
int themeIdx = id - MENU_ID_THEME_1 + 1;
if (queuedTheme == themeIdx) display.print(F(" [NEXT]"));
if (currentThemeIndex == themeIdx) display.print(F(" *"));
}
y += 9;
} }
y += 9;
currentVisualIndex++;
} }
} }
@ -217,7 +207,7 @@ int UIManager::getPixelIndex(int x, int y) {
void UIManager::updateLeds(const Step sequence[][NUM_STEPS], int playbackStep, bool isPlaying, void UIManager::updateLeds(const Step sequence[][NUM_STEPS], int playbackStep, bool isPlaying,
UIState currentState, bool songModeEnabled, UIState currentState, bool songModeEnabled,
int songRepeatsRemaining, bool sequenceChangeScheduled, PlayMode playMode, int songRepeatsRemaining, bool sequenceChangeScheduled, PlayMode playMode,
int selectedTrack, int numSteps, int numScaleNotes, const int* scaleNotes, const bool* trackMute) { int numScaleNotes, const int* scaleNotes, const bool* trackMute) {
pixels.clear(); pixels.clear();
const uint32_t COLOR_PLAYHEAD = pixels.Color(0, 255, 0); const uint32_t COLOR_PLAYHEAD = pixels.Color(0, 255, 0);
@ -229,10 +219,8 @@ void UIManager::updateLeds(const Step sequence[][NUM_STEPS], int playbackStep, b
if(playMode == MODE_POLY) { if(playMode == MODE_POLY) {
for(int t=0; t<NUM_TRACKS; t++) { for(int t=0; t<NUM_TRACKS; t++) {
for(int s=0; s<NUM_STEPS; s++) { for(int s=0; s<NUM_STEPS; s++) {
if (s >= numSteps) continue; int col = t * 2 + (s / 8);
int row = s % 8;
int row = t * 2 + (s / 8);
int col = s % 8;
uint32_t color = 0; uint32_t color = 0;
int note = sequence[t][s].note; int note = sequence[t][s].note;
@ -254,24 +242,21 @@ void UIManager::updateLeds(const Step sequence[][NUM_STEPS], int playbackStep, b
} }
} else { } else {
// --- Mono Mode (original) --- // --- Mono Mode (original) ---
const Step* trackSequence = sequence[selectedTrack];
for (int s = 0; s < NUM_STEPS; s++) { for (int s = 0; s < NUM_STEPS; s++) {
if (s >= numSteps) continue;
int x = s % 8; int x = s % 8;
int yBase = (s / 8) * 4; int yBase = (s / 8) * 4;
uint32_t color = 0, dimColor = 0; uint32_t color = 0, dimColor = 0;
bool isCursorHere = (isPlaying && s == playbackStep); bool isCursorHere = (isPlaying && s == playbackStep);
if (trackSequence[s].note != -1) { if (sequence[0][s].note != -1) {
color = getNoteColor(trackSequence[s].note, trackSequence[s].tie); color = getNoteColor(sequence[0][s].note, sequence[0][s].tie);
dimColor = getNoteColor(trackSequence[s].note, true); dimColor = getNoteColor(sequence[0][s].note, true);
} }
uint32_t c[4] = {0}; uint32_t c[4] = {0};
if (trackSequence[s].note != -1) { if (sequence[0][s].note != -1) {
int octave = trackSequence[s].note / 12; int octave = sequence[0][s].note / 12;
if (octave > 4) { c[0] = color; if (trackSequence[s].accent) c[1] = dimColor; } if (octave > 4) { c[0] = color; if (sequence[0][s].accent) c[1] = dimColor; }
else if (octave < 4) { c[2] = color; if (trackSequence[s].accent) c[1] = dimColor; } else if (octave < 4) { c[2] = color; if (sequence[0][s].accent) c[1] = dimColor; }
else { c[1] = color; if (trackSequence[s].accent) { c[0] = dimColor; c[2] = dimColor; } } else { c[1] = color; if (sequence[0][s].accent) { c[0] = dimColor; c[2] = dimColor; } }
} }
uint32_t cursorColor = pixels.Color(0, 0, 50); uint32_t cursorColor = pixels.Color(0, 0, 50);
if (isPlaying) { if (isPlaying) {

View File

@ -17,25 +17,29 @@ public:
void draw(UIState currentState, int menuSelection, void draw(UIState currentState, int menuSelection,
int midiChannel, int tempo, MelodyStrategy* currentStrategy, int midiChannel, int tempo, MelodyStrategy* currentStrategy,
int queuedTheme, int currentThemeIndex, int queuedTheme, int currentThemeIndex,
int numScaleNotes, const int* scaleNotes, int melodySeed, int numSteps, int numScaleNotes, const int* scaleNotes, int melodySeed,
bool mutationEnabled, bool songModeEnabled, bool mutationEnabled, bool songModeEnabled,
const Step sequence[][NUM_STEPS], int playbackStep, bool isPlaying, const Step sequence[][NUM_STEPS], int playbackStep, bool isPlaying,
int randomizeTrack, const bool* trackMute); const char* mainMenu[], int mainMenuCount,
const char* randomizeMenu[], int randomizeMenuCount,
const char* setupMenu[], int setupMenuCount, int theme1Index,
PlayMode playMode, int randomizeTrack, const bool* trackMute);
void updateLeds(const Step sequence[][NUM_STEPS], int playbackStep, bool isPlaying, void updateLeds(const Step sequence[][NUM_STEPS], int playbackStep, bool isPlaying,
UIState currentState, bool songModeEnabled, UIState currentState, bool songModeEnabled,
int songRepeatsRemaining, bool sequenceChangeScheduled, PlayMode playMode, int songRepeatsRemaining, bool sequenceChangeScheduled, PlayMode playMode,
int selectedTrack, int numSteps, int numScaleNotes, const int* scaleNotes, const bool* trackMute); int numScaleNotes, const int* scaleNotes, const bool* trackMute);
private: private:
Adafruit_SSD1306 display; Adafruit_SSD1306 display;
Adafruit_NeoPixel pixels; Adafruit_NeoPixel pixels;
uint32_t leds_buffer[8][8]; // For piano roll uint32_t leds_buffer[8][8]; // For piano roll
void drawMenu(int selection, UIState currentState, int midiChannel, int tempo, const char* flavourName, void drawMenu(const char* title, const char* items[], int count, int selection,
UIState currentState, int midiChannel, int tempo, const char* flavourName,
int queuedTheme, int currentThemeIndex, int queuedTheme, int currentThemeIndex,
int numScaleNotes, const int* scaleNotes, int melodySeed, int numSteps, int numScaleNotes, const int* scaleNotes, int melodySeed,
bool mutationEnabled, bool songModeEnabled, bool isPlaying, int randomizeTrack, const bool* trackMute); bool mutationEnabled, bool songModeEnabled, int theme1Index, PlayMode playMode, int randomizeTrack, const bool* trackMute);
uint32_t getNoteColor(int note, bool dim); uint32_t getNoteColor(int note, bool dim);
int getPixelIndex(int x, int y); int getPixelIndex(int x, int y);

View File

@ -36,7 +36,7 @@ void saveSequence(bool quiet) {
for(int i=0; i<NUM_TRACKS; i++) mutes[i] = trackMute[i]; for(int i=0; i<NUM_TRACKS; i++) mutes[i] = trackMute[i];
EEPROM.put(addr, mutes); addr += sizeof(mutes); EEPROM.put(addr, mutes); addr += sizeof(mutes);
EEPROM.put(addr, (int)tempo); addr += sizeof(int); EEPROM.put(addr, (int)tempo); addr += sizeof(int);
EEPROM.put(addr, (int)numSteps); addr += sizeof(int); EEPROM.put(addr, (int)playMode); addr += sizeof(int);
EEPROM.put(addr, numScaleNotes); addr += sizeof(numScaleNotes); EEPROM.put(addr, numScaleNotes); addr += sizeof(numScaleNotes);
for (int i = 0; i<12; i++) { for (int i = 0; i<12; i++) {
@ -54,10 +54,7 @@ bool loadSequence() {
int addr = 0; int addr = 0;
uint32_t magic; uint32_t magic;
EEPROM.get(addr, magic); addr += sizeof(magic); EEPROM.get(addr, magic); addr += sizeof(magic);
if (magic != EEPROM_MAGIC) { if (magic != EEPROM_MAGIC) return false;
midi.unlock();
return false;
}
int channels[NUM_TRACKS]; int channels[NUM_TRACKS];
EEPROM.get(addr, channels); addr += sizeof(channels); EEPROM.get(addr, channels); addr += sizeof(channels);
@ -71,8 +68,7 @@ bool loadSequence() {
EEPROM.get(addr, t); addr += sizeof(int); EEPROM.get(addr, t); addr += sizeof(int);
tempo = t; tempo = t;
EEPROM.get(addr, t); addr += sizeof(int); EEPROM.get(addr, t); addr += sizeof(int);
numSteps = t; playMode = (PlayMode)t;
if (numSteps <= 0 || numSteps >= NUM_STEPS) numSteps = NUM_STEPS;
EEPROM.get(addr, numScaleNotes); addr += sizeof(numScaleNotes); EEPROM.get(addr, numScaleNotes); addr += sizeof(numScaleNotes);
for (int i = 0; i<12; i++) { for (int i = 0; i<12; i++) {
@ -95,17 +91,15 @@ void factoryReset() {
static void generateTrackData(int track, int themeType, Step (*target)[NUM_STEPS]) { static void generateTrackData(int track, int themeType, Step (*target)[NUM_STEPS]) {
randomSeed(melodySeeds[track] + themeType * 12345); randomSeed(melodySeeds[track] + themeType * 12345);
strategies[currentStrategyIndices[track]]->generate(target, track, numSteps, scaleNotes, numScaleNotes, melodySeeds[track] + themeType * 12345); strategies[currentStrategyIndices[track]]->generate(target, track, NUM_STEPS, scaleNotes, numScaleNotes, melodySeeds[track] + themeType * 12345);
} }
void generateRandomScale() { void generateRandomScale() {
Serial.println(F("Generating new scale."));
// All tracks share the same scale for now // All tracks share the same scale for now
strategies[currentStrategyIndices[0]]->generateScale(scaleNotes, numScaleNotes); strategies[currentStrategyIndices[0]]->generateScale(scaleNotes, numScaleNotes);
} }
static void generateSequenceData(int themeType, Step (*target)[NUM_STEPS]) { static void generateSequenceData(int themeType, Step (*target)[NUM_STEPS]) {
Serial.println(F("Generating sequence."));
for(int i=0; i<NUM_TRACKS; i++) { for(int i=0; i<NUM_TRACKS; i++) {
generateTrackData(i, themeType, target); generateTrackData(i, themeType, target);
} }
@ -114,13 +108,11 @@ static void generateSequenceData(int themeType, Step (*target)[NUM_STEPS]) {
void generateTheme(int themeType) { void generateTheme(int themeType) {
generateSequenceData(themeType, local_sequence); generateSequenceData(themeType, local_sequence);
Serial.println(F("Generating theme."));
midi.lock(); midi.lock();
memcpy(sequence, local_sequence, sizeof(local_sequence)); memcpy(sequence, local_sequence, sizeof(local_sequence));
needsPanic = true; needsPanic = true;
midi.unlock(); midi.unlock();
Serial.println(F("Theme ready."));
currentThemeIndex = themeType; currentThemeIndex = themeType;
clockCount = 0; clockCount = 0;
lastClockTime = micros(); lastClockTime = micros();
@ -129,7 +121,11 @@ void generateTheme(int themeType) {
} }
void mutateSequence(Step (*target)[NUM_STEPS]) { void mutateSequence(Step (*target)[NUM_STEPS]) {
for(int i=0; i<NUM_TRACKS; i++) strategies[currentStrategyIndices[i]]->mutate(target, i, numSteps, scaleNotes, numScaleNotes); 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);
}
} }
static void handleInput() { static void handleInput() {
@ -143,23 +139,29 @@ static void handleInput() {
if (delta != 0) { if (delta != 0) {
switch(currentState) { switch(currentState) {
case UI_MENU_MAIN: case UI_MENU_MAIN:
menuSelection += (delta > 0 ? 1 : -1);
if (menuSelection < 0) menuSelection = mainMenuCount - 1;
if (menuSelection >= mainMenuCount) menuSelection = 0;
break;
case UI_MENU_RANDOMIZE:
{ {
int next = menuSelection; menuSelection += (delta > 0 ? 1 : -1);
int count = 0; int count = (playMode == MODE_POLY) ? randomizeMenuPolyCount : randomizeMenuMonoCount;
do { if (menuSelection < 0) menuSelection = count - 1;
next += (delta > 0 ? 1 : -1); if (menuSelection >= count) menuSelection = 0;
if (next < 0) next = menuItemsCount - 1;
if (next >= menuItemsCount) next = 0;
count++;
} while (!isItemVisible(next) && count < menuItemsCount);
menuSelection = next;
} }
break; break;
case UI_MENU_SETUP:
menuSelection += (delta > 0 ? 1 : -1);
if (menuSelection < 0) menuSelection = setupMenuCount - 1;
if (menuSelection >= setupMenuCount) menuSelection = 0;
break;
case UI_SETUP_CHANNEL_EDIT: case UI_SETUP_CHANNEL_EDIT:
{ {
midiChannels[randomizeTrack] += (delta > 0 ? 1 : -1); int trackToEdit = (playMode == MODE_POLY) ? randomizeTrack : 0;
if (midiChannels[randomizeTrack] < 1) midiChannels[randomizeTrack] = 16; midiChannels[trackToEdit] += (delta > 0 ? 1 : -1);
if (midiChannels[randomizeTrack] > 16) midiChannels[randomizeTrack] = 1; if (midiChannels[trackToEdit] < 1) midiChannels[trackToEdit] = 16;
if (midiChannels[trackToEdit] > 16) midiChannels[trackToEdit] = 1;
} }
break; break;
case UI_EDIT_TEMPO: case UI_EDIT_TEMPO:
@ -167,18 +169,17 @@ static void handleInput() {
if (tempo < 40) tempo = 40; if (tempo < 40) tempo = 40;
if (tempo > 240) tempo = 240; if (tempo > 240) tempo = 240;
break; break;
case UI_EDIT_STEPS:
numSteps += delta;
if (numSteps < 1) numSteps = 1;
if (numSteps > NUM_STEPS) numSteps = NUM_STEPS;
break;
case UI_EDIT_FLAVOUR: case UI_EDIT_FLAVOUR:
{ {
currentStrategyIndices[randomizeTrack] += (delta > 0 ? 1 : -1); int trackToEdit = playMode == MODE_POLY ? randomizeTrack : 0;
if (currentStrategyIndices[randomizeTrack] < 0) currentStrategyIndices[randomizeTrack] = numStrategies - 1; currentStrategyIndices[trackToEdit] += (delta > 0 ? 1 : -1);
if (currentStrategyIndices[randomizeTrack] >= numStrategies) currentStrategyIndices[randomizeTrack] = 0; if (currentStrategyIndices[trackToEdit] < 0) currentStrategyIndices[trackToEdit] = numStrategies - 1;
if (currentStrategyIndices[trackToEdit] >= numStrategies) currentStrategyIndices[trackToEdit] = 0;
} }
break; break;
case UI_SETUP_PLAYMODE_EDIT:
playMode = (playMode == MODE_MONO) ? MODE_POLY : MODE_MONO;
break;
} }
if (currentState == UI_RANDOMIZE_TRACK_EDIT) { if (currentState == UI_RANDOMIZE_TRACK_EDIT) {
randomizeTrack += (delta > 0 ? 1 : -1); randomizeTrack += (delta > 0 ? 1 : -1);
@ -210,38 +211,37 @@ static void handleInput() {
if (!buttonConsumed) { // Short press action if (!buttonConsumed) { // Short press action
switch(currentState) { switch(currentState) {
case UI_MENU_MAIN: case UI_MENU_MAIN:
if (menuItems[menuSelection].isGroup) { if (menuSelection == 0) { currentState = UI_MENU_RANDOMIZE; menuSelection = 0; break; }
menuItems[menuSelection].expanded = !menuItems[menuSelection].expanded; if (menuSelection == 1) { currentState = UI_MENU_SETUP; menuSelection = 0; break; }
break; break;
case UI_MENU_RANDOMIZE:
{
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_SETUP; menuSelection = 0; break; }
if (playMode == MODE_POLY) {
if (menuSelection == 1) { currentState = UI_RANDOMIZE_TRACK_EDIT; break; }
if (menuSelection == 2) { trackMute[randomizeTrack] = !trackMute[randomizeTrack]; break; }
} }
switch(menuItems[menuSelection].id) { if (menuSelection == 1 + track_offset) { // Melody
case MENU_ID_PLAYBACK: int track = playMode == MODE_POLY ? randomizeTrack : 0;
isPlaying = !isPlaying;
if (isPlaying) {
playbackStep = 0;
clockCount = 0;
lastClockTime = micros();
} else {
queuedTheme = -1;
}
break;
case MENU_ID_MELODY:
midi.lock(); midi.lock();
melodySeeds[randomizeTrack] = random(10000); melodySeeds[track] = random(10000);
if (isPlaying) { if (isPlaying) {
int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex; int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex;
if (!sequenceChangeScheduled) { if (!sequenceChangeScheduled) {
memcpy(nextSequence, sequence, sizeof(sequence)); memcpy(nextSequence, sequence, sizeof(sequence));
} }
generateTrackData(randomizeTrack, theme, nextSequence); generateTrackData(track, theme, nextSequence);
sequenceChangeScheduled = true; sequenceChangeScheduled = true;
} }
midi.unlock(); midi.unlock();
saveSequence(true); saveSequence(true);
break; break;
}
case MENU_ID_SCALE: if (menuSelection == 2 + track_offset) { currentState = UI_EDIT_FLAVOUR; break; } // Flavour
if (menuSelection == 3 + track_offset) { // Scale
generateRandomScale(); generateRandomScale();
if (isPlaying) { if (isPlaying) {
int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex; int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex;
@ -253,28 +253,18 @@ static void handleInput() {
} }
saveSequence(true); saveSequence(true);
break; break;
}
case MENU_ID_TEMPO: currentState = UI_EDIT_TEMPO; break; if (menuSelection == 4 + track_offset) { currentState = UI_EDIT_TEMPO; break; }
case MENU_ID_STEPS: currentState = UI_EDIT_STEPS; break; if (menuSelection == 5 + track_offset) { mutationEnabled = !mutationEnabled; break; }
if (menuSelection == 6 + track_offset) {
case MENU_ID_SONG_MODE:
songModeEnabled = !songModeEnabled; songModeEnabled = !songModeEnabled;
if (songModeEnabled) { if (songModeEnabled) {
songModeNeedsNext = true; songModeNeedsNext = true;
} }
break; break;
}
case MENU_ID_TRACK_SELECT: currentState = UI_RANDOMIZE_TRACK_EDIT; break; if (menuSelection >= theme_1_index) { // Themes
case MENU_ID_MUTE: trackMute[randomizeTrack] = !trackMute[randomizeTrack]; break; const int selectedTheme = menuSelection - theme_1_index + 1;
case MENU_ID_FLAVOUR: currentState = UI_EDIT_FLAVOUR; break;
case MENU_ID_MUTATION: mutationEnabled = !mutationEnabled; break;
case MENU_ID_CHANNEL: currentState = UI_SETUP_CHANNEL_EDIT; break;
case MENU_ID_RESET: factoryReset(); break;
default:
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) { if (isPlaying) {
queuedTheme = selectedTheme; queuedTheme = selectedTheme;
midi.lock(); midi.lock();
@ -286,37 +276,43 @@ static void handleInput() {
} }
break; break;
} }
break;
} }
break; break;
case UI_MENU_SETUP:
if (menuSelection == 0) { currentState = UI_MENU_RANDOMIZE; menuSelection = 0; 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: case UI_SETUP_CHANNEL_EDIT:
currentState = UI_MENU_MAIN; currentState = UI_MENU_SETUP;
saveSequence(true); saveSequence(true);
break; break;
case UI_EDIT_TEMPO: case UI_EDIT_TEMPO:
currentState = UI_MENU_MAIN; currentState = UI_MENU_RANDOMIZE;
saveSequence(true);
break;
case UI_EDIT_STEPS:
currentState = UI_MENU_MAIN;
saveSequence(true); saveSequence(true);
break; break;
case UI_EDIT_FLAVOUR: case UI_EDIT_FLAVOUR:
currentState = UI_MENU_MAIN; currentState = UI_MENU_RANDOMIZE;
if (isPlaying) { if (isPlaying) {
int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex; int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex;
int track = playMode == MODE_POLY ? randomizeTrack : 0;
midi.lock(); midi.lock();
if (!sequenceChangeScheduled) { if (!sequenceChangeScheduled) {
memcpy(nextSequence, sequence, sizeof(sequence)); memcpy(nextSequence, sequence, sizeof(sequence));
} }
generateTrackData(randomizeTrack, theme, nextSequence); generateTrackData(track, theme, nextSequence);
sequenceChangeScheduled = true; sequenceChangeScheduled = true;
midi.unlock(); midi.unlock();
} }
saveSequence(true); saveSequence(true);
break; break;
case UI_SETUP_PLAYMODE_EDIT:
currentState = UI_MENU_SETUP;
saveSequence(true);
break;
case UI_RANDOMIZE_TRACK_EDIT: case UI_RANDOMIZE_TRACK_EDIT:
currentState = UI_MENU_MAIN; currentState = UI_MENU_RANDOMIZE;
saveSequence(true); saveSequence(true);
break; break;
} }
@ -342,11 +338,16 @@ static void handleInput() {
} }
static void drawUI() { static void drawUI() {
const char **randMenu;
int randMenuCount;
int themeIndex;
// Make local copies of shared data inside a critical section // Make local copies of shared data inside a critical section
// to avoid holding the lock during slow display operations. // to avoid holding the lock during slow display operations.
UIState local_currentState; UIState local_currentState;
PlayMode local_playMode;
int local_menuSelection, local_randomizeTrack, local_tempo, local_currentThemeIndex, local_queuedTheme, local_numScaleNotes; int local_menuSelection, local_randomizeTrack, local_tempo, local_currentThemeIndex, local_queuedTheme, local_numScaleNotes;
int local_melodySeed, local_numSteps; int local_melodySeed;
bool local_mutationEnabled, local_songModeEnabled, local_isPlaying; bool local_mutationEnabled, local_songModeEnabled, local_isPlaying;
bool local_trackMute[NUM_TRACKS]; bool local_trackMute[NUM_TRACKS];
int local_midiChannel; int local_midiChannel;
@ -355,19 +356,30 @@ static void drawUI() {
int local_scaleNotes[12]; int local_scaleNotes[12];
midi.lock(); midi.lock();
local_playMode = playMode;
local_randomizeTrack = randomizeTrack; local_randomizeTrack = randomizeTrack;
int ui_track = (local_playMode == MODE_POLY) ? local_randomizeTrack : 0;
if (local_playMode == MODE_POLY) {
randMenu = randomizeMenuPoly;
randMenuCount = randomizeMenuPolyCount;
themeIndex = THEME_1_INDEX_POLY;
} else {
randMenu = randomizeMenuMono;
randMenuCount = randomizeMenuMonoCount;
themeIndex = THEME_1_INDEX_MONO;
}
local_currentState = currentState; local_currentState = currentState;
local_menuSelection = menuSelection; local_menuSelection = menuSelection;
local_midiChannel = midiChannels[local_randomizeTrack]; local_midiChannel = midiChannels[ui_track];
local_tempo = tempo; local_tempo = tempo;
local_numSteps = numSteps; local_strategy = strategies[currentStrategyIndices[ui_track]];
local_strategy = strategies[currentStrategyIndices[local_randomizeTrack]];
local_queuedTheme = queuedTheme; local_queuedTheme = queuedTheme;
local_currentThemeIndex = currentThemeIndex; local_currentThemeIndex = currentThemeIndex;
local_numScaleNotes = numScaleNotes; local_numScaleNotes = numScaleNotes;
memcpy(local_scaleNotes, scaleNotes, sizeof(local_scaleNotes)); memcpy(local_scaleNotes, scaleNotes, sizeof(local_scaleNotes));
local_melodySeed = melodySeeds[local_randomizeTrack]; local_melodySeed = melodySeeds[ui_track];
local_mutationEnabled = mutationEnabled; local_mutationEnabled = mutationEnabled;
local_songModeEnabled = songModeEnabled; local_songModeEnabled = songModeEnabled;
memcpy(local_sequence, sequence, sizeof(local_sequence)); memcpy(local_sequence, sequence, sizeof(local_sequence));
@ -378,8 +390,10 @@ static void drawUI() {
ui.draw(local_currentState, local_menuSelection, ui.draw(local_currentState, local_menuSelection,
local_midiChannel, local_tempo, local_strategy, 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_mutationEnabled, local_songModeEnabled, (const Step (*)[NUM_STEPS])local_sequence, local_playbackStep, local_isPlaying, local_randomizeTrack, (const bool*)local_trackMute); local_mutationEnabled, local_songModeEnabled, (const Step (*)[NUM_STEPS])local_sequence, local_playbackStep, local_isPlaying,
mainMenu, mainMenuCount, randMenu, randMenuCount, setupMenu, setupMenuCount,
themeIndex, local_playMode, local_randomizeTrack, (const bool*)local_trackMute);
} }
static void updateLeds() { static void updateLeds() {
@ -388,50 +402,31 @@ static void updateLeds() {
int local_playbackStep; int local_playbackStep;
bool local_isPlaying; bool local_isPlaying;
UIState local_currentState; UIState local_currentState;
int local_menuSelection;
bool local_songModeEnabled; bool local_songModeEnabled;
int local_songRepeatsRemaining; int local_songRepeatsRemaining;
bool local_sequenceChangeScheduled; bool local_sequenceChangeScheduled;
PlayMode local_playMode; PlayMode local_playMode;
int local_numScaleNotes, local_numSteps; int local_numScaleNotes;
int local_scaleNotes[12]; int local_scaleNotes[12];
bool local_trackMute[NUM_TRACKS]; bool local_trackMute[NUM_TRACKS];
int local_randomizeTrack;
midi.lock(); midi.lock();
memcpy(local_sequence, sequence, sizeof(local_sequence)); memcpy(local_sequence, sequence, sizeof(local_sequence));
local_playbackStep = playbackStep; local_playbackStep = playbackStep;
local_isPlaying = isPlaying; local_isPlaying = isPlaying;
local_currentState = currentState; local_currentState = currentState;
local_menuSelection = menuSelection;
local_songModeEnabled = songModeEnabled; local_songModeEnabled = songModeEnabled;
local_songRepeatsRemaining = songRepeatsRemaining; local_songRepeatsRemaining = songRepeatsRemaining;
local_sequenceChangeScheduled = sequenceChangeScheduled; local_sequenceChangeScheduled = sequenceChangeScheduled;
local_playMode = playMode; local_playMode = playMode;
local_numScaleNotes = numScaleNotes; local_numScaleNotes = numScaleNotes;
local_numSteps = numSteps;
local_randomizeTrack = randomizeTrack;
memcpy(local_scaleNotes, scaleNotes, sizeof(local_scaleNotes)); memcpy(local_scaleNotes, scaleNotes, sizeof(local_scaleNotes));
memcpy(local_trackMute, (const void*)trackMute, sizeof(local_trackMute)); memcpy(local_trackMute, (const void*)trackMute, sizeof(local_trackMute));
midi.unlock(); midi.unlock();
PlayMode ledDisplayMode = MODE_POLY; // Default to POLY (MAIN section view)
if (local_currentState == UI_MENU_MAIN) {
MenuItemID id = menuItems[local_menuSelection].id;
// Check if we are in the Track group (IDs between TRACK_SELECT and THEME_7)
if (id >= MENU_ID_TRACK_SELECT && id <= MENU_ID_THEME_7) {
// It's a TRACK section item (Track, Mute, Flavour, Mutation, Themes)
ledDisplayMode = MODE_MONO;
}
} else if (local_currentState == UI_EDIT_FLAVOUR || local_currentState == UI_RANDOMIZE_TRACK_EDIT) {
// These are entered from TRACK section items
ledDisplayMode = MODE_MONO;
}
ui.updateLeds((const Step (*)[NUM_STEPS])local_sequence, local_playbackStep, local_isPlaying, ui.updateLeds((const Step (*)[NUM_STEPS])local_sequence, local_playbackStep, local_isPlaying,
local_currentState, local_songModeEnabled, local_songRepeatsRemaining, local_currentState, local_songModeEnabled, local_songRepeatsRemaining,
local_sequenceChangeScheduled, ledDisplayMode, local_randomizeTrack, local_numSteps, local_numScaleNotes, local_sequenceChangeScheduled, local_playMode, local_numScaleNotes,
local_scaleNotes, (const bool*)local_trackMute); local_scaleNotes, (const bool*)local_trackMute);
} }

View File

@ -16,7 +16,7 @@
#define ENC_SW 14 #define ENC_SW 14
// --- TRACKER DATA --- // --- TRACKER DATA ---
#define NUM_STEPS 8 #define NUM_STEPS 16
#define NUM_TRACKS 4 #define NUM_TRACKS 4
#endif #endif