More strategies
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
#ifndef CELLULAR_AUTOMATA_STRATEGY_H
|
||||
#define CELLULAR_AUTOMATA_STRATEGY_H
|
||||
|
||||
#include "MelodyStrategy.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
class CellularAutomataStrategy : public MelodyStrategy {
|
||||
public:
|
||||
void generate(Step (*sequence)[NUM_STEPS], int track, int numSteps, int* scaleNotes, int numScaleNotes, int seed) override {
|
||||
randomSeed(seed);
|
||||
if (numScaleNotes == 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 {
|
||||
for(int i=0; i<numSteps; i++) cells[i] = (random(100) < 30);
|
||||
}
|
||||
|
||||
// Evolve for some generations to let patterns emerge
|
||||
int generations = numSteps + random(16);
|
||||
|
||||
for (int g = 0; g < generations; g++) {
|
||||
for (int i = 0; i < numSteps; i++) {
|
||||
bool left = cells[(i - 1 + numSteps) % numSteps];
|
||||
bool center = 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;
|
||||
}
|
||||
for(int i=0; i<numSteps; i++) cells[i] = next_cells[i];
|
||||
}
|
||||
|
||||
// Map to notes
|
||||
for (int i = 0; i < numSteps; i++) {
|
||||
if (cells[i]) {
|
||||
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);
|
||||
} else {
|
||||
sequence[track][i].note = -1;
|
||||
sequence[track][i].accent = false;
|
||||
sequence[track][i].tie = false;
|
||||
}
|
||||
}
|
||||
randomSeed(micros());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void mutate(Step (*sequence)[NUM_STEPS], int track, int numSteps, int* scaleNotes, int numScaleNotes) 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];
|
||||
}
|
||||
|
||||
const char* getName() override {
|
||||
return "Cellular";
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user