amy.c: Chained oscs are evaluated backwards from the end of the chain. - #1200
Conversation
🎛️ AMY HW CI (AMYboard bench)Flashed this PR's AMY (LoadTestChord: 6-voice Juno ✅ PASS — the bench ran the test to completion.
Full chord settled render μs: 2614 (was 2627, Δ -0.5%) (peak 2618, 39 samples) ⬇️ Artifacts: serial log · load trace · report Self-hosted bench (amyboardci). FAIL means only that the test could not run — the load values are informational, with no threshold and no audio compare. See |
⛓️ tulipcc integration PR openedThis merge was pinned into tulipcc for full-system CI: shorepine/tulipcc#1382 Test it there and merge that PR to move tulipcc onto this AMY. |
Brings in amy up to 1.2.190 (e153f7d): the render lock (shorepine#1205), chained oscs evaluated tail-first (shorepine#1200), MIDI CC output and midi_cc naming an AMY parameter directly (shorepine#1175), a PCM render gain ramp (shorepine#1208), no osc allocation on the ingest path, and the docs that go with them. One conflict, in src/amy.c: upstream grew its own lock abstraction for ESP_PLATFORM (a FreeRTOS mutex behind lock_init/lock_take/lock_give) where we had a bare SemaphoreHandle_t plus esp_rom_sys.h. Took upstream's side, and dropped our a84e093 render-in-flight counter with it: upstream's render lock (f11a552, 2812743) closes the same FREE_OSC-under-render race more thoroughly -- the render thread holds it for a whole block and a patch load holds it across its flush -- and it was measured, where ours spun for up to 50 ms. Nothing else of ours changed; src/amy.c is now identical to upstream/main. f11a552 lists tulipcc's native render loops as needing the wrap themselves, but they do not: every tulipcc path reaches a render through an amy entry point that already takes the lock. The Tab5 sets platform.multithread = 0, so its audio task's amy_render_audio() takes the branch that grabs it around esp_render_on_cores() + amy_fill_buffer(); Tulip CC keeps the default multithread = 1 and renders in amy's own esp_fill_audio_buffer_task; AMYboard-in-VCV pulls blocks with amy_simple_fill_buffer(). All three are wrapped upstream. make ctest: all passed. make test: 135 tests pass, no failures, including our TestAllNotesOffNoteZero at err=-100.0 dB. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude informed me that, counter to my understanding, filter (and distortion) applied to the chain up until that point in the chain; I expected them to apply to everything after the filtering osc (so a filter osc at the head of a chain filters the entire chain). I'm not sure when this got turned around (since that's how the original Juno voices worked), but the fix (head-recursing on chained osc) simplified the code, since the only thing special about a SILENT osc now is that it applies its envelope to the cumulated sum.