125 lines
4.0 KiB
C++
125 lines
4.0 KiB
C++
#ifndef ARP_STRATEGY_H
|
|
#define ARP_STRATEGY_H
|
|
|
|
#include "MelodyStrategy.h"
|
|
#include <Arduino.h>
|
|
|
|
class ArpStrategy : 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. Select Subset
|
|
int subsetSize = random(2, numScaleNotes + 1);
|
|
if (subsetSize > 12) subsetSize = 12;
|
|
int subset[12];
|
|
|
|
// Create indices to shuffle
|
|
int indices[12];
|
|
for(int i=0; i<numScaleNotes; i++) indices[i] = i;
|
|
|
|
// Shuffle indices
|
|
for(int i=0; i<numScaleNotes; i++) {
|
|
int r = random(numScaleNotes);
|
|
int temp = indices[i];
|
|
indices[i] = indices[r];
|
|
indices[r] = temp;
|
|
}
|
|
|
|
// Pick subset
|
|
for(int i=0; i<subsetSize; i++) {
|
|
subset[i] = scaleNotes[indices[i]];
|
|
}
|
|
sortArray(subset, subsetSize);
|
|
|
|
// 2. Pick Arp Length & Pattern
|
|
int arpLength = random(2, 17); // 2 to 16
|
|
Step arpPattern[16];
|
|
|
|
int mode = random(3); // 0: Up, 1: Down, 2: Up/Down
|
|
int numOctaves = 3;
|
|
int baseOctave = 3;
|
|
int totalNotes = subsetSize * numOctaves;
|
|
|
|
int currentIndex = 0;
|
|
int direction = 1;
|
|
|
|
if (mode == 1) { // Down
|
|
currentIndex = totalNotes - 1;
|
|
direction = -1;
|
|
}
|
|
|
|
for (int i = 0; i < arpLength; i++) {
|
|
// Chance of rest (15%)
|
|
if (random(100) < 15) {
|
|
arpPattern[i].note = -1;
|
|
arpPattern[i].accent = false;
|
|
arpPattern[i].tie = false;
|
|
} else {
|
|
int octaveOffset = currentIndex / subsetSize;
|
|
int noteIndex = currentIndex % subsetSize;
|
|
int octave = baseOctave + octaveOffset;
|
|
|
|
arpPattern[i].note = 12 * octave + subset[noteIndex];
|
|
arpPattern[i].accent = (i % 4 == 0); // Accent on beat
|
|
arpPattern[i].tie = false;
|
|
}
|
|
|
|
if (mode == 0) { // Up
|
|
currentIndex++;
|
|
if (currentIndex >= totalNotes) currentIndex = 0;
|
|
} else if (mode == 1) { // Down
|
|
currentIndex--;
|
|
if (currentIndex < 0) currentIndex = totalNotes - 1;
|
|
} else { // Up/Down
|
|
currentIndex += direction;
|
|
if (currentIndex >= totalNotes) {
|
|
currentIndex = max(0, totalNotes - 2);
|
|
direction = -1;
|
|
} else if (currentIndex < 0) {
|
|
currentIndex = min(totalNotes - 1, 1);
|
|
direction = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3. Fill Sequence
|
|
for (int i = 0; i < numSteps; i++) {
|
|
sequence[track][i] = arpPattern[i % arpLength];
|
|
}
|
|
randomSeed(micros());
|
|
}
|
|
|
|
void generateScale(int* scaleNotes, int& numScaleNotes) override {
|
|
numScaleNotes = random(3, 13); // 3 to 12 notes
|
|
for (int i = 0; i < 12; i++) {
|
|
scaleNotes[i] = i; // Fill with all notes
|
|
}
|
|
// Shuffle
|
|
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 {
|
|
// Swap two notes
|
|
int s1 = random(numSteps);
|
|
int s2 = random(numSteps);
|
|
if (sequence[track][s1].note != -1 && sequence[track][s2].note != -1) {
|
|
int8_t temp = sequence[track][s1].note;
|
|
sequence[track][s1].note = sequence[track][s2].note;
|
|
sequence[track][s2].note = temp;
|
|
}
|
|
}
|
|
|
|
const char* getName() override {
|
|
return "Arp";
|
|
}
|
|
};
|
|
|
|
#endif |