Root note and bitmap of scale types
This commit is contained in:
parent
84ef40a555
commit
b6150cbacd
@ -23,6 +23,8 @@ MenuItem menuItems[] = {
|
||||
{ "Playback", MENU_ID_PLAYBACK, false, false, 1 },
|
||||
{ "Melody", MENU_ID_MELODY, false, false, 1 },
|
||||
{ "Scale", MENU_ID_SCALE, false, false, 1 },
|
||||
{ "Root", MENU_ID_ROOT, false, false, 1 },
|
||||
{ "Type", MENU_ID_SCALE_TYPE, 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 },
|
||||
@ -112,7 +114,10 @@ int numScaleNotes = 0;
|
||||
int melodySeeds[NUM_TRACKS];
|
||||
volatile int queuedTheme = -1;
|
||||
volatile int currentThemeIndex = 1;
|
||||
extern const uint32_t EEPROM_MAGIC = 0x4242424E;
|
||||
int currentRoot = 0; // C
|
||||
int currentScaleType = 1; // Major
|
||||
int enabledScaleTypes = 2; // Major (1<<1)
|
||||
extern const uint32_t EEPROM_MAGIC = 0x42424250;
|
||||
|
||||
MelodyStrategy* strategies[] = {
|
||||
new LuckyStrategy(), new ArpStrategy(), new EuclideanStrategy(),
|
||||
|
||||
@ -19,6 +19,8 @@ enum MenuItemID {
|
||||
MENU_ID_PLAYBACK,
|
||||
MENU_ID_MELODY,
|
||||
MENU_ID_SCALE,
|
||||
MENU_ID_ROOT,
|
||||
MENU_ID_SCALE_TYPE,
|
||||
MENU_ID_TEMPO,
|
||||
MENU_ID_SONG_MODE,
|
||||
|
||||
@ -73,6 +75,9 @@ extern int numScaleNotes;
|
||||
extern int melodySeeds[NUM_TRACKS];
|
||||
extern volatile int queuedTheme;
|
||||
extern volatile int currentThemeIndex;
|
||||
extern int currentRoot;
|
||||
extern int currentScaleType;
|
||||
extern int enabledScaleTypes;
|
||||
extern const uint32_t EEPROM_MAGIC;
|
||||
|
||||
extern MelodyStrategy* strategies[];
|
||||
|
||||
@ -26,7 +26,9 @@ enum UIState {
|
||||
UI_RANDOMIZE_TRACK_EDIT,
|
||||
UI_SCALE_EDIT,
|
||||
UI_SCALE_NOTE_EDIT,
|
||||
UI_SCALE_TRANSPOSE
|
||||
UI_SCALE_TRANSPOSE,
|
||||
UI_EDIT_ROOT,
|
||||
UI_EDIT_SCALE_TYPE
|
||||
};
|
||||
|
||||
inline void sortArray(int arr[], int size) {
|
||||
|
||||
@ -110,6 +110,39 @@ void UIManager::draw(UIState currentState, int menuSelection,
|
||||
display.setCursor(0, 50);
|
||||
display.println(F(" (Press to confirm)"));
|
||||
break;
|
||||
case UI_EDIT_ROOT:
|
||||
display.println(F("SET ROOT NOTE"));
|
||||
display.drawLine(0, 8, 128, 8, SSD1306_WHITE);
|
||||
display.setCursor(20, 25);
|
||||
display.setTextSize(2);
|
||||
{
|
||||
const char* noteNames[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};
|
||||
display.print(noteNames[currentRoot % 12]);
|
||||
}
|
||||
display.setTextSize(1);
|
||||
display.setCursor(0, 50);
|
||||
display.println(F(" (Press to confirm)"));
|
||||
break;
|
||||
case UI_EDIT_SCALE_TYPE:
|
||||
display.println(F("ENABLED SCALES"));
|
||||
display.drawLine(0, 8, 128, 8, SSD1306_WHITE);
|
||||
display.setCursor(0, 20);
|
||||
if (menuSelection < 10) {
|
||||
const char* typeNames[] = {"Chrom", "Major", "Minor", "H.Min", "P.Maj", "P.Min", "ChdMaj", "ChdMin", "ChdDim", "Chd7"};
|
||||
display.setTextSize(2);
|
||||
display.print(typeNames[menuSelection]);
|
||||
display.setTextSize(1);
|
||||
display.setCursor(0, 45);
|
||||
bool isEnabled = (enabledScaleTypes & (1 << menuSelection));
|
||||
display.print(isEnabled ? F("[ENABLED]") : F("[DISABLED]"));
|
||||
} else {
|
||||
display.setTextSize(2);
|
||||
display.print(F("Back"));
|
||||
}
|
||||
display.setTextSize(1);
|
||||
display.setCursor(0, 55);
|
||||
display.println(F(" (Press to toggle)"));
|
||||
break;
|
||||
case UI_EDIT_INTENSITY:
|
||||
drawNumberEditor("SET INTENSITY", trackIntensities[randomizeTrack], 1, 10);
|
||||
break;
|
||||
@ -294,6 +327,14 @@ 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_ROOT) {
|
||||
const char* noteNames[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};
|
||||
display.print(F(": ")); display.print(noteNames[currentRoot % 12]);
|
||||
}
|
||||
else if (id == MENU_ID_SCALE_TYPE) {
|
||||
const char* typeNames[] = {"Chrom", "Major", "Minor", "H.Min", "P.Maj", "P.Min", "ChdMaj", "ChdMin", "ChdDim", "Chd7"};
|
||||
display.print(F(": ")); if (currentScaleType >= 0 && currentScaleType < 10) display.print(typeNames[currentScaleType]);
|
||||
}
|
||||
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); }
|
||||
|
||||
112
UIThread.cpp
112
UIThread.cpp
@ -22,6 +22,7 @@ static Step local_sequence[NUM_TRACKS][NUM_STEPS];
|
||||
|
||||
static void handleInput();
|
||||
static int scaleEditSelection = 0;
|
||||
static int scaleTypeEditIndex = 0;
|
||||
static int scaleEditNoteIndex = 0;
|
||||
static void drawUI();
|
||||
static void updateLeds();
|
||||
@ -29,6 +30,8 @@ static void generateTrackData(int track, int themeType, Step (*target)[NUM_STEPS
|
||||
static void generateSequenceData(int themeType, Step (*target)[NUM_STEPS]);
|
||||
static void savePatch(int bank, int slot);
|
||||
static void loadPatch(int bank, int slot);
|
||||
static void updateScale();
|
||||
static void pickRandomScaleType();
|
||||
|
||||
void saveSequence(bool quiet) {
|
||||
midi.lock();
|
||||
@ -50,6 +53,9 @@ void saveSequence(bool quiet) {
|
||||
for(int i=0; i<NUM_TRACKS; i++) steps[i] = numSteps[i];
|
||||
EEPROM.put(addr, steps); addr += sizeof(steps);
|
||||
|
||||
EEPROM.put(addr, currentRoot); addr += sizeof(currentRoot);
|
||||
EEPROM.put(addr, currentScaleType); addr += sizeof(currentScaleType);
|
||||
EEPROM.put(addr, enabledScaleTypes); addr += sizeof(enabledScaleTypes);
|
||||
EEPROM.put(addr, numScaleNotes); addr += sizeof(numScaleNotes);
|
||||
for (int i = 0; i<12; i++) {
|
||||
EEPROM.put(addr, scaleNotes[i]); addr += sizeof(int);
|
||||
@ -107,6 +113,9 @@ bool loadSequence() {
|
||||
}
|
||||
}
|
||||
|
||||
EEPROM.get(addr, currentRoot); addr += sizeof(currentRoot);
|
||||
EEPROM.get(addr, currentScaleType); addr += sizeof(currentScaleType);
|
||||
EEPROM.get(addr, enabledScaleTypes); addr += sizeof(enabledScaleTypes);
|
||||
EEPROM.get(addr, numScaleNotes); addr += sizeof(numScaleNotes);
|
||||
if (numScaleNotes < 0 || numScaleNotes > 12) numScaleNotes = 0;
|
||||
for (int i = 0; i<12; i++) {
|
||||
@ -125,6 +134,9 @@ static void savePatch(int bank, int slot) {
|
||||
int addr = 512 + patchIndex * 256; // Start after main save, 256 bytes per patch
|
||||
|
||||
midi.lock();
|
||||
EEPROM.put(addr, currentRoot); addr += sizeof(currentRoot);
|
||||
EEPROM.put(addr, currentScaleType); addr += sizeof(currentScaleType);
|
||||
EEPROM.put(addr, enabledScaleTypes); addr += sizeof(enabledScaleTypes);
|
||||
EEPROM.put(addr, numScaleNotes); addr += sizeof(numScaleNotes);
|
||||
for (int i = 0; i < 12; i++) {
|
||||
EEPROM.put(addr, scaleNotes[i]); addr += sizeof(int);
|
||||
@ -150,6 +162,9 @@ static void loadPatch(int bank, int slot) {
|
||||
int addr = 512 + patchIndex * 256;
|
||||
|
||||
midi.lock();
|
||||
EEPROM.get(addr, currentRoot); addr += sizeof(currentRoot);
|
||||
EEPROM.get(addr, currentScaleType); addr += sizeof(currentScaleType);
|
||||
EEPROM.get(addr, enabledScaleTypes); addr += sizeof(enabledScaleTypes);
|
||||
EEPROM.get(addr, numScaleNotes); addr += sizeof(numScaleNotes);
|
||||
if (numScaleNotes < 0 || numScaleNotes > 12) numScaleNotes = 0;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
@ -225,6 +240,7 @@ static void generateSequenceData(int themeType, Step (*target)[NUM_STEPS]) {
|
||||
}
|
||||
|
||||
void generateTheme(int themeType) {
|
||||
pickRandomScaleType();
|
||||
generateSequenceData(themeType, local_sequence);
|
||||
|
||||
Serial.println(F("Generating theme."));
|
||||
@ -249,6 +265,67 @@ void mutateSequence(Step (*target)[NUM_STEPS]) {
|
||||
}
|
||||
}
|
||||
|
||||
static void updateScale() {
|
||||
// 0: Chromatic, 1: Major, 2: Minor, 3: Harm Min, 4: Pent Maj, 5: Pent Min, 6: Chord Maj, 7: Chord Min, 8: Chord Dim, 9: Chord 7
|
||||
int intervals[12];
|
||||
int count = 0;
|
||||
|
||||
switch(currentScaleType) {
|
||||
case 0: // Chromatic
|
||||
for(int i=0; i<12; i++) intervals[count++] = i;
|
||||
break;
|
||||
case 1: // Major
|
||||
intervals[0]=0; intervals[1]=2; intervals[2]=4; intervals[3]=5; intervals[4]=7; intervals[5]=9; intervals[6]=11; count=7;
|
||||
break;
|
||||
case 2: // Minor
|
||||
intervals[0]=0; intervals[1]=2; intervals[2]=3; intervals[3]=5; intervals[4]=7; intervals[5]=8; intervals[6]=10; count=7;
|
||||
break;
|
||||
case 3: // Harmonic Minor
|
||||
intervals[0]=0; intervals[1]=2; intervals[2]=3; intervals[3]=5; intervals[4]=7; intervals[5]=8; intervals[6]=11; count=7;
|
||||
break;
|
||||
case 4: // Pentatonic Major
|
||||
intervals[0]=0; intervals[1]=2; intervals[2]=4; intervals[3]=7; intervals[4]=9; count=5;
|
||||
break;
|
||||
case 5: // Pentatonic Minor
|
||||
intervals[0]=0; intervals[1]=3; intervals[2]=5; intervals[3]=7; intervals[4]=10; count=5;
|
||||
break;
|
||||
case 6: // Chord Major
|
||||
intervals[0]=0; intervals[1]=4; intervals[2]=7; count=3;
|
||||
break;
|
||||
case 7: // Chord Minor
|
||||
intervals[0]=0; intervals[1]=3; intervals[2]=7; count=3;
|
||||
break;
|
||||
case 8: // Chord Dim
|
||||
intervals[0]=0; intervals[1]=3; intervals[2]=6; count=3;
|
||||
break;
|
||||
case 9: // Chord 7
|
||||
intervals[0]=0; intervals[1]=4; intervals[2]=7; intervals[3]=10; count=4;
|
||||
break;
|
||||
}
|
||||
|
||||
midi.lock();
|
||||
numScaleNotes = count;
|
||||
for(int i=0; i<count; i++) {
|
||||
scaleNotes[i] = (currentRoot + intervals[i]) % 12;
|
||||
}
|
||||
sortArray(scaleNotes, numScaleNotes);
|
||||
midi.unlock();
|
||||
}
|
||||
|
||||
static void pickRandomScaleType() {
|
||||
int candidates[10];
|
||||
int count = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (enabledScaleTypes & (1 << i)) {
|
||||
candidates[count++] = i;
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
currentScaleType = candidates[random(count)];
|
||||
updateScale();
|
||||
}
|
||||
}
|
||||
|
||||
static void handleInput() {
|
||||
// Handle Encoder Rotation
|
||||
int delta = 0;
|
||||
@ -289,6 +366,21 @@ static void handleInput() {
|
||||
if (numSteps[randomizeTrack] < 1) numSteps[randomizeTrack] = 1;
|
||||
if (numSteps[randomizeTrack] > NUM_STEPS) numSteps[randomizeTrack] = NUM_STEPS;
|
||||
break;
|
||||
case UI_EDIT_ROOT:
|
||||
currentRoot += delta;
|
||||
if (currentRoot < 0) currentRoot = 11;
|
||||
if (currentRoot > 11) currentRoot = 0;
|
||||
updateScale();
|
||||
if (isPlaying) {
|
||||
sequenceChangeScheduled = true;
|
||||
generateSequenceData((queuedTheme != -1) ? queuedTheme : currentThemeIndex, nextSequence);
|
||||
}
|
||||
break;
|
||||
case UI_EDIT_SCALE_TYPE:
|
||||
scaleTypeEditIndex += delta;
|
||||
if (scaleTypeEditIndex < 0) scaleTypeEditIndex = 10;
|
||||
if (scaleTypeEditIndex > 10) scaleTypeEditIndex = 0;
|
||||
break;
|
||||
case UI_EDIT_FLAVOUR:
|
||||
{
|
||||
currentStrategyIndices[randomizeTrack] += (delta > 0 ? 1 : -1);
|
||||
@ -408,6 +500,11 @@ static void handleInput() {
|
||||
scaleEditSelection = 0;
|
||||
break;
|
||||
|
||||
case MENU_ID_ROOT: currentState = UI_EDIT_ROOT; break;
|
||||
case MENU_ID_SCALE_TYPE:
|
||||
currentState = UI_EDIT_SCALE_TYPE;
|
||||
scaleTypeEditIndex = 0;
|
||||
break;
|
||||
case MENU_ID_TEMPO: currentState = UI_EDIT_TEMPO; break;
|
||||
case MENU_ID_STEPS: currentState = UI_EDIT_STEPS; break;
|
||||
|
||||
@ -467,6 +564,19 @@ static void handleInput() {
|
||||
currentState = UI_MENU_MAIN;
|
||||
saveSequence(true);
|
||||
break;
|
||||
case UI_EDIT_ROOT:
|
||||
currentState = UI_MENU_MAIN;
|
||||
saveSequence(true);
|
||||
break;
|
||||
case UI_EDIT_SCALE_TYPE:
|
||||
if (scaleTypeEditIndex == 10) {
|
||||
currentState = UI_MENU_MAIN;
|
||||
saveSequence(true);
|
||||
} else {
|
||||
enabledScaleTypes ^= (1 << scaleTypeEditIndex);
|
||||
if (enabledScaleTypes == 0) enabledScaleTypes = (1 << scaleTypeEditIndex); // Prevent disabling all
|
||||
}
|
||||
break;
|
||||
case UI_EDIT_FLAVOUR:
|
||||
currentState = UI_MENU_MAIN;
|
||||
if (isPlaying) {
|
||||
@ -606,6 +716,8 @@ static void drawUI() {
|
||||
local_currentState = currentState;
|
||||
if (local_currentState == UI_SCALE_EDIT || local_currentState == UI_SCALE_NOTE_EDIT || local_currentState == UI_SCALE_TRANSPOSE) {
|
||||
local_menuSelection = scaleEditSelection;
|
||||
} else if (local_currentState == UI_EDIT_SCALE_TYPE) {
|
||||
local_menuSelection = scaleTypeEditIndex;
|
||||
} else {
|
||||
local_menuSelection = menuSelection;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user