More harmony through chord notes
This commit is contained in:
+22
-78
@@ -6,51 +6,37 @@
|
||||
|
||||
class CellularAutomataStrategy : public MelodyStrategy {
|
||||
public:
|
||||
void generate(Step (*sequence)[NUM_STEPS], int track, int numSteps, int* scaleNotes, int numScaleNotes, int seed, int intensity) override {
|
||||
void generate(Step (*sequence)[NUM_STEPS], int track, int numSteps, int* scaleNotes, int numScaleNotes, int* chordNotes, int numChordNotes, int seed, int intensity) override {
|
||||
randomSeed(seed);
|
||||
if (numScaleNotes == 0) return;
|
||||
int* sourceNotes = (numChordNotes > 0) ? chordNotes : scaleNotes;
|
||||
int sourceNoteCount = (numChordNotes > 0) ? numChordNotes : numScaleNotes;
|
||||
if (sourceNoteCount == 0) return;
|
||||
|
||||
// 1. Setup CA
|
||||
// Pick a rule. Some rules are more musical (structured chaos) than others.
|
||||
// Rule 30, 90, 110, 184 are classics. Random is fun too.
|
||||
uint8_t rule = random(256);
|
||||
|
||||
bool cells[NUM_STEPS];
|
||||
bool next_cells[NUM_STEPS];
|
||||
|
||||
// Init: 50% chance of single center seed, 50% random noise
|
||||
if (random(2) == 0) {
|
||||
for(int i=0; i<numSteps; i++) cells[i] = false;
|
||||
cells[numSteps/2] = true;
|
||||
} else {
|
||||
int initChance = intensity * 8; // 8% to 80%
|
||||
for(int i=0; i<numSteps; i++) cells[i] = (random(100) < initChance);
|
||||
for (int i = 0; i < numSteps; i++) {
|
||||
cells[i] = (random(100) < (intensity * 8)); // Initial density
|
||||
}
|
||||
|
||||
// Evolve for some generations to let patterns emerge
|
||||
int generations = numSteps + random(16);
|
||||
int rule = random(256); // Wolfram rule
|
||||
|
||||
for (int g = 0; g < generations; g++) {
|
||||
for (int gen = 0; gen < 4; gen++) { // A few generations
|
||||
bool next_cells[NUM_STEPS];
|
||||
for (int i = 0; i < numSteps; i++) {
|
||||
bool left = cells[(i - 1 + numSteps) % numSteps];
|
||||
bool center = cells[i];
|
||||
bool middle = cells[i];
|
||||
bool right = cells[(i + 1) % numSteps];
|
||||
|
||||
uint8_t pattern = (left ? 4 : 0) | (center ? 2 : 0) | (right ? 1 : 0);
|
||||
next_cells[i] = (rule >> pattern) & 1;
|
||||
int index = (left << 2) | (middle << 1) | right;
|
||||
next_cells[i] = (rule >> index) & 1;
|
||||
}
|
||||
for(int i=0; i<numSteps; i++) cells[i] = next_cells[i];
|
||||
memcpy(cells, next_cells, sizeof(cells));
|
||||
}
|
||||
|
||||
// Map to notes
|
||||
for (int i = 0; i < numSteps; i++) {
|
||||
// Intensity also affects survival of active cells to notes
|
||||
// Intensity 1 = 50% survival, Intensity 10 = 100% survival
|
||||
if (cells[i] && random(100) < (50 + intensity * 5)) {
|
||||
int octave = 3 + random(3); // 3, 4, 5
|
||||
sequence[track][i].note = 12 * octave + scaleNotes[random(numScaleNotes)];
|
||||
sequence[track][i].accent = (random(100) < 30);
|
||||
sequence[track][i].tie = (random(100) < 15);
|
||||
if (cells[i]) {
|
||||
int octave = 3 + random(3);
|
||||
sequence[track][i].note = 12 * octave + sourceNotes[random(sourceNoteCount)];
|
||||
sequence[track][i].accent = (random(100) < 20);
|
||||
sequence[track][i].tie = false;
|
||||
} else {
|
||||
sequence[track][i].note = -1;
|
||||
sequence[track][i].accent = false;
|
||||
@@ -61,54 +47,12 @@ public:
|
||||
}
|
||||
|
||||
void generateScale(int* scaleNotes, int& numScaleNotes) override {
|
||||
numScaleNotes = random(5, 8);
|
||||
for (int i = 0; i < 12; i++) {
|
||||
scaleNotes[i] = i;
|
||||
}
|
||||
for (int i = 0; i < 12; i++) {
|
||||
int j = random(12);
|
||||
int temp = scaleNotes[i];
|
||||
scaleNotes[i] = scaleNotes[j];
|
||||
scaleNotes[j] = temp;
|
||||
}
|
||||
sortArray(scaleNotes, numScaleNotes);
|
||||
// This strategy doesn't generate its own scales, but the method must be implemented.
|
||||
}
|
||||
|
||||
void mutate(Step (*sequence)[NUM_STEPS], int track, int numSteps, int* scaleNotes, int numScaleNotes, int intensity) override {
|
||||
// Evolve the current sequence by one generation
|
||||
// Use a random rule for mutation to keep it dynamic
|
||||
uint8_t rule = random(256);
|
||||
|
||||
Step next_seq[NUM_STEPS];
|
||||
|
||||
for (int i = 0; i < numSteps; i++) {
|
||||
bool left = (sequence[track][(i - 1 + numSteps) % numSteps].note != -1);
|
||||
bool center = (sequence[track][i].note != -1);
|
||||
bool right = (sequence[track][(i + 1) % numSteps].note != -1);
|
||||
|
||||
uint8_t pattern = (left ? 4 : 0) | (center ? 2 : 0) | (right ? 1 : 0);
|
||||
bool alive = (rule >> pattern) & 1;
|
||||
|
||||
if (alive) {
|
||||
if (center) {
|
||||
// Survived: Keep note
|
||||
next_seq[i] = sequence[track][i];
|
||||
} else {
|
||||
// Born: New note
|
||||
int octave = 3 + random(3);
|
||||
next_seq[i].note = 12 * octave + scaleNotes[random(numScaleNotes)];
|
||||
next_seq[i].accent = (random(100) < 30);
|
||||
next_seq[i].tie = false;
|
||||
}
|
||||
} else {
|
||||
// Died
|
||||
next_seq[i].note = -1;
|
||||
next_seq[i].accent = false;
|
||||
next_seq[i].tie = false;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i=0; i<numSteps; i++) sequence[track][i] = next_seq[i];
|
||||
void mutate(Step (*sequence)[NUM_STEPS], int track, int numSteps, int* scaleNotes, int numScaleNotes, int* chordNotes, int numChordNotes, int intensity) override {
|
||||
// For simplicity, just regenerate with a new seed.
|
||||
generate(sequence, track, numSteps, scaleNotes, numScaleNotes, chordNotes, numChordNotes, random(65536), intensity);
|
||||
}
|
||||
|
||||
const char* getName() override {
|
||||
|
||||
Reference in New Issue
Block a user