Commitf39cce1eRecorded18 Sep 2026Repositorysigil-dsp
chorus: keep the tap read position inside the delay line
Message
A read position a few float ulps below zero rounded to exactly bufsize after the wrap, indexing one past the buffer. Found by review; an exhaustive index probe hit it 2,425 times in 25M (position, delay) pairs and 0 after the fix. Also document that comp applies makeup at ratio 1 and treats a ratio below 1 as bypass.
Changed
src/c/fx4.c | 3 +++
src/c/fx4.h | 3 ++-
2 files changed, 5 insertions(+), 1 deletion(-)Diff
src/c/fx4.cmodified
@@ -44,6 +44,9 @@ static inline SPFLOAT chorus_tap(const sp_chorus *p, SPFLOAT delay)
44
if (delay > maxd) delay = maxd; 45
SPFLOAT rpos = (SPFLOAT)p->pos - delay; 46
while (rpos < 0.0f) rpos += (SPFLOAT)p->bufsize;+47
/* A tiny negative rpos rounds to exactly bufsize after the add+48
* (float ulp at ~1768), which would index one past the buffer. */+49
if (rpos >= (SPFLOAT)p->bufsize) rpos -= (SPFLOAT)p->bufsize; 50
int i0 = (int)rpos; 51
SPFLOAT frac = rpos - (SPFLOAT)i0; 52
int i1 = i0 + 1;src/c/fx4.hmodified
@@ -11,7 +11,8 @@
11
* rate reduction. compute(in, drive, bits, rate_div) 12
* sp_comp feed-forward peak compressor with a one-pole attack/release 13
* envelope. compute(in, threshold_db, ratio); attack, release−14
* and makeup gain are init-time.+14
* and makeup gain are init-time; makeup is applied even at+15
* ratio 1, and a ratio below 1 is a bypass, not expansion. 16
* 17
* Deterministic: no randomness, no libm on the per-sample path except 18
* tanhf in the crush drive stage and the dB conversions in the compressor