Scale editor
This commit is contained in:
parent
36d3fd2e80
commit
4a3f2b8eb2
@ -22,7 +22,10 @@ enum UIState {
|
|||||||
UI_EDIT_STEPS,
|
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,
|
||||||
|
UI_SCALE_EDIT,
|
||||||
|
UI_SCALE_NOTE_EDIT,
|
||||||
|
UI_SCALE_TRANSPOSE
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void sortArray(int arr[], int size) {
|
inline void sortArray(int arr[], int size) {
|
||||||
|
|||||||
@ -119,6 +119,85 @@ 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_SCALE_EDIT:
|
||||||
|
case UI_SCALE_NOTE_EDIT:
|
||||||
|
case UI_SCALE_TRANSPOSE:
|
||||||
|
display.println(F("EDIT SCALE"));
|
||||||
|
display.drawLine(0, 8, 128, 8, SSD1306_WHITE);
|
||||||
|
|
||||||
|
int totalItems = numScaleNotes + 5; // Back + Randomize + Notes + Add + Remove + Transpose
|
||||||
|
int startIdx = 0;
|
||||||
|
if (menuSelection >= 4) startIdx = menuSelection - 3;
|
||||||
|
|
||||||
|
int y = 12;
|
||||||
|
for (int i = startIdx; i < totalItems; i++) {
|
||||||
|
if (y > 54) break;
|
||||||
|
|
||||||
|
if (i == menuSelection) {
|
||||||
|
display.fillRect(0, y, 75, 9, SSD1306_WHITE);
|
||||||
|
display.setTextColor(SSD1306_BLACK, SSD1306_WHITE);
|
||||||
|
} else {
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
}
|
||||||
|
display.setCursor(2, y + 1);
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
display.print(F("Back"));
|
||||||
|
} else if (i == 1) {
|
||||||
|
display.print(F("Randomize"));
|
||||||
|
} else if (i <= numScaleNotes + 1) {
|
||||||
|
int noteIdx = i - 2;
|
||||||
|
const char* noteNames[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};
|
||||||
|
display.print(noteNames[scaleNotes[noteIdx]]);
|
||||||
|
if (currentState == UI_SCALE_NOTE_EDIT && i == menuSelection) {
|
||||||
|
display.print(F(" <"));
|
||||||
|
}
|
||||||
|
} else if (i == numScaleNotes + 2) {
|
||||||
|
display.print(F("Transpose"));
|
||||||
|
if (currentState == UI_SCALE_TRANSPOSE) {
|
||||||
|
display.print(F(" < >"));
|
||||||
|
}
|
||||||
|
} else if (i == numScaleNotes + 3) {
|
||||||
|
display.print(F("Add Note"));
|
||||||
|
} else if (i == numScaleNotes + 4) {
|
||||||
|
display.print(F("Remove Note"));
|
||||||
|
}
|
||||||
|
y += 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Piano Roll Preview
|
||||||
|
int px = 82;
|
||||||
|
int py = 20;
|
||||||
|
int wk_w = 5;
|
||||||
|
int wk_h = 20;
|
||||||
|
int bk_w = 4;
|
||||||
|
int bk_h = 12;
|
||||||
|
|
||||||
|
// White keys: C, D, E, F, G, A, B
|
||||||
|
int whiteNotes[] = {0, 2, 4, 5, 7, 9, 11};
|
||||||
|
for (int k = 0; k < 7; k++) {
|
||||||
|
bool active = false;
|
||||||
|
for (int j = 0; j < numScaleNotes; j++) {
|
||||||
|
if (scaleNotes[j] == whiteNotes[k]) { active = true; break; }
|
||||||
|
}
|
||||||
|
if (active) display.fillRect(px + k*6, py, wk_w, wk_h, SSD1306_WHITE);
|
||||||
|
else display.drawRect(px + k*6, py, wk_w, wk_h, SSD1306_WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Black keys: C#, D#, F#, G#, A#
|
||||||
|
int blackNotes[] = {1, 3, 6, 8, 10};
|
||||||
|
int blackOffsets[] = {3, 9, 21, 27, 33};
|
||||||
|
for (int k = 0; k < 5; k++) {
|
||||||
|
bool active = false;
|
||||||
|
for (int j = 0; j < numScaleNotes; j++) {
|
||||||
|
if (scaleNotes[j] == blackNotes[k]) { active = true; break; }
|
||||||
|
}
|
||||||
|
int bx = px + blackOffsets[k];
|
||||||
|
display.fillRect(bx - 1, py - 1, bk_w + 2, bk_h + 2, SSD1306_BLACK);
|
||||||
|
if (active) display.fillRect(bx, py, bk_w, bk_h, SSD1306_WHITE);
|
||||||
|
else display.drawRect(bx, py, bk_w, bk_h, SSD1306_WHITE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
display.display();
|
display.display();
|
||||||
}
|
}
|
||||||
|
|||||||
119
UIThread.cpp
119
UIThread.cpp
@ -18,6 +18,8 @@
|
|||||||
static Step local_sequence[NUM_TRACKS][NUM_STEPS];
|
static Step local_sequence[NUM_TRACKS][NUM_STEPS];
|
||||||
|
|
||||||
static void handleInput();
|
static void handleInput();
|
||||||
|
static int scaleEditSelection = 0;
|
||||||
|
static int scaleEditNoteIndex = 0;
|
||||||
static void drawUI();
|
static void drawUI();
|
||||||
static void updateLeds();
|
static void updateLeds();
|
||||||
static void generateTrackData(int track, int themeType, Step (*target)[NUM_STEPS]);
|
static void generateTrackData(int track, int themeType, Step (*target)[NUM_STEPS]);
|
||||||
@ -179,6 +181,38 @@ static void handleInput() {
|
|||||||
if (currentStrategyIndices[randomizeTrack] >= numStrategies) currentStrategyIndices[randomizeTrack] = 0;
|
if (currentStrategyIndices[randomizeTrack] >= numStrategies) currentStrategyIndices[randomizeTrack] = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case UI_SCALE_EDIT:
|
||||||
|
scaleEditSelection += (delta > 0 ? 1 : -1);
|
||||||
|
if (scaleEditSelection < 0) scaleEditSelection = numScaleNotes + 4;
|
||||||
|
if (scaleEditSelection > numScaleNotes + 4) scaleEditSelection = 0;
|
||||||
|
break;
|
||||||
|
case UI_SCALE_NOTE_EDIT:
|
||||||
|
scaleNotes[scaleEditNoteIndex] += (delta > 0 ? 1 : -1);
|
||||||
|
if (scaleNotes[scaleEditNoteIndex] < 0) scaleNotes[scaleEditNoteIndex] = 11;
|
||||||
|
if (scaleNotes[scaleEditNoteIndex] > 11) scaleNotes[scaleEditNoteIndex] = 0;
|
||||||
|
midi.lock();
|
||||||
|
midi.sendNoteOn(60 + scaleNotes[scaleEditNoteIndex], 100, midiChannels[randomizeTrack]);
|
||||||
|
midi.unlock();
|
||||||
|
delay(50);
|
||||||
|
midi.lock();
|
||||||
|
midi.sendNoteOff(60 + scaleNotes[scaleEditNoteIndex], midiChannels[randomizeTrack]);
|
||||||
|
midi.unlock();
|
||||||
|
break;
|
||||||
|
case UI_SCALE_TRANSPOSE:
|
||||||
|
if (delta != 0) {
|
||||||
|
int shift = delta % 12;
|
||||||
|
if (shift < 0) shift += 12;
|
||||||
|
for(int i=0; i<numScaleNotes; i++) scaleNotes[i] = (scaleNotes[i] + shift) % 12;
|
||||||
|
sortArray(scaleNotes, numScaleNotes);
|
||||||
|
if (isPlaying) {
|
||||||
|
int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex;
|
||||||
|
midi.lock();
|
||||||
|
generateSequenceData(theme, nextSequence);
|
||||||
|
sequenceChangeScheduled = true;
|
||||||
|
midi.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (currentState == UI_RANDOMIZE_TRACK_EDIT) {
|
if (currentState == UI_RANDOMIZE_TRACK_EDIT) {
|
||||||
randomizeTrack += (delta > 0 ? 1 : -1);
|
randomizeTrack += (delta > 0 ? 1 : -1);
|
||||||
@ -242,16 +276,8 @@ static void handleInput() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case MENU_ID_SCALE:
|
case MENU_ID_SCALE:
|
||||||
generateRandomScale();
|
currentState = UI_SCALE_EDIT;
|
||||||
if (isPlaying) {
|
scaleEditSelection = 0;
|
||||||
int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex;
|
|
||||||
midi.lock();
|
|
||||||
// Regenerate all tracks with new scale
|
|
||||||
generateSequenceData(theme, nextSequence);
|
|
||||||
sequenceChangeScheduled = true;
|
|
||||||
midi.unlock();
|
|
||||||
}
|
|
||||||
saveSequence(true);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MENU_ID_TEMPO: currentState = UI_EDIT_TEMPO; break;
|
case MENU_ID_TEMPO: currentState = UI_EDIT_TEMPO; break;
|
||||||
@ -319,6 +345,71 @@ static void handleInput() {
|
|||||||
currentState = UI_MENU_MAIN;
|
currentState = UI_MENU_MAIN;
|
||||||
saveSequence(true);
|
saveSequence(true);
|
||||||
break;
|
break;
|
||||||
|
case UI_SCALE_EDIT:
|
||||||
|
if (scaleEditSelection == 0) {
|
||||||
|
currentState = UI_MENU_MAIN;
|
||||||
|
saveSequence(true);
|
||||||
|
} else if (scaleEditSelection == 1) {
|
||||||
|
generateRandomScale();
|
||||||
|
if (isPlaying) {
|
||||||
|
int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex;
|
||||||
|
midi.lock();
|
||||||
|
generateSequenceData(theme, nextSequence);
|
||||||
|
sequenceChangeScheduled = true;
|
||||||
|
midi.unlock();
|
||||||
|
}
|
||||||
|
saveSequence(true);
|
||||||
|
} else if (scaleEditSelection <= numScaleNotes + 1) {
|
||||||
|
scaleEditNoteIndex = scaleEditSelection - 2;
|
||||||
|
currentState = UI_SCALE_NOTE_EDIT;
|
||||||
|
} else if (scaleEditSelection == numScaleNotes + 2) {
|
||||||
|
currentState = UI_SCALE_TRANSPOSE;
|
||||||
|
} else if (scaleEditSelection == numScaleNotes + 3) {
|
||||||
|
if (numScaleNotes < 12) {
|
||||||
|
int next = (numScaleNotes > 0) ? (scaleNotes[numScaleNotes-1] + 1) % 12 : 0;
|
||||||
|
scaleNotes[numScaleNotes] = next;
|
||||||
|
numScaleNotes++;
|
||||||
|
scaleEditSelection--; // Move cursor to the new note
|
||||||
|
if (isPlaying) {
|
||||||
|
int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex;
|
||||||
|
midi.lock();
|
||||||
|
generateSequenceData(theme, nextSequence);
|
||||||
|
sequenceChangeScheduled = true;
|
||||||
|
midi.unlock();
|
||||||
|
}
|
||||||
|
saveSequence(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (numScaleNotes > 1) {
|
||||||
|
numScaleNotes--;
|
||||||
|
scaleEditSelection--;
|
||||||
|
if (isPlaying) {
|
||||||
|
int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex;
|
||||||
|
midi.lock();
|
||||||
|
generateSequenceData(theme, nextSequence);
|
||||||
|
sequenceChangeScheduled = true;
|
||||||
|
midi.unlock();
|
||||||
|
}
|
||||||
|
saveSequence(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case UI_SCALE_NOTE_EDIT:
|
||||||
|
sortArray(scaleNotes, numScaleNotes);
|
||||||
|
if (isPlaying) {
|
||||||
|
int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex;
|
||||||
|
midi.lock();
|
||||||
|
generateSequenceData(theme, nextSequence);
|
||||||
|
sequenceChangeScheduled = true;
|
||||||
|
midi.unlock();
|
||||||
|
}
|
||||||
|
currentState = UI_SCALE_EDIT;
|
||||||
|
saveSequence(true);
|
||||||
|
break;
|
||||||
|
case UI_SCALE_TRANSPOSE:
|
||||||
|
currentState = UI_SCALE_EDIT;
|
||||||
|
saveSequence(true);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -358,7 +449,11 @@ static void drawUI() {
|
|||||||
local_randomizeTrack = randomizeTrack;
|
local_randomizeTrack = randomizeTrack;
|
||||||
|
|
||||||
local_currentState = currentState;
|
local_currentState = currentState;
|
||||||
local_menuSelection = menuSelection;
|
if (local_currentState == UI_SCALE_EDIT || local_currentState == UI_SCALE_NOTE_EDIT || local_currentState == UI_SCALE_TRANSPOSE) {
|
||||||
|
local_menuSelection = scaleEditSelection;
|
||||||
|
} else {
|
||||||
|
local_menuSelection = menuSelection;
|
||||||
|
}
|
||||||
local_midiChannel = midiChannels[local_randomizeTrack];
|
local_midiChannel = midiChannels[local_randomizeTrack];
|
||||||
local_tempo = tempo;
|
local_tempo = tempo;
|
||||||
local_numSteps = numSteps;
|
local_numSteps = numSteps;
|
||||||
@ -424,7 +519,7 @@ static void updateLeds() {
|
|||||||
// It's a TRACK section item (Track, Mute, Flavour, Mutation, Themes)
|
// It's a TRACK section item (Track, Mute, Flavour, Mutation, Themes)
|
||||||
ledDisplayMode = MODE_MONO;
|
ledDisplayMode = MODE_MONO;
|
||||||
}
|
}
|
||||||
} else if (local_currentState == UI_EDIT_FLAVOUR || local_currentState == UI_RANDOMIZE_TRACK_EDIT) {
|
} else if (local_currentState == UI_EDIT_FLAVOUR || local_currentState == UI_RANDOMIZE_TRACK_EDIT || local_currentState == UI_SCALE_EDIT || local_currentState == UI_SCALE_NOTE_EDIT || local_currentState == UI_SCALE_TRANSPOSE) {
|
||||||
// These are entered from TRACK section items
|
// These are entered from TRACK section items
|
||||||
ledDisplayMode = MODE_MONO;
|
ledDisplayMode = MODE_MONO;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user