// GENERATIVE MATH SEQUENCER WITH 9 ADVANCED SCALES, SUB-HARMONICS & SOURCE OF UNCERTAINTY
// ENCODER BEDIENING:
// - LANG indrukken (langer dan 400ms): Wisselen tussen PLAY MODE en EDIT MODE
// - KORT indrukken in EDIT MODE: Wisselen tussen SEQUENCER (Rood) en OSCILLATOR (Magenta)
// - KORT indrukken in PLAY MODE: Wisselen tussen EFFECTEN (Cyan Constant) en AMBIENT / DRONE (Knipperend Cyan)
//
// EDIT MODE - ADVANCED OSCILLATOR PAGE (Magenta LEDs):
// Normal: Knob 1 = Wave Shape | Knob 2 = Wavefolder Depth
// Hold Button 1: Knob 1 = Sub 1 Div (/2 tot /5) | Knob 2 = Sub 2 Div (/3 tot /8)
// Hold Button 2: Knob 1 = Sub 1 Vol | Knob 2 = Sub 2 Vol
// Hold Both Buttons: Knob 1 = FM Ratio | Knob 2 = Chorus Mix
#include "DaisyDuino.h"
#include <math.h>
DaisyHardware pod;
Oscillator carrier;
Oscillator modulator;
Oscillator subOsc1; // Eerste subharmonische oscillator
Oscillator subOsc2; // Tweede subharmonische oscillator
Oscillator tapeLfo;
Oscillator chorusLfo;
AdEnv env;
AdEnv modEnv;
Metro tick;
MoogLadder flt;
ReverbSc verb;
#define MAX_DELAY_SAMPLES 36000
static DelayLine<float, MAX_DELAY_SAMPLES> DSY_SDRAM_BSS delayL;
static DelayLine<float, MAX_DELAY_SAMPLES> DSY_SDRAM_BSS delayR;
#define MAX_CHORUS_SAMPLES 1024
static DelayLine<float, MAX_CHORUS_SAMPLES> DSY_SDRAM_BSS chorusDelayL;
static DelayLine<float, MAX_CHORUS_SAMPLES> DSY_SDRAM_BSS chorusDelayR;
#define MAX_CLOUD_SAMPLES 4000
static DelayLine<float, MAX_CLOUD_SAMPLES> DSY_SDRAM_BSS cloudDelayL;
static DelayLine<float, MAX_CLOUD_SAMPLES> DSY_SDRAM_BSS cloudDelayR;
uint8_t step = 0;
uint8_t stepCount = 16;
bool pendulumForward = true;
float turingPitch[16];
float turingDecay[16];
bool active[16];
float env_out;
bool editMode = true;
bool oscEditPage = false;
bool playAmbientPage = false;
uint8_t pulseCount = 6;
uint8_t selectedScale = 1;
float rootNote = 165.0f;
uint8_t currentChord = 0;
uint32_t totalStepsPlayed = 0;
// Encoder timing variabelen
unsigned long encoderPressedTime = 0;
bool encoderWasPressed = false;
const unsigned long LONG_PRESS_TIME = 400;
// Effecten parameters
float revMix = 0.35f;
float revDecay = 0.82f;
float delayTime = 0.30f;
float delayFeedback = 0.45f;
float tapeDrive = 1.0f;
float chorusMix = 0.40f;
// Generatieve & Wiskundige parameters
float turingProbability = 0.0f;
uint8_t octaveRange = 3;
uint8_t mathMode = 0;
uint8_t playDirection = 0;
float fmRatio = 14.0f;
// Advanced Oscillator & Sub-Harmonic parameters
float oscWaveShape = 0.0f;
float wavefolderDepth = 0.0f;
float sub1Vol = 0.0f;
float sub2Vol = 0.0f;
uint8_t sub1Div = 2;
uint8_t sub2Div = 3;
// Ambient & Infinite Drone parameters
float glideTime = 0.0f;
float droneSustain = 0.0f;
float currentPitchSlew = 165.0f;
// Source of Uncertainty parameters
float randomVoltage1 = 0.0f;
float randomVoltage2 = 0.0f;
const float uncertaintySlew = 0.0002f;
// Buchla Envelope & FM Input parameters
float envAttack = 0.005f;
float envDecay = 0.35f;
float externalFmDepth = 400.0f;
float fmRatioList[] = {1.0f, 2.0f, 3.0f, 5.0f, 7.0f, 11.0f, 14.0f};
int fibonacciSequence[] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987};
int primeSequence[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};
int scales[9][4][5] = {
{{0, 2, 4, 6, 8}, {1, 3, 5, 7, 9}, {2, 4, 6, 8, 11}, {3, 5, 7, 9, 12}},
{{2, 4, 6, 8, 9}, {0, 2, 4, 6, 7}, {3, 5, 7, 9, 10}, {1, 3, 5, 7, 8}},
{{0, 2, 3, 6, 7}, {1, 3, 4, 6, 8}, {2, 4, 6, 7, 9}, {3, 5, 6, 8, 11}},
{{0, 2, 4, 6, 7}, {2, 4, 6, 7, 9}, {4, 6, 7, 9, 11}, {0, 2, 4, 6, 11}},
{{0, 2, 3, 4, 6}, {0, 2, 3, 4, 6}, {3, 5, 6, 7, 9}, {2, 4, 5, 6, 8}},
{{0, 3, 5, 7, 10}, {1, 4, 6, 8, 11}, {2, 5, 7, 9, 12}, {3, 6, 8, 10, 13}},
{{0, 1, 5, 7, 8}, {0, 1, 5, 7, 8}, {5, 7, 8, 12, 13}, {1, 5, 7, 8, 12}},
{{0, 1, 4, 5, 7}, {2, 3, 6, 7, 9}, {0, 1, 4, 5, 12}, {3, 4, 7, 8, 10}},
{{0, 2, 4, 6, 8}, {1, 3, 5, 7, 9}, {2, 4, 6, 8, 10}, {0, 2, 5, 7, 9}}
};
float scaleRatios[] = {
1.000f, 1.125f, 1.250f, 1.414f, 1.500f, 1.666f, 1.875f,
2.000f, 2.250f, 2.500f, 2.828f, 3.000f, 3.333f, 3.750f,
4.000f, 4.500f, 5.000f, 5.656f, 6.000f
};
float oldk1, oldk2;
float tickFrequency, filterFrequency;
float k1, k2;
void controls();
void NextSamples(float &sigL, float &sigR, float inL);
void RegenerateEuclidean();
void GenerateSingleTuringStep(uint8_t targetStep);
void GenerateMathBuffer();
void AdvanceStep();
static void AudioCallback(float **in, float **out, size_t size) {
float sigL, sigR;
controls();
for (size_t i = 0; i < size; i++) {
float inL = in[0][i];
NextSamples(sigL, sigR, inL);
out[0][i] = sigL;
out[1][i] = sigR;
}
}
void setup() {
float sample_rate;
pod = DAISY.init(DAISY_POD, AUDIO_SR_48K);
sample_rate = DAISY.get_samplerate();
tickFrequency = 3.5f;
filterFrequency = 6000.f;
k1 = k2 = 0.f;
carrier.Init(sample_rate);
modulator.Init(sample_rate);
subOsc1.Init(sample_rate);
subOsc2.Init(sample_rate);
tapeLfo.Init(sample_rate);
chorusLfo.Init(sample_rate);
env.Init(sample_rate);
modEnv.Init(sample_rate);
tick.Init(3.5f, sample_rate);
flt.Init(sample_rate);
verb.Init(sample_rate);
delayL.Init();
delayR.Init();
chorusDelayL.Init();
chorusDelayR.Init();
cloudDelayL.Init();
cloudDelayR.Init();
carrier.SetWaveform(carrier.WAVE_SIN);
modulator.SetWaveform(modulator.WAVE_SIN);
subOsc1.SetWaveform(subOsc1.WAVE_SIN);
subOsc2.SetWaveform(subOsc2.WAVE_SIN);
tapeLfo.SetWaveform(tapeLfo.WAVE_SIN);
tapeLfo.SetFreq(2.2f);
tapeLfo.SetAmp(1.0f);
chorusLfo.SetWaveform(chorusLfo.WAVE_SIN);
chorusLfo.SetFreq(0.6f);
chorusLfo.SetAmp(1.0f);
env.SetTime(ADENV_SEG_ATTACK, 0.005f);
env.SetMin(0.0f);
env.SetMax(0.35f);
modEnv.SetTime(ADENV_SEG_ATTACK, 0.001f);
modEnv.SetTime(ADENV_SEG_DECAY, 0.10f);
modEnv.SetMin(0.0f);
modEnv.SetMax(1.0f);
flt.SetFreq(6000);
flt.SetRes(0.05f);
GenerateMathBuffer();
RegenerateEuclidean();
DAISY.begin(AudioCallback);
}
void loop() {}
void ConditionalParameter(float o, float n, float ¶m, float update) {
if (abs(o - n) > 0.0005) {
param = update;
}
}
void controls() {
pod.DebounceControls();
bool currentPressed = pod.encoder.Pressed();
if (currentPressed && !encoderWasPressed) {
encoderPressedTime = millis();
encoderWasPressed = true;
}
else if (!currentPressed && encoderWasPressed) {
unsigned long duration = millis() - encoderPressedTime;
if (duration >= LONG_PRESS_TIME) {
editMode = !editMode;
oscEditPage = false;
playAmbientPage = false;
}
else {
if (editMode) {
oscEditPage = !oscEditPage;
} else {
playAmbientPage = !playAmbientPage;
}
}
encoderWasPressed = false;
}
int inc = pod.encoder.Increment();
if (inc != 0) {
if (!editMode && pod.buttons[0].Pressed() && pod.buttons[1].Pressed()) {
externalFmDepth += inc * 50.0f;
if (externalFmDepth < 0.0f) externalFmDepth = 0.0f;
if (externalFmDepth > 1500.0f) externalFmDepth = 1500.0f;
}
else {
if (inc > 0) rootNote *= 1.05946f;
else rootNote /= 1.05946f;
if(rootNote < 82.0f) rootNote = 82.0f;
if(rootNote > 660.0f) rootNote = 660.0f;
GenerateMathBuffer();
}
}
k1 = analogRead(PIN_POD_POT_1) / 1023.f;
k2 = analogRead(PIN_POD_POT_2) / 1023.f;
if (editMode) {
if (pod.buttons[0].Pressed() && pod.buttons[1].Pressed()) {
uint8_t ratioIndex = (uint8_t)(k1 * 6.9f);
if (ratioIndex > 6) ratioIndex = 6;
fmRatio = fmRatioList[ratioIndex];
chorusMix = k2;
}
else if (pod.buttons[0].Pressed()) {
if (oscEditPage) {
sub1Div = 2 + (uint8_t)(k1 * 3.9f);
sub2Div = 3 + (uint8_t)(k2 * 5.9f);
} else {
uint8_t newRange = 1 + (uint8_t)(k1 * 2.9f);
if (newRange > 3) newRange = 3;
if (newRange != octaveRange) {
octaveRange = newRange;
GenerateMathBuffer();
}
uint8_t newMath = (uint8_t)(k2 * 2.9f);
if (newMath > 2) newMath = 2;
if (newMath != mathMode) {
mathMode = newMath;
GenerateMathBuffer();
}
}
}
else if (pod.buttons[1].Pressed()) {
if (oscEditPage) {
sub1Vol = k1 * 0.35f;
sub2Vol = k2 * 0.35f;
} else {
turingProbability = k1;
uint8_t newDir = (uint8_t)(k2 * 3.9f);
if (newDir > 3) newDir = 3;
playDirection = newDir;
}
}
else {
if (oscEditPage) {
oscWaveShape = k1;
wavefolderDepth = k2;
} else {
uint8_t newPulses = 1 + (uint8_t)(k1 * 11.9f);
if (newPulses != pulseCount) {
pulseCount = newPulses;
RegenerateEuclidean();
}
uint8_t newScale = (uint8_t)(k2 * 8.9f);
if (newScale > 8) newScale = 8;
if (newScale != selectedScale) {
selectedScale = newScale;
GenerateMathBuffer();
}
}
}
}
else {
if (pod.buttons[0].Pressed() && pod.buttons[1].Pressed()) {
envAttack = 0.001f + (k1 * k1 * 1.499f);
envDecay = 0.010f + (k2 * k2 * 2.990f);
}
else if (pod.buttons[0].Pressed()) {
delayTime = 0.05f + (k1 * 0.70f);
delayFeedback = k2 * 0.72f;
tapeDrive = 1.0f + (k2 * 3.5f);
}
else if (pod.buttons[1].Pressed()) {
revDecay = k1 * 0.96f;
revMix = k2 * 0.80f;
}
else {
if (playAmbientPage) {
glideTime = k1;
droneSustain = k2;
} else {
ConditionalParameter(oldk1, k1, tickFrequency, k1 * 8.f + 1.f);
ConditionalParameter(oldk2, k2, filterFrequency, k2 * 9000.f + 500.f);
tick.SetFreq(tickFrequency);
flt.SetFreq(filterFrequency);
}
}
}
oldk1 = k1;
oldk2 = k2;
if (editMode) {
float r = active[step] ? 0.5f : 0.1f;
if (oscEditPage) {
pod.leds[0].Set(r, 0, r); pod.leds[1].Set(r, 0, r);
} else {
pod.leds[0].Set(r, 0, 0); pod.leds[1].Set(r, 0, 0);
}
} else {
if (playAmbientPage) {
float blink = (millis() % 200 < 100) ? 0.4f : 0.0f;
pod.leds[0].Set(0, blink, blink); pod.leds[1].Set(0, blink, blink);
} else {
pod.leds[0].Set(0, 0.4f, 0.4f); pod.leds[1].Set(0, 0.4f, 0.4f);
}
}
} // <-- Dit is de herstelde sluitaccolade die de compilerfout oplost!
void NextSamples(float &sigL, float &sigR, float inL) {
env_out = env.Process();
float mod_env_out = modEnv.Process();
float targetRand1 = ((rand() % 2000) / 1000.0f) - 1.0f;
float targetRand2 = ((rand() % 2000) / 1000.0f) - 1.0f;
randomVoltage1 += (targetRand1 - randomVoltage1) * uncertaintySlew;
randomVoltage2 += (targetRand2 - randomVoltage2) * uncertaintySlew;
float wow = tapeLfo.Process() * 1.5f;
float targetPitch = turingPitch[step];
float glideFactor = 1.0f - (glideTime * 0.999f);
currentPitchSlew += (targetPitch - currentPitchSlew) * glideFactor * 0.05f;
float mod_freq = (currentPitchSlew + wow) * fmRatio;
modulator.SetFreq(mod_freq);
float fm_index = 1100.0f * mod_env_out;
float modulation = (modulator.Process() * fm_index) + (inL * externalFmDepth);
float carrierFreq = currentPitchSlew + wow + modulation;
carrier.SetFreq(carrierFreq);
subOsc1.SetFreq(carrierFreq / (float)sub1Div);
subOsc2.SetFreq(carrierFreq / (float)sub2Div);
carrier.SetWaveform(carrier.WAVE_SIN);
carrier.SetAmp(1.0f);
float rawSin = carrier.Process();
float tri = asinf(rawSin) * 0.6366f;
float square = (rawSin >= 0.0f) ? 1.0f : -1.0f;
float saw = tri * 1.5f;
if(saw > 1.0f) saw = 2.0f - saw;
else if(saw < -1.0f) saw = -2.0f - saw;
float rawOsc = 0.0f;
if (oscWaveShape < 0.33f) {
float t = oscWaveShape / 0.33f;
rawOsc = (rawSin * (1.0f - t)) + (tri * t);
} else if (oscWaveShape < 0.66f) {
float t = (oscWaveShape - 0.33f) / 0.33f;
rawOsc = (tri * (1.0f - t)) + (saw * t);
} else {
float t = (oscWaveShape - 0.66f) / 0.34f;
rawOsc = (saw * (1.0f - t)) + (square * t);
}
float finalFolderDepth = wavefolderDepth + (randomVoltage1 * 0.12f);
if(finalFolderDepth < 0.0f) finalFolderDepth = 0.0f;
if(finalFolderDepth > 1.0f) finalFolderDepth = 1.0f;
float foldGain = 1.0f + (finalFolderDepth * 7.0f);
float folded = sinf(rawOsc * foldGain * 1.5707f);
float sub1Out = subOsc1.Process() * sub1Vol;
float sub2Out = subOsc2.Process() * sub2Vol;
float masterVca = (folded + sub1Out + sub2Out) * ((env_out * (1.0f - droneSustain)) + (droneSustain * 0.35f));
float finalFilterFreq = filterFrequency + (randomVoltage2 * 350.0f);
if(finalFilterFreq < 100.0f) finalFilterFreq = 100.0f;
if(finalFilterFreq > 12000.0f) finalFilterFreq = 12000.0f;
flt.SetFreq(finalFilterFreq);
float dry = flt.Process(masterVca);
cloudDelayL.Write(dry);
cloudDelayR.Write(dry);
float chaosScale = externalFmDepth / 1500.0f;
float cloudTimeL = 0.005f + (tapeLfo.Process() * 0.030f * chaosScale);
float cloudTimeR = 0.005f + (chorusLfo.Process() * 0.030f * chaosScale);
float grainOutL = cloudDelayL.Read(cloudTimeL * AUDIO_SR_48K);
float grainOutR = cloudDelayR.Read(cloudTimeR * AUDIO_SR_48K);
float cloudMix = chaosScale * 0.40f;
float dryL = (dry * (1.0f - cloudMix)) + (grainOutL * cloudMix);
float dryR = (dry * (1.0f - cloudMix)) + (grainOutR * cloudMix);
float lfoVal = chorusLfo.Process();
float chorusDelayTimeL = 0.011f + (lfoVal * 0.003f);
float chorusDelayTimeR = 0.011f - (lfoVal * 0.003f);
chorusDelayL.Write(dryL);
chorusDelayR.Write(dryR);
float chorusOutL = chorusDelayL.Read(chorusDelayTimeL * AUDIO_SR_48K);
float chorusOutR = chorusDelayR.Read(chorusDelayTimeR * AUDIO_SR_48K);
float dryChorusL = (dryL * (1.0f - chorusMix)) + (chorusOutL * chorusMix);
float dryChorusR = (dryR * (1.0f - chorusMix)) + (chorusOutR * chorusMix);
float dynamicDelayTime = delayTime + (wow * 0.002f);
uint32_t sampleDelay = dynamicDelayTime * AUDIO_SR_48K;
if(sampleDelay >= MAX_DELAY_SAMPLES) sampleDelay = MAX_DELAY_SAMPLES - 1;
float delOutL = delayL.Read(sampleDelay);
float delOutR = delayR.Read(sampleDelay);
float delInL = dryChorusL + (delOutR * delayFeedback);
float delInR = dryChorusR + (delOutL * delayFeedback);
delInL = tanh(delInL * tapeDrive) / tapeDrive;
delInR = tanh(delInR * tapeDrive) / tapeDrive;
delayL.Write(delInL);
delayR.Write(delInR);
float delayMixL = (dryChorusL * 0.6f) + (delOutL * delayFeedback);
float delayMixR = (dryChorusR * 0.6f) + (delOutR * delayFeedback);
float wetL, wetR;
verb.SetFeedback(revDecay);
verb.Process(delayMixL, delayMixR, &wetL, &wetR);
sigL = (delayMixL * (1.0f - revMix)) + (wetL * revMix);
sigR = (delayMixR * (1.0f - revMix)) + (wetR * revMix);
if (tick.Process()) {
AdvanceStep();
totalStepsPlayed++;
if (mathMode == 0) {
float diceRoll = (rand() % 100) / 100.0f;
if (diceRoll < turingProbability) {
GenerateSingleTuringStep(step);
}
}
if (totalStepsPlayed % 64 == 0) {
currentChord = (currentChord + 1) % 4;
GenerateMathBuffer();
}
if (active[step]) {
env.SetTime(ADENV_SEG_ATTACK, envAttack);
float dynamicDecay = envDecay * turingDecay[step];
env.SetTime(ADENV_SEG_DECAY, dynamicDecay);
modEnv.SetTime(ADENV_SEG_ATTACK, envAttack * 0.5f);
modEnv.SetTime(ADENV_SEG_DECAY, dynamicDecay * 0.8f);
env.Trigger();
modEnv.Trigger();
}
}
}
void RegenerateEuclidean() {
int bucket = 0;
for (int i = 0; i < stepCount; i++) {
bucket += pulseCount;
if (bucket >= stepCount) {
bucket -= stepCount;
active[i] = true;
} else {
active[i] = false;
}
}
}
void GenerateSingleTuringStep(uint8_t targetStep) {
int chordIndex = rand() % 5;
int scaleIndex = scales[selectedScale][currentChord][chordIndex];
int octaveOffset = rand() % octaveRange;
int finalIndex = scaleIndex + (octaveOffset * 7);
if (finalIndex >= 19) finalIndex = 18;
turingPitch[targetStep] = rootNote * scaleRatios[finalIndex];
if (octaveOffset == 0) turingDecay[targetStep] = 1.2f;
else if (octaveOffset == 1) turingDecay[targetStep] = 0.7f;
else turingDecay[targetStep] = 0.3f;
}
void GenerateMathBuffer() {
for (int i = 0; i < stepCount; i++) {
if (mathMode == 1) {
int fibVal = fibonacciSequence[i];
int chordIndex = fibVal % 5;
int scaleIndex = scales[selectedScale][currentChord][chordIndex];
int octaveOffset = (fibVal / 5) % octaveRange;
int finalIndex = scaleIndex + (octaveOffset * 7);
if (finalIndex >= 19) finalIndex = 18;
turingPitch[i] = rootNote * scaleRatios[finalIndex];
turingDecay[i] = 0.8f - (octaveOffset * 0.2f);
}
else if (mathMode == 2) {
int primeVal = primeSequence[i];
int chordIndex = primeVal % 5;
int scaleIndex = scales[selectedScale][currentChord][chordIndex];
int octaveOffset = (primeVal / 3) % octaveRange;
int finalIndex = scaleIndex + (octaveOffset * 7);
if (finalIndex >= 19) finalIndex = 18;
turingPitch[i] = rootNote * scaleRatios[finalIndex];
turingDecay[i] = 0.9f - (octaveOffset * 0.2f);
}
else {
GenerateSingleTuringStep(i);
}
}
}
void AdvanceStep() {
if (playDirection == 0) {
step = (step + 1) % stepCount;
}
else if (playDirection == 1) {
step = (step - 1 + stepCount) % stepCount;
}
else if (playDirection == 2) {
if (pendulumForward) {
step++;
if (step >= stepCount - 1) {
step = stepCount - 1;
pendulumForward = false;
}
} else {
if (step == 0) {
step = 1;
pendulumForward = true;
} else {
step--;
}
}
}
else if (playDirection == 3) {
step = rand() % stepCount;
}
}