145 lines
5.1 KiB
C++
145 lines
5.1 KiB
C++
#include "SequenceGenerator.h"
|
|
#include "SharedState.h"
|
|
#include "MidiDriver.h"
|
|
#include <Arduino.h>
|
|
|
|
void SequenceGenerator::getChordNotes(int* chordNotes, int& numChordNotes) {
|
|
numChordNotes = 0;
|
|
if (numScaleNotes == 0) return;
|
|
|
|
// For explicit chord types, chord notes are the scale notes
|
|
if (currentScaleType >= 6 && currentScaleType <= 9) {
|
|
numChordNotes = numScaleNotes;
|
|
for (int i = 0; i < numScaleNotes; i++) {
|
|
chordNotes[i] = scaleNotes[i];
|
|
}
|
|
return;
|
|
}
|
|
|
|
// For other scales, derive the basic triad (root, third, fifth) from the current scale
|
|
chordNotes[numChordNotes++] = currentRoot;
|
|
|
|
// Find the third (major or minor)
|
|
int majorThird = (currentRoot + 4) % 12;
|
|
int minorThird = (currentRoot + 3) % 12;
|
|
bool thirdFound = false;
|
|
for (int i = 0; i < numScaleNotes; i++) {
|
|
if (scaleNotes[i] == majorThird) {
|
|
chordNotes[numChordNotes++] = majorThird;
|
|
thirdFound = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!thirdFound) {
|
|
for (int i = 0; i < numScaleNotes; i++) {
|
|
if (scaleNotes[i] == minorThird) {
|
|
chordNotes[numChordNotes++] = minorThird;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find the fifth (perfect, or diminished if not found)
|
|
int perfectFifth = (currentRoot + 7) % 12;
|
|
int diminishedFifth = (currentRoot + 6) % 12;
|
|
for (int i = 0; i < numScaleNotes; i++) {
|
|
if (scaleNotes[i] == perfectFifth) {
|
|
chordNotes[numChordNotes++] = perfectFifth;
|
|
return;
|
|
}
|
|
}
|
|
for (int i = 0; i < numScaleNotes; i++) {
|
|
if (scaleNotes[i] == diminishedFifth) {
|
|
chordNotes[numChordNotes++] = diminishedFifth;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SequenceGenerator::generateTrackData(int track, int themeType, Step (*target)[NUM_STEPS]) {
|
|
randomSeed(melodySeeds[track] + themeType * 12345);
|
|
int chordNotes[12];
|
|
int numChordNotes = 0;
|
|
getChordNotes(chordNotes, numChordNotes);
|
|
strategies[currentStrategyIndices[track]]->generate(target, track, numSteps[track], scaleNotes, numScaleNotes, chordNotes, numChordNotes, melodySeeds[track] + themeType * 12345, trackIntensity[track]);
|
|
}
|
|
|
|
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<count; i++) {
|
|
scaleNotes[i] = (currentRoot + intervals[i]) % 12;
|
|
}
|
|
sortArray(scaleNotes, numScaleNotes);
|
|
midi.unlock();
|
|
}
|
|
|
|
void SequenceGenerator::generateSequenceData(int themeType, Step (*target)[NUM_STEPS]) {
|
|
Serial.println(F("Generating sequence."));
|
|
for(int i=0; i<NUM_TRACKS; i++) {
|
|
SequenceGenerator::generateTrackData(i, themeType, target);
|
|
}
|
|
}
|
|
|
|
void SequenceGenerator::mutateSequence(Step (*target)[NUM_STEPS]) {
|
|
int chordNotes[12];
|
|
int numChordNotes = 0;
|
|
getChordNotes(chordNotes, numChordNotes);
|
|
for(int i=0; i<NUM_TRACKS; i++) {
|
|
if (random(100) < (trackIntensity[i] * 10)) {
|
|
strategies[currentStrategyIndices[i]]->mutate(target, i, numSteps[i], scaleNotes, numScaleNotes, chordNotes, numChordNotes, trackIntensity[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SequenceGenerator::pickRandomScaleType(int themeType) {
|
|
unsigned long seed = themeType * 9999;
|
|
for(int i=0; i<NUM_TRACKS; i++) seed += melodySeeds[i];
|
|
randomSeed(seed);
|
|
|
|
currentScaleType = random(10);
|
|
SequenceGenerator::updateScale();
|
|
}
|