Effects program, Phase 0 — a surviving node ask. One issue per surviving node ask is the roadmap's last Phase 0 work item; this is one of eight. The candidate survived a two-refuter pass (audiocomponents/docs/effects-survey.md §"The node list"), and the body below is its design sketch, copied verbatim from that survey's §"Design sketches (survivors)".
Standing rules this ask is written under. It is additive, in an audioif-own module (D1, Brad 2026-09-06). No CircuitPython-ported node is modified — not by this ask and not by any of the 46 seeds. Defaults are chosen so an unset node is byte-identical to today, and the parity golden's diff must add only the new probe's digests with every existing digest unchanged. Nothing here is built before Phase 1.
Candidates 11–18 and 20–22, all surviving; 16, 20, 21 and 11 on a 1–1 split, each split reported in the survey with the losing argument intact.
| # |
Option |
Traits it unblocks |
| 11 |
DYN_TRANSIENT time constants exposed |
Exciter T5 (tau clause) |
| 12 |
RMS detector |
Compressor V3, Expander E2, DeEsser D6 |
| 13 |
feedback detector topology |
Compressor F4, O5, M4, V5 |
| 14 |
BS.1770 true-peak detector |
Limiter L1 |
| 15 |
side-chain band (12 dB/oct + LP corner + key) |
Expander E3, NoiseGate G3, DeEsser D2 |
| 16 |
depth_db floor |
Expander E4 (NoiseGate G4 refuted by one vote) |
| 17 |
four-stage gate envelope with hold |
NoiseGate G1/G2/G5/G7, Expander E5/E6 |
| 18 |
relative-threshold detection |
DeEsser D1, D2 |
| 20 |
program-dependent attack |
DeEsser D5 |
| 21 |
simultaneous attack + sustain gains |
TransientShaper T2 |
| 22 |
peak-hold on the slow envelope |
TransientShaper T4 |
Split before building. The sketch below is written for candidate 11 only. The other ten survivors are carried under it and have no sketch of their own — RMS detection, the feedback topology, the true-peak FIR, the side-chain band and key, the depth_db floor, the gate state machine, relative-threshold detection, the program-dependent attack, the second envelope pair and the peak-hold each need their own API line, state field and probe case. Phase 1 writes them before writing the C; the cost and latency arguments are in the survey's verdicts.
Default-off is load-bearing here. Any option added to audiodynamics must default off so dynamics_probe.py still hashes to vstaudio_dsp.c compiled unmodified (roadmap Phase 1 Work).
Design sketch (verbatim from the survey)
Module audiodynamics — four additive options on the existing own node.
API fast_attack_ms=1.0, fast_release_ms=50.0, slow_attack_ms=25.0, slow_release_ms=300.0, constructor and set(), plain floats (Dynamics.c:19-31 is scalar). Defaults are exactly the literals at audioif_dynamics.c:215/:217/:219/:221, so an unset node is byte-identical to today.
DSP Move those four audioif_dynamics_ms_to_coef() calls from process entry into audioif_dynamics_config_t as fast_attack_coef etc. Four enum entries appended after AUDIOIF_DYNAMICS_OPT_TRUE_PEAK (audioif_dynamics.h:36-47; that order is the bindings' keyword order, so append only), four rows in dynamics_option_names[]. Keep the sentinel: config_init leaves them 0.0f, config_finish fills 1/50/25/300 if still zero, as attack/release at :42-51. Arithmetic unchanged: float coefficients and envelopes (:286-289). Python owns Exciter's Transient Time macro — tau maps to the four times at the literals' ratios (tau/50, tau, tau/2, 6*tau); C sees four milliseconds.
Latency Zero: one-pole followers. lookahead_ms stays the node's only latency option, default 0 (audioif_dynamics.h:73-76).
Probe Extend dynamics_extras_probe.py, not dynamics_probe.py — the oracle never had these (docstring 5-10). Case 1 sets none and must match dynamics_probe.py's transient checksums byte for byte; then each option alone; then tau = 8/20/150 ms, measuring the step reaching within 1 dB of steady state in tau +/-25% (Exciter T5's clause). Planted fault: swap fast_att and slow_att, that measurement must fail.
Cost Nothing per sample; it removes 4 expf per process() call (:215-222). The transient path stays 256 frames x (2 muls :286-289 + 2 logf :291-292) + 256 expf (:310) per stereo block.
Note: this sketch is written for candidate 11. The other ten Dynamics survivors are carried under it and do not have sketches of their own — RMS detection, the feedback topology, the true-peak FIR, the side-chain band and key, the depth_db floor, the gate state machine, relative-threshold detection, the program-dependent attack, the second envelope pair and the peak-hold each need their own API line, state field and probe case. Phase 1 must write them before the audioif issues are filed; the cost and latency arguments are in the verdicts above.
Where this lands. Phase 1 of the effects program (docs/effects-roadmap.md Part II, Phase 1 — "The palette"). audioif is released once, at Phase 7, when the effects work is complete, so this node ships in that release and the AUDIOIF_PIN moves to the node commit at Gate 1, not to a tag.
Effects program, Phase 0 — a surviving node ask. One issue per surviving node ask is the roadmap's last Phase 0 work item; this is one of eight. The candidate survived a two-refuter pass (
audiocomponents/docs/effects-survey.md§"The node list"), and the body below is its design sketch, copied verbatim from that survey's §"Design sketches (survivors)".Standing rules this ask is written under. It is additive, in an audioif-own module (D1, Brad 2026-09-06). No CircuitPython-ported node is modified — not by this ask and not by any of the 46 seeds. Defaults are chosen so an unset node is byte-identical to today, and the parity golden's diff must add only the new probe's digests with every existing digest unchanged. Nothing here is built before Phase 1.
Candidates 11–18 and 20–22, all surviving; 16, 20, 21 and 11 on a 1–1 split, each split reported in the survey with the losing argument intact.
DYN_TRANSIENTtime constants exposedExciterT5 (tau clause)CompressorV3,ExpanderE2,DeEsserD6CompressorF4, O5, M4, V5LimiterL1ExpanderE3,NoiseGateG3,DeEsserD2depth_dbfloorExpanderE4 (NoiseGateG4 refuted by one vote)NoiseGateG1/G2/G5/G7,ExpanderE5/E6DeEsserD1, D2DeEsserD5TransientShaperT2TransientShaperT4Split before building. The sketch below is written for candidate 11 only. The other ten survivors are carried under it and have no sketch of their own — RMS detection, the feedback topology, the true-peak FIR, the side-chain band and key, the
depth_dbfloor, the gate state machine, relative-threshold detection, the program-dependent attack, the second envelope pair and the peak-hold each need their own API line, state field and probe case. Phase 1 writes them before writing the C; the cost and latency arguments are in the survey's verdicts.Default-off is load-bearing here. Any option added to
audiodynamicsmust default off sodynamics_probe.pystill hashes tovstaudio_dsp.ccompiled unmodified (roadmap Phase 1 Work).Design sketch (verbatim from the survey)
Module
audiodynamics— four additive options on the existing own node.API
fast_attack_ms=1.0, fast_release_ms=50.0, slow_attack_ms=25.0, slow_release_ms=300.0, constructor andset(), plain floats (Dynamics.c:19-31 is scalar). Defaults are exactly the literals at audioif_dynamics.c:215/:217/:219/:221, so an unset node is byte-identical to today.DSP Move those four
audioif_dynamics_ms_to_coef()calls from process entry intoaudioif_dynamics_config_tasfast_attack_coefetc. Four enum entries appended afterAUDIOIF_DYNAMICS_OPT_TRUE_PEAK(audioif_dynamics.h:36-47; that order is the bindings' keyword order, so append only), four rows indynamics_option_names[]. Keep the sentinel:config_initleaves them 0.0f,config_finishfills 1/50/25/300 if still zero, as attack/release at :42-51. Arithmetic unchanged: float coefficients and envelopes (:286-289). Python owns Exciter's Transient Time macro — tau maps to the four times at the literals' ratios (tau/50, tau, tau/2, 6*tau); C sees four milliseconds.Latency Zero: one-pole followers.
lookahead_msstays the node's only latency option, default 0 (audioif_dynamics.h:73-76).Probe Extend
dynamics_extras_probe.py, notdynamics_probe.py— the oracle never had these (docstring 5-10). Case 1 sets none and must matchdynamics_probe.py'stransientchecksums byte for byte; then each option alone; then tau = 8/20/150 ms, measuring the step reaching within 1 dB of steady state in tau +/-25% (Exciter T5's clause). Planted fault: swap fast_att and slow_att, that measurement must fail.Cost Nothing per sample; it removes 4
expfper process() call (:215-222). The transient path stays 256 frames x (2 muls :286-289 + 2logf:291-292) + 256expf(:310) per stereo block.Where this lands. Phase 1 of the effects program (
docs/effects-roadmap.mdPart II, Phase 1 — "The palette"). audioif is released once, at Phase 7, when the effects work is complete, so this node ships in that release and theAUDIOIF_PINmoves to the node commit at Gate 1, not to a tag.