From d2b4e3291d17b86e496bcfe814f78ef72cb84dd1 Mon Sep 17 00:00:00 2001 From: Brad Barnett <127794626+bdbarnett@users.noreply.github.com> Date: Fri, 23 Oct 2026 12:07:44 -0500 Subject: [PATCH] audiograph: round without lrintf so RP2350 CircuitPython links (#311) CircuitPython's raspberry_pi_pico2 port links no lrintf, so building audiodsp there failed at link with an undefined reference from audiodsp_graph.o. The graph runner called it in two places: turning a sample-and-hold rate in Hz into a whole number, and rounding a clamped float sample to int16. Both now go through a small static inline that rounds to nearest, ties to even, which is what lrintf does in the default rounding mode. It matches lrintf for every float up to 2^24 in magnitude (checked exhaustively), so the render is the same on every build. --- src/shared/audiodsp_graph.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/shared/audiodsp_graph.c b/src/shared/audiodsp_graph.c index c9a9bc5..6a5f502 100644 --- a/src/shared/audiodsp_graph.c +++ b/src/shared/audiodsp_graph.c @@ -10,6 +10,25 @@ #include #include +// Round to the nearest integer, ties to even: what lrintf gives in the default +// rounding mode, without calling it. CircuitPython's RP2350 port links no +// lrintf, so a call to it leaves the firmware unable to link (the same trap +// audiodsp_trig.c steps round for fmod). Callers pass a finite value well +// inside int32_t range: a sample rate in Hz, or a sample already clamped to +// the int16 range. A NaN gives 0, as lrintf's result does once cut to int16. +static inline int32_t round_even(float v) { + if (!(v == v)) { + return 0; + } + float a = v < 0.0f ? -v : v; + int32_t i = (int32_t)a; + float frac = a - (float)i; // exact: a and i are within a binade + if (frac > 0.5f || (frac == 0.5f && (i & 1))) { + ++i; + } + return v < 0.0f ? -i : i; +} + // ---------------------------------------------------------------- nodes -- typedef struct { @@ -485,7 +504,7 @@ static void derive(audiodsp_graph_t *graph, audiodsp_graph_node_t *n) { uint32_t rate = 0; if (hz >= 0.5f) { rate = hz >= (float)graph->sample_rate - ? graph->sample_rate : (uint32_t)lrintf(hz); + ? graph->sample_rate : (uint32_t)round_even(hz); } if (rate != d->rate) { d->rate = rate; @@ -515,7 +534,7 @@ static inline int16_t sat16(float v) { if (v < -32768.0f) { return -32768; } - return (int16_t)lrintf(v); + return (int16_t)round_even(v); } static void run_env(audiodsp_graph_node_t *n, const int16_t *gate, int16_t *out,