From bbe6c4ff2f842c34a6fbdc984e9b529d1e323813 Mon Sep 17 00:00:00 2001 From: Dejvino Date: Fri, 6 Mar 2026 23:13:09 +0100 Subject: [PATCH] 3 new melody strategies --- CallAndResponseStrategy.h | 91 ++++++++++++++++++++++++++++++++++ IsorhythmStrategy.h | 102 ++++++++++++++++++++++++++++++++++++++ SharedState.cpp | 9 +++- WaveStrategy.h | 97 ++++++++++++++++++++++++++++++++++++ 4 files changed, 297 insertions(+), 2 deletions(-) create mode 100644 CallAndResponseStrategy.h create mode 100644 IsorhythmStrategy.h create mode 100644 WaveStrategy.h diff --git a/CallAndResponseStrategy.h b/CallAndResponseStrategy.h new file mode 100644 index 0000000..a216116 --- /dev/null +++ b/CallAndResponseStrategy.h @@ -0,0 +1,91 @@ +#include "MelodyStrategy.h" +#include + +#ifndef CALL_AND_RESPONSE_STRATEGY_H +#define CALL_AND_RESPONSE_STRATEGY_H + +class CallAndResponseStrategy : public MelodyStrategy { +public: + void generate(Step (*sequence)[NUM_STEPS], int track, int numSteps, int* scaleNotes, int numScaleNotes, int seed, int intensity) override { + randomSeed(seed); + if (numScaleNotes == 0) return; + + int halfSteps = numSteps / 2; + if (halfSteps < 1) halfSteps = 1; + + // Generate Call (First Half) + for (int i = 0; i < halfSteps; i++) { + // Simple random generation for the call, weighted by intensity + if (random(100) < (intensity * 8 + 20)) { + int octave = 3 + random(3); + sequence[track][i].note = 12 * octave + scaleNotes[random(numScaleNotes)]; + sequence[track][i].accent = (random(100) < 30); + sequence[track][i].tie = (random(100) < 10); + } else { + sequence[track][i].note = -1; + sequence[track][i].accent = false; + sequence[track][i].tie = false; + } + } + + // Generate Response (Second Half) + for (int i = halfSteps; i < numSteps; i++) { + int srcIndex = i - halfSteps; + Step srcStep = sequence[track][srcIndex]; + + // Default: Copy + sequence[track][i] = srcStep; + + // Variation based on intensity + if (srcStep.note != -1) { + int r = random(100); + if (r < intensity * 5) { + // Transpose / Shift + int shift = random(-2, 3); + int octave = srcStep.note / 12; + int noteVal = srcStep.note % 12; + + // Find index in scale + int idx = 0; + for(int k=0; k + +class IsorhythmStrategy : public MelodyStrategy { +public: + void generate(Step (*sequence)[NUM_STEPS], int track, int numSteps, int* scaleNotes, int numScaleNotes, int seed, int intensity) override { + randomSeed(seed); + if (numScaleNotes == 0) return; + + // 1. Create the Color (pitch pattern) + // Intensity affects color length. Higher intensity = longer, more complex color. + int colorLength = 2 + random(intensity + 2); // 2 to intensity+3 notes + if (colorLength > numScaleNotes) colorLength = numScaleNotes; + if (colorLength > 8) colorLength = 8; // Keep it reasonable + + int color[8]; + // Pick unique notes from the scale for the color + int scaleIndices[12]; + for(int i=0; i numSteps) taleaLength = numSteps; + + bool talea[NUM_STEPS]; + int pulses = 2 + random(taleaLength - 2); // At least 2 pulses + + // Euclidean distribution for a nice rhythm + int bucket = 0; + for(int i=0; i= taleaLength) { + bucket -= taleaLength; + talea[i] = true; + } else { + talea[i] = false; + } + } + + // 3. Apply Color and Talea to the sequence + int colorIdx = 0; + for (int i = 0; i < numSteps; i++) { + int taleaIdx = i % taleaLength; + if (talea[taleaIdx]) { + int octave = 3 + random(3); + int noteScaleIndex = color[colorIdx % colorLength]; + sequence[track][i].note = 12 * octave + scaleNotes[noteScaleIndex]; + sequence[track][i].accent = (i % 4 == 0); // Accent on downbeats + sequence[track][i].tie = false; + colorIdx++; + } 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, int intensity) override { + // Mutation: rotate the talea (rhythmic displacement) + int rotation = random(1, numSteps); + Step temp[NUM_STEPS]; + for(int i=0; i + +#ifndef WAVE_STRATEGY_H +#define WAVE_STRATEGY_H + +class WaveStrategy : public MelodyStrategy { +public: + void generate(Step (*sequence)[NUM_STEPS], int track, int numSteps, int* scaleNotes, int numScaleNotes, int seed, int intensity) override { + randomSeed(seed); + if (numScaleNotes == 0) return; + + // Wave parameters + // Frequency: How many cycles per sequence + float freq = (float)random(1, 4) + (intensity * 0.2f); + // Phase offset + float phase = random(100) / 100.0f * 2.0f * PI; + // Waveform type: 0=Sine, 1=Triangle, 2=Saw + int type = random(3); + + // Center pitch (note index) + int centerIdx = numScaleNotes * 2; // Middle of 4 octaves roughly + int amp = numScaleNotes + (intensity); // Amplitude in scale degrees + + for (int i = 0; i < numSteps; i++) { + float t = (float)i / (float)numSteps; // 0.0 to 1.0 + float val = 0.0f; + + if (type == 0) { // Sine + val = sin(t * freq * 2.0f * PI + phase); + } else if (type == 1) { // Triangle + float x = t * freq + phase / (2.0f*PI); + x = x - (int)x; // frac + val = (x < 0.5f) ? (4.0f * x - 1.0f) : (3.0f - 4.0f * x); + } else { // Saw + float x = t * freq + phase / (2.0f*PI); + x = x - (int)x; + val = 2.0f * x - 1.0f; + } + + // Map val (-1 to 1) to note index + int noteIdxOffset = (int)(val * amp); + int totalIdx = centerIdx + noteIdxOffset; + + // Normalize totalIdx + while(totalIdx < 0) totalIdx += numScaleNotes; + + int octave = 2 + (totalIdx / numScaleNotes); + int scaleIdx = totalIdx % numScaleNotes; + + if (octave < 0) octave = 0; + if (octave > 8) octave = 8; + + // Rhythmic density based on intensity + if (random(100) < (40 + intensity * 5)) { + sequence[track][i].note = 12 * octave + scaleNotes[scaleIdx]; + sequence[track][i].accent = (val > 0.8f); // Accent peaks + sequence[track][i].tie = false; + } 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, int intensity) override { + // Phase shift the sequence + int shift = random(1, 4); + Step temp[NUM_STEPS]; + for(int i=0; i