Skip to content

Effects: a jump of Mix or a level walks over 20 ms in the older classes - #15

Merged
bdbarnett merged 3 commits into
mainfrom
mix-ramp
Nov 10, 2026
Merged

bdbarnett merged 3 commits into
mainfrom
mix-ramp

Conversation

@bdbarnett

@bdbarnett bdbarnett commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

A jump of Mix or a level now walks over 20 ms instead of stepping in Bitcrusher, Chorus, CombFilter, Compressor, DeEsser, Distortion, DynamicEQ, Exciter, Fuzz, MultibandCompressor, Overdrive, Phaser, Saturation, Tremolo and Vibrato. It's the first stage of the plan in docs/how-it-works/control-smoothing.md, built on the node ramps in PyDevices/audiodsp#400, which AUDIODSP_PIN now names.

The walks live in audiodsp's nodes, so they run every sample on every interpreter. The effects set level_ramp on the mixers that carry their Mix and levels (_component.ramp_levels), and use the nodes' own walks for a filter's mix, a shaper's post gain and a compressor's makeup. Mix 0 is still the source byte for byte: a class that hands you the source there now walks its graph to the dry first and switches with Port.play(after=) once the walk has landed, then walks back in from the dry when Mix leaves 0 (WALKS_TO_BYPASS in _component.py). Overdrive now does this too; it used to cut straight to the source.

How much it changes, rendered on CPython at 48 and 22.05 kHz with a triangle going in, as the largest step at the move over the triangle's own steepest step (1.5 is the lifecycle matrix's limit): Saturation's Mix to 0 went from 61.8 to 0.79, MultibandCompressor's Mix to 0 from 50.2 to 0.77, Compressor's Makeup to full from 34.6 to 0.36, Phaser's Mix to 0 from 21.3 to 0.99, Vibrato's Level to 0 from 18.9 to 0.92, Overdrive's Mix to 0 from 2.3 to 0.80. Every rendered jump of a Mix or level is at or under 1.09, except Tremolo's Depth.

The lifecycle matrix learns the walk. P1 judges Mix 0 from where the walk has landed. P4 gives a moved control the 20 ms walk on top of its bound. P5 also allows the step a class makes when it lands on a louder setting at once, as a fresh instance built there would, so a Makeup or Gain jump that walks is no longer read as a click; the new LoudJump plant shows the clause still catches a cut. PR's walk-in starts after the walk.

A few tests change with the walk. The stale-rejoin plants in tests/support/stale_blocks.py switch at once, as the code they stand for did, since a walk to the dry and back fades what a stale graph held. test_first_pull renders 8192 frames, so Overdrive's mono walk lands inside it. Overdrive's T5 fault check reads the clip branch's level only where the graph plays, since Mix 0 now walks it out.

Other classes move with the nodes. DigitalDelay's and PingPongDelay's Mix and SlapbackDelay's Level walk now, since the delay node does, so their docstrings and claim tests say a jump walks. A GraphicEQ band coming back from its detent fades in, and the stale state its _wake fault leaves fades in with it: at 31 Hz the fault now departs from the class by 0.17 and 0.19 of the settled level, so that test's bar is a tenth of it, while the crossing-free control still holds at exactly zero. The walk to the dry is rounded up to whole 256-frame blocks, so the switch to the source lands on a block edge whatever the source's buffer size. KNOWN_RED is recounted from a quick run of all 45 classes on this branch: 51 rows clear, 84 shrink or move, and four are new. Exciter's patches 1 and 4 now step on arrival, since the patch's levels walk while its switch lands at once, and Fuzz's Mix, Level or patch set after it is built and before the first pull walks for 20 ms from the built value: its build charges the output pole through the shapers, so they have already played a block. Both are listed with those reasons; patch changes that crossfade are the later stage that clears the first.

Still stepping, and on the roadmap: Fuzz on its way back from Mix 0, Tremolo's Depth, filter corners and other non-level controls, and patch changes that rebuild part of a graph. DeEsser's Range back from 0 at 22.05 kHz is unchanged from main.

What I ran: the lifecycle matrix (quick) for the 15 classes on main and on this branch, for the events that move a control, and the whole quick matrix for all 45 classes on this branch; the tests of the 15 classes, of GraphicEQ and the three delays, the plant tests, tests/test_level_ramp.py, tools/validate_api.py, tests/parity/effects_library_smoke.py and flake8. Not verified: the MicroPython and CircuitPython runs of the matrix, which a manual run of every class checks; and nothing on a board.

Bitcrusher, Chorus, CombFilter, Compressor, DeEsser, Distortion,
DynamicEQ, Exciter, Fuzz, MultibandCompressor, Overdrive, Phaser,
Saturation, Tremolo and Vibrato set level_ramp on the mixers that carry
their Mix and levels and use the nodes' own walks, so a jump no longer
clicks. Mix 0 is still the source: a class that hands the source over
walks its graph to the dry first and switches once the walk has landed
(Port.play(after=)), then walks back in from the dry.

AUDIODSP_PIN moves to the audiodsp commit with the node ramps. The
lifecycle matrix learns the walk: P1 judges Mix 0 from where it lands, P4
allows the 20 ms on top of its bound, P5 allows the step of landing on a
louder setting at once, and PR's walk-in starts after the walk.
…the fifteen

The delay node's Mix walks now, so DigitalDelay's and PingPongDelay's Mix and SlapbackDelay's Level say a jump walks, and their claim tests check it. A GraphicEQ band back from its detent fades in, so the wake fault's test bar is a tenth of the settled level. KNOWN_RED is recounted from a quick run of all 45 classes. The pin moves to audiodsp#400 rebased onto main.

Ran the four classes' tests, and the lifecycle table check for Fuzz, Exciter, Overdrive and Compressor at the new pin.
@bdbarnett
bdbarnett merged commit 9986763 into main Nov 10, 2026
8 checks passed
@bdbarnett
bdbarnett deleted the mix-ramp branch November 10, 2026 14:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant