11/* Lightweight software synthesizer */
22
3+ #include <math.h>
34#include <stdlib.h>
45#include <string.h>
56
@@ -240,6 +241,7 @@ void picosynth_note_off(picosynth_t *s, uint8_t voice)
240241#define PICOSYNTH_ENV_RATE_FROM_MS (ms ) \
241242 (PICOSYNTH_MS(ms) > 0 ? (((int64_t) Q15_MAX << 4) / PICOSYNTH_MS(ms)) \
242243 : ((int32_t) Q15_MAX << 4))
244+ #define PICOSYNTH_ENV_MIN_RATIO 1e-4
243245
244246/* Phase increments for octave 8; shift right for lower octaves */
245247#define BASE_OCTAVE 8
@@ -259,6 +261,50 @@ static const q15_t octave8_freq[NOTES_PER_OCTAVE] = {
259261 PICOSYNTH_HZ_TO_FREQ (7902.13 ), /* B8 */
260262};
261263
264+ /* Calculate exponential envelope multiplier for a given duration in samples
265+ * toward a target ratio relative to the starting value. */
266+ static q15_t env_calc_exp_coeff (uint32_t samples , double target_ratio )
267+ {
268+ if (samples < 10 )
269+ return Q15_MAX >> 1 ; /* Very fast decay */
270+ if (target_ratio < PICOSYNTH_ENV_MIN_RATIO )
271+ target_ratio = PICOSYNTH_ENV_MIN_RATIO ;
272+ if (target_ratio > 0.9999 )
273+ target_ratio = 0.9999 ;
274+
275+ double coeff = exp (log (target_ratio ) / (double ) samples );
276+ int32_t q = (int32_t ) (coeff * (double ) Q15_MAX + 0.5 );
277+ if (q < 0 )
278+ q = 0 ;
279+ if (q > Q15_MAX )
280+ q = Q15_MAX ;
281+ return (q15_t ) q ;
282+ }
283+
284+ /* Recalculate decay/release exponential coefficients to roughly match the
285+ * linear timing implied by the configured rates. */
286+ static void env_update_exp_coeffs (picosynth_env_t * env )
287+ {
288+ int32_t sus_abs = env -> sustain < 0 ? - env -> sustain : env -> sustain ;
289+ uint32_t sus_level = (uint32_t ) sus_abs << 4 ;
290+ uint32_t peak = (uint32_t ) Q15_MAX << 4 ;
291+ uint32_t decay_span = peak > sus_level ? peak - sus_level : 1 ;
292+
293+ uint32_t decay_samples =
294+ env -> decay > 0
295+ ? (decay_span + (uint32_t ) env -> decay - 1 ) / (uint32_t ) env -> decay
296+ : 1 ;
297+ double target = (double ) sus_level / (double ) peak ;
298+ env -> decay_coeff = env_calc_exp_coeff (decay_samples , target );
299+
300+ uint32_t release_samples =
301+ env -> release > 0
302+ ? (peak + (uint32_t ) env -> release - 1 ) / (uint32_t ) env -> release
303+ : 1 ;
304+ env -> release_coeff =
305+ env_calc_exp_coeff (release_samples , PICOSYNTH_ENV_MIN_RATIO );
306+ }
307+
262308q15_t picosynth_midi_to_freq (uint8_t note )
263309{
264310 if (note > 119 )
@@ -301,6 +347,7 @@ void picosynth_init_env(picosynth_node_t *n,
301347 n -> env .decay = decay ;
302348 n -> env .sustain = sustain ;
303349 n -> env .release = release ;
350+ env_update_exp_coeffs (& n -> env );
304351}
305352
306353void picosynth_init_env_ms (picosynth_node_t * n ,
@@ -471,10 +518,10 @@ q15_t picosynth_process(picosynth_t *s)
471518 if (n -> env .block_counter == 0 ) {
472519 n -> env .block_counter = PICOSYNTH_BLOCK_SIZE ;
473520 if (!v -> gate ) {
474- n -> env .block_rate = - n -> env .release ;
521+ n -> env .block_rate = - n -> env .release ; /* Informational */
475522 } else if (((uint32_t ) n -> state ) &
476523 ENVELOPE_STATE_MODE_BIT ) {
477- n -> env .block_rate = - n -> env .decay ;
524+ n -> env .block_rate = - n -> env .decay ; /* Informational */
478525 } else {
479526 n -> env .block_rate = n -> env .attack ;
480527 }
@@ -483,20 +530,24 @@ q15_t picosynth_process(picosynth_t *s)
483530
484531 /* Apply rate */
485532 int32_t val = n -> state & ENVELOPE_STATE_VALUE_MASK ;
486- val += n -> env .block_rate ;
487-
488533 if (v -> gate ) {
489534 uint32_t mode =
490535 ((uint32_t ) n -> state ) & ENVELOPE_STATE_MODE_BIT ;
491536 if (mode ) {
492- /* Decay: clamp to sustain level */
493537 q15_t sus_abs = n -> env .sustain < 0 ? - n -> env .sustain
494538 : n -> env .sustain ;
495539 int32_t sus_level = sus_abs << 4 ;
540+ int32_t delta = val - sus_level ;
541+ /* Exponential decay of delta toward sustain */
542+ val =
543+ sus_level +
544+ (int32_t ) (((int64_t ) delta * n -> env .decay_coeff ) >>
545+ 15 );
496546 if (val < sus_level )
497547 val = sus_level ;
498548 } else {
499549 /* Attack: check for transition to decay */
550+ val += n -> env .block_rate ;
500551 if (val >= (int32_t ) Q15_MAX << 4 ) {
501552 val = (int32_t ) Q15_MAX << 4 ;
502553 mode = ENVELOPE_STATE_MODE_BIT ;
@@ -506,8 +557,10 @@ q15_t picosynth_process(picosynth_t *s)
506557 }
507558 n -> state = (int32_t ) (((uint32_t ) val ) | mode );
508559 } else {
509- /* Release: clamp to zero */
510- if (val < 0 )
560+ /* Exponential release */
561+ val = (int32_t ) (((int64_t ) val * n -> env .release_coeff ) >>
562+ 15 );
563+ if (val < 16 )
511564 val = 0 ;
512565 n -> state = val ;
513566 }
0 commit comments