diff --git a/Persistence.cpp b/Persistence.cpp new file mode 100644 index 0000000..ff4cc5b --- /dev/null +++ b/Persistence.cpp @@ -0,0 +1,195 @@ +#include "Persistence.h" +#include "SharedState.h" +#include "MidiDriver.h" +#include "UIManager.h" +#include "SequenceGenerator.h" +#include +#include + +void Persistence::saveSequence(bool quiet) { + midi.lock(); + int addr = 0; + EEPROM.put(addr, EEPROM_MAGIC); addr += sizeof(EEPROM_MAGIC); + int channels[NUM_TRACKS]; + for(int i=0; i 16) midiChannels[i] = 16; + } + EEPROM.get(addr, melodySeeds); addr += sizeof(melodySeeds); + EEPROM.get(addr, currentStrategyIndices); addr += sizeof(currentStrategyIndices); + for(int i=0; i= numStrategies) currentStrategyIndices[i] = 0; + } + bool mutes[NUM_TRACKS]; + EEPROM.get(addr, mutes); addr += sizeof(mutes); + for(int i=0; i 240) tempo = 240; + int intensities[NUM_TRACKS]; + EEPROM.get(addr, intensities); addr += sizeof(intensities); + for(int i=0; i 10) trackIntensity[i] = 10; + } + 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, 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++) { + EEPROM.get(addr, scaleNotes[i]); addr += sizeof(int); + if (scaleNotes[i] < 0) scaleNotes[i] = 0; + if (scaleNotes[i] > 11) scaleNotes[i] = 11; + } + + EEPROM.get(addr, sequence); addr += sizeof(sequence); + midi.unlock(); + return true; +} + +void Persistence::savePatch(int bank, int slot) { + int patchIndex = bank * 4 + 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); + } + EEPROM.put(addr, currentStrategyIndices); addr += sizeof(currentStrategyIndices); + EEPROM.put(addr, melodySeeds); addr += sizeof(melodySeeds); + int steps[NUM_TRACKS]; + for(int i=0; i 12) numScaleNotes = 0; + for (int i = 0; i < 12; i++) { + EEPROM.get(addr, scaleNotes[i]); addr += sizeof(int); + if (scaleNotes[i] < 0) scaleNotes[i] = 0; + if (scaleNotes[i] > 11) scaleNotes[i] = 11; + } + EEPROM.get(addr, currentStrategyIndices); addr += sizeof(currentStrategyIndices); + for(int i=0; i= numStrategies) currentStrategyIndices[i] = 0; + } + EEPROM.get(addr, melodySeeds); addr += sizeof(melodySeeds); + 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); + for(int i=0; i 10) trackIntensity[i] = 10; + } + + if (isPlaying) { + SequenceGenerator::generateSequenceData(currentThemeIndex, nextSequence); + sequenceChangeScheduled = true; + } else { + SequenceGenerator::generateSequenceData(currentThemeIndex, scratchBuffer); + memcpy(sequence, scratchBuffer, sizeof(sequence)); + } + midi.unlock(); + ui.showMessage("LOADED!"); +} + +void Persistence::factoryReset() { + ui.showMessage("RESETTING..."); + for(int i=0; i + +void SequenceGenerator::generateTrackData(int track, int themeType, Step (*target)[NUM_STEPS]) { + randomSeed(melodySeeds[track] + themeType * 12345); + strategies[currentStrategyIndices[track]]->generate(target, track, numSteps[track], scaleNotes, numScaleNotes, melodySeeds[track] + themeType * 12345, trackIntensity[track]); +} + +void SequenceGenerator::generateSequenceData(int themeType, Step (*target)[NUM_STEPS]) { + Serial.println(F("Generating sequence.")); + for(int i=0; imutate(target, i, numSteps[i], scaleNotes, numScaleNotes, trackIntensity[i]); + } + } +} + +void SequenceGenerator::generateRandomScale() { + Serial.println(F("Generating new scale.")); + SequenceGenerator::updateScale(); +} + +void SequenceGenerator::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 0) { + currentScaleType = candidates[random(count)]; + SequenceGenerator::updateScale(); + } +} diff --git a/SequenceGenerator.h b/SequenceGenerator.h new file mode 100644 index 0000000..26efb02 --- /dev/null +++ b/SequenceGenerator.h @@ -0,0 +1,17 @@ +#ifndef SEQUENCE_GENERATOR_H +#define SEQUENCE_GENERATOR_H + +#include "TrackerTypes.h" +#include "config.h" + +class SequenceGenerator { +public: + static void generateTrackData(int track, int themeType, Step (*target)[NUM_STEPS]); + static void generateSequenceData(int themeType, Step (*target)[NUM_STEPS]); + static void mutateSequence(Step (*target)[NUM_STEPS]); + static void generateRandomScale(); + static void updateScale(); + static void pickRandomScaleType(); +}; + +#endif diff --git a/SharedState.h b/SharedState.h index 0e962d3..144e172 100644 --- a/SharedState.h +++ b/SharedState.h @@ -76,6 +76,7 @@ extern int melodySeeds[NUM_TRACKS]; extern volatile int queuedTheme; extern volatile int currentThemeIndex; extern int currentRoot; +extern volatile int trackIntensity[NUM_TRACKS]; extern int currentScaleType; extern int enabledScaleTypes; extern const uint32_t EEPROM_MAGIC; diff --git a/UIThread.cpp b/UIThread.cpp index 338a015..4332938 100644 --- a/UIThread.cpp +++ b/UIThread.cpp @@ -1,22 +1,13 @@ #include #include -#include #include "TrackerTypes.h" -#include "MelodyStrategy.h" -#include "LuckyStrategy.h" -#include "ArpStrategy.h" -#include "EuclideanStrategy.h" -#include "MarkovStrategy.h" -#include "CellularAutomataStrategy.h" -#include "LSystemStrategy.h" -#include "DroneStrategy.h" #include "MidiDriver.h" #include "UIManager.h" #include "config.h" #include "UIThread.h" #include "SharedState.h" - -extern volatile int trackIntensity[NUM_TRACKS]; +#include "SequenceGenerator.h" +#include "Persistence.h" static Step local_sequence[NUM_TRACKS][NUM_STEPS]; @@ -26,222 +17,26 @@ static int scaleTypeEditIndex = 0; static int scaleEditNoteIndex = 0; static void drawUI(); static void updateLeds(); -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(); - int addr = 0; - EEPROM.put(addr, EEPROM_MAGIC); addr += sizeof(EEPROM_MAGIC); - int channels[NUM_TRACKS]; - for(int i=0; i 16) midiChannels[i] = 16; - } - EEPROM.get(addr, melodySeeds); addr += sizeof(melodySeeds); - EEPROM.get(addr, currentStrategyIndices); addr += sizeof(currentStrategyIndices); - for(int i=0; i= numStrategies) currentStrategyIndices[i] = 0; - } - bool mutes[NUM_TRACKS]; - EEPROM.get(addr, mutes); addr += sizeof(mutes); - for(int i=0; i 240) tempo = 240; - int intensities[NUM_TRACKS]; - EEPROM.get(addr, intensities); addr += sizeof(intensities); - for(int i=0; i 10) trackIntensity[i] = 10; - } - 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, 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++) { - EEPROM.get(addr, scaleNotes[i]); addr += sizeof(int); - if (scaleNotes[i] < 0) scaleNotes[i] = 0; - if (scaleNotes[i] > 11) scaleNotes[i] = 11; - } - - EEPROM.get(addr, sequence); addr += sizeof(sequence); - midi.unlock(); - return true; -} - -static void savePatch(int bank, int slot) { - int patchIndex = bank * 4 + 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); - } - EEPROM.put(addr, currentStrategyIndices); addr += sizeof(currentStrategyIndices); - EEPROM.put(addr, melodySeeds); addr += sizeof(melodySeeds); - int steps[NUM_TRACKS]; - for(int i=0; i 12) numScaleNotes = 0; - for (int i = 0; i < 12; i++) { - EEPROM.get(addr, scaleNotes[i]); addr += sizeof(int); - if (scaleNotes[i] < 0) scaleNotes[i] = 0; - if (scaleNotes[i] > 11) scaleNotes[i] = 11; - } - EEPROM.get(addr, currentStrategyIndices); addr += sizeof(currentStrategyIndices); - for(int i=0; i= numStrategies) currentStrategyIndices[i] = 0; - } - EEPROM.get(addr, melodySeeds); addr += sizeof(melodySeeds); - 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); - for(int i=0; i 10) trackIntensity[i] = 10; - } - - if (isPlaying) { - generateSequenceData(currentThemeIndex, nextSequence); - sequenceChangeScheduled = true; - } else { - generateSequenceData(currentThemeIndex, local_sequence); - memcpy(sequence, local_sequence, sizeof(local_sequence)); - } - midi.unlock(); - ui.showMessage("LOADED!"); + return Persistence::loadSequence(); } void factoryReset() { - ui.showMessage("RESETTING..."); - for(int i=0; igenerate(target, track, numSteps[track], scaleNotes, numScaleNotes, melodySeeds[track] + themeType * 12345, trackIntensity[track]); + Persistence::factoryReset(); } void generateRandomScale() { - Serial.println(F("Generating new scale.")); - // All tracks share the same scale for now - strategies[currentStrategyIndices[0]]->generateScale(scaleNotes, numScaleNotes); -} - -static void generateSequenceData(int themeType, Step (*target)[NUM_STEPS]) { - Serial.println(F("Generating sequence.")); - for(int i=0; imutate(target, i, numSteps[i], scaleNotes, numScaleNotes, trackIntensity[i]); - } - } -} - -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 0) { - currentScaleType = candidates[random(count)]; - updateScale(); - } + SequenceGenerator::mutateSequence(target); } static void handleInput() { @@ -370,10 +100,10 @@ static void handleInput() { currentRoot += delta; if (currentRoot < 0) currentRoot = 11; if (currentRoot > 11) currentRoot = 0; - updateScale(); + SequenceGenerator::updateScale(); if (isPlaying) { sequenceChangeScheduled = true; - generateSequenceData((queuedTheme != -1) ? queuedTheme : currentThemeIndex, nextSequence); + SequenceGenerator::generateSequenceData((queuedTheme != -1) ? queuedTheme : currentThemeIndex, nextSequence); } break; case UI_EDIT_SCALE_TYPE: @@ -423,7 +153,7 @@ static void handleInput() { if (isPlaying) { int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex; midi.lock(); - generateSequenceData(theme, nextSequence); + SequenceGenerator::generateSequenceData(theme, nextSequence); sequenceChangeScheduled = true; midi.unlock(); } @@ -488,7 +218,7 @@ static void handleInput() { if (!sequenceChangeScheduled) { memcpy(nextSequence, sequence, sizeof(sequence)); } - generateTrackData(randomizeTrack, theme, nextSequence); + SequenceGenerator::generateTrackData(randomizeTrack, theme, nextSequence); sequenceChangeScheduled = true; } midi.unlock(); @@ -531,7 +261,7 @@ static void handleInput() { if (isPlaying) { queuedTheme = selectedTheme; midi.lock(); - generateSequenceData(queuedTheme, nextSequence); + SequenceGenerator::generateSequenceData(queuedTheme, nextSequence); sequenceChangeScheduled = true; midi.unlock(); } else { @@ -545,8 +275,8 @@ static void handleInput() { int sub = offset % 8; bool isSave = sub >= 4; int slot = sub % 4; - if (isSave) savePatch(bank, slot); - else loadPatch(bank, slot); + if (isSave) Persistence::savePatch(bank, slot); + else Persistence::loadPatch(bank, slot, local_sequence); break; } break; @@ -585,7 +315,7 @@ static void handleInput() { if (!sequenceChangeScheduled) { memcpy(nextSequence, sequence, sizeof(sequence)); } - generateTrackData(randomizeTrack, theme, nextSequence); + SequenceGenerator::generateTrackData(randomizeTrack, theme, nextSequence); sequenceChangeScheduled = true; midi.unlock(); } @@ -599,7 +329,7 @@ static void handleInput() { if (!sequenceChangeScheduled) { memcpy(nextSequence, sequence, sizeof(sequence)); } - generateTrackData(randomizeTrack, theme, nextSequence); + SequenceGenerator::generateTrackData(randomizeTrack, theme, nextSequence); sequenceChangeScheduled = true; midi.unlock(); } @@ -618,7 +348,7 @@ static void handleInput() { if (isPlaying) { int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex; midi.lock(); - generateSequenceData(theme, nextSequence); + SequenceGenerator::generateSequenceData(theme, nextSequence); sequenceChangeScheduled = true; midi.unlock(); } @@ -637,7 +367,7 @@ static void handleInput() { if (isPlaying) { int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex; midi.lock(); - generateSequenceData(theme, nextSequence); + SequenceGenerator::generateSequenceData(theme, nextSequence); sequenceChangeScheduled = true; midi.unlock(); } @@ -650,7 +380,7 @@ static void handleInput() { if (isPlaying) { int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex; midi.lock(); - generateSequenceData(theme, nextSequence); + SequenceGenerator::generateSequenceData(theme, nextSequence); sequenceChangeScheduled = true; midi.unlock(); } @@ -663,7 +393,7 @@ static void handleInput() { if (isPlaying) { int theme = (queuedTheme != -1) ? queuedTheme : currentThemeIndex; midi.lock(); - generateSequenceData(theme, nextSequence); + SequenceGenerator::generateSequenceData(theme, nextSequence); sequenceChangeScheduled = true; midi.unlock(); } @@ -804,7 +534,7 @@ void loopUI() { int nextTheme = random(1, 8); // Themes 1-7 int repeats = random(1, 9); // 1-8 repeats - generateSequenceData(nextTheme, nextSequence); + SequenceGenerator::generateSequenceData(nextTheme, nextSequence); queuedTheme = nextTheme; nextSongRepeats = repeats; sequenceChangeScheduled = true;