[sdiy] software / firmware envelopes in C (or anything else)
Chris McDowell
declareupdate at gmail.com
Thu Nov 6 21:22:46 CET 2025
Love it, nice cheap exp approximation. Are you running your whole synth on the G0?
Cheers,
Chris
> On Nov 6, 2025, at 2:16 PM, Jonatan Liljedahl <lijon at kymatica.com> wrote:
>
> In my WIP polysynth project, I wanted full flexibility with
> multi-stage envelope as in DX7, but with a curve setting per segment.
> (as well as a bunch of other features)
> So I do a ramp for each segment, apply curve bending, and then lerp it
> into the correct from-to range. All in fixed point math, because it's
> running on an STM32G0. It's a lot more operations than the typical
> simple RC envelope, but I didn't find a cheaper way to get this
> flexibility and curve shaping.
>
> env tick:
>
> int32_t y = e->phase >> 16;
> int32_t K = k_lut[16+s->curve]; // K = 1 / (1-exp2(x))
> y = K * ((1<<16) - exp2_s32_cheap(s->curve * y)) >> 15; // y = K *
> (1-exp2(x * curve))
>
> lut:
>
> static int32_t k_lut[32] = { 32768, 32769, 32770, 32772, 32776, 32784,
> 32800, 32832, 32896, 33026, 33288, 33825, 34952, 37449, 43690, 65536,
> 0, -32768, -10922, -4681, -2184, -1057, -520, -258, -128, -64, -32,
> -16, -8, -4, -2, -1};
>
> cheap exp2:
>
> uint32_t exp2_s32_cheap (int32_t x) {
> const int32_t c = 0x57d8;
> int32_t i = x >> 16;
>
> x &= 0xffff;
> int32_t y = x*x;
> y -= x << 16;
> y >>= 16;
> y *= c;
> y >>= 16;
> y += x + 0x10000;
> if(i<0) return y >> -i;
> else return y << i;
> }
>
>> On Wed, Nov 5, 2025 at 11:00 PM Chris McDowell via Synth-diy
>> <synth-diy at synth-diy.org> wrote:
>>
>> Howdy list,
>>
>> I've been thinking about this for about a decade. I use what the web calls a "leaky integrator" all over my synth code. I first used it as the basis of an envelope some 15 years ago when mimicking the Moog Rogue's strategy of providing a target and a "rate" to a lowpass filter built around a 3080 OTA. On an MCU with an FPU, it works pretty much great. the basics:
>>
>> output += (target - output) * rate
>>
>> where target changes based on what stage of an envelope we're in and rate is some small float coefficient calculated based on desired stage length in milliseconds.
>>
>> my question for this deep well of synth experience: is there a better way to do it? I came across a similar strategy on musicdsp.org recently that suggested baking the target into the coefficient, which does indeed save one subtract per iteration with the tradeoff that you have to do more math when changing envelope states. its use of log() (admittedly infrequently) makes me not all that interested, even if that's maybe a little irrational. If I find myself num_envelopes cycles away from meeting a timing deadline, I'll consider it...
>>
>> I'm familiar with Tom Wiltshire's envelope chip, which is another cool and very different strategy. What else is out there?!
>>
>> Cheers,
>> Chris McDowell
>> ________________________________________________________
>> This is the Synth-diy mailing list
>> Submit email to: Synth-diy at synth-diy.org
>> View archive at: https://synth-diy.org/pipermail/synth-diy/
>> Check your settings at: https://synth-diy.org/mailman/listinfo/synth-diy
>> Selling or trading? Use marketplace at synth-diy.org
>
>
>
> --
> /Jonatan
> http://kymatica.com
More information about the Synth-diy
mailing list