Created
March 12, 2022 20:01
-
-
Save borogove/e7b64d50fcc1ffa18ea7527b9312d651 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Weirdolope - a minimal one-control envelope generator | |
// (c) 2022 Russell Borogove | |
// Use at your own risk | |
// This envelope generator uses floating point math; | |
// if your MCU doesn't have floating-point hardware | |
// support you're gonna have a bad time. | |
// Call updateEnvelope() periodically at a rate >= 1000 Hz | |
// Set envelopeState to ENVELOPE_STATE_ATTACK to | |
// begin the envelope and ENVELOPE_STATE_RELEASE to | |
// release it. | |
// envelopeLevel will range from 0.0 to 1.0. | |
// Implement setEnvelope() to scale that value | |
// appropriately and send it to your DAC. | |
// Thanks to "synthesizers, yo" user meem for | |
// getting me some nice clean samples of guitars | |
// and pianos that I could measure some | |
// representative envelope time curves from. | |
// attack rate, amplitude change per millisec | |
float AttackRateTable[9] = | |
{ | |
1.00000f, // instant | |
0.08333f, // guitar | |
0.04000f, // bass | |
0.01667f, // piano bass | |
0.10000f, // piano treble | |
0.20000f, // chiff organ | |
0.10000f, // synth lead | |
0.00250f, // synth pad | |
0.00050f, // long ambient pad | |
}; | |
float DecayRateTable[9] = | |
{ | |
0.96555f, // instant | |
0.99929f, // guitar | |
0.98851f, // bass | |
0.99934f, // piano bass | |
0.98746f, // piano treble | |
0.98000f, // chiff organ | |
0.99644f, // synth lead | |
0.99978f, // synth pad | |
0.99988f, // long ambient pad | |
}; | |
// End-of-decay level | |
float SustainLevelTable[9] = | |
{ | |
0.70f, // instant | |
0.72f, // guitar | |
0.75f, // bass | |
0.70f, // piano bass | |
0.70f, // piano treble | |
0.70f, // chiff organ | |
0.70f, // synth lead | |
0.80f, // synth pad | |
0.90f, // long ambient pad | |
}; | |
// Unlike a typical synth ADSR envelope, we use an | |
// exponentially decaying sustain. | |
float SustainRateTable[9] = | |
{ | |
0.99880f, // instant | |
0.99928f, // guitar | |
0.99957f, // bass | |
0.99913f, // piano bass | |
0.99015f, // piano treble | |
0.99977f, // chiff organ | |
0.99991f, // synth lead | |
1.0f, // synth pad | |
1.0f, // long ambient pad | |
}; | |
// Unlike a typical synth ADSR envelope, we use an | |
// exponentially decaying sustain | |
float ReleaseRateTable[9] = | |
{ | |
0.99312f, // instant | |
0.99421f, // guitar | |
0.99484f, // bass | |
0.99092f, // piano bass | |
0.97727f, // piano treble | |
0.99500f, // chiff organ | |
0.99500f, // synth lead | |
0.99650f, // synth pad | |
0.99800f, // long ambient pad | |
}; | |
int EnvA = 0; | |
int EnvB = 1; | |
float EnvAlpha = 0.0f; | |
const int ENVELOPE_STATE_IDLE = 0; | |
const int ENVELOPE_STATE_ATTACK = 1; | |
const int ENVELOPE_STATE_DECAY = 2; | |
const int ENVELOPE_STATE_SUSTAIN = 3; | |
const int ENVELOPE_STATE_RELEASE = 4; | |
int envelopeState = ENVELOPE_STATE_IDLE; | |
float envelopeLevel = 0.0f; | |
// silence the envelope when it reaches this level, well below 12 bit dac resolution. | |
float envelopeStopLevel = 0.0001f; | |
unsigned long nextEnvelopeUpdate = 0; | |
float lerp( float y0, float y1, float alpha ) | |
{ | |
return (y1-y0)*alpha + y0; | |
} | |
void setEnvelope() | |
{ | |
// unipolar | |
cvValues[ CV_CHANNEL_ENVELOPE ] = envelopeLevel; | |
} | |
void updateEnvelope() | |
{ | |
int envelopeControl = analogRead( PIN_ENVELOPE ); | |
float x = constrain( 8.0f * (float)(envelopeControl-10) / 1003.1f, 0.0f, 7.999f ); | |
EnvA = int(x); | |
EnvB = EnvA+1; | |
EnvAlpha = constrain( x - EnvA, 0.0f, 1.0f ); | |
long ttg = nextEnvelopeUpdate - micros(); | |
float delta = 0.0f; | |
float damp = 1.0f; | |
float sustainLevel = 0.7f; | |
if (ttg < 0) | |
{ | |
nextEnvelopeUpdate += 1000; | |
switch (envelopeState) | |
{ | |
case ENVELOPE_STATE_IDLE: | |
envelopeLevel = 0.0f; | |
break; | |
case ENVELOPE_STATE_ATTACK: | |
delta = lerp( AttackRateTable[EnvA], AttackRateTable[EnvB], EnvAlpha ); | |
envelopeLevel += delta; | |
if (envelopeLevel >= 1.0f) | |
{ | |
envelopeLevel = 1.0f; | |
envelopeState = ENVELOPE_STATE_DECAY; | |
} | |
break; | |
case ENVELOPE_STATE_DECAY: | |
damp = lerp( DecayRateTable[EnvA], DecayRateTable[EnvB], EnvAlpha ); | |
envelopeLevel *= damp; | |
sustainLevel = lerp(SustainLevelTable[EnvA],SustainLevelTable[EnvB],EnvAlpha); | |
if (envelopeLevel <= sustainLevel) | |
{ | |
envelopeState = ENVELOPE_STATE_SUSTAIN; | |
} | |
break; | |
case ENVELOPE_STATE_SUSTAIN: | |
damp = lerp( SustainRateTable[EnvA], SustainRateTable[EnvB], EnvAlpha ); | |
envelopeLevel *= damp; | |
if (envelopeLevel <= envelopeStopLevel) | |
{ | |
envelopeState = ENVELOPE_STATE_IDLE; | |
} | |
break; | |
case ENVELOPE_STATE_RELEASE: | |
damp = lerp( ReleaseRateTable[EnvA], ReleaseRateTable[EnvB], EnvAlpha ); | |
envelopeLevel *= damp; | |
if (envelopeLevel <= envelopeStopLevel) | |
{ | |
envelopeState = ENVELOPE_STATE_IDLE; | |
} | |
break; | |
} | |
setEnvelope(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment