Skip to content

Change the patch of a graph the audio pump plays - #5

Merged
bdbarnett merged 4 commits into
mainfrom
graph/pump-swap
Oct 31, 2026
Merged

bdbarnett merged 4 commits into
mainfrom
graph/pump-swap

Conversation

@bdbarnett

@bdbarnett bdbarnett commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #4. A rack attached to the audio pump can now change its patch while it plays: plug or pull a cable, add or remove a module.

recompile() on an attached graph compiles the new graph while the pump plays on, then parks the pump at a block boundary, carries the state and trades patches with Graph.swap. Whatever pulls the graph (the pump, an output, a mixer) keeps the same object and hears the new patch from its next block. An instrument's connect(), disconnect(), add_module() and remove_module() now work with output="graph"; every change made in one turn lands together on the block after the turn. A module that still runs in Python is refused, and the old patch plays on.

It needs audiodsp's Graph.swap and carry-while-parked, PyDevices/audiodsp#350, which isn't released yet. Without it an attached recompile raises RuntimeError as before, and the new tests skip (MicroPython) or fail (CPython) on an audiodsp without it.

What was checked. tools/pump_swap.py plays the cable phrase on the pump, plugs a cable and opens an attenuator in one held batch mid-phrase, and compares every block the pump played with a rack built with the cable whose attenuator opens at the block the swap reports:

CPython (service pump) MicroPython unix (pthread pump)
swap block 40, byte-equal block 64, byte-equal
planted: state not carried caught at 40 caught at 64
planted: swap a block late caught at 40 caught at 70
planted: carry without parking no fault (nothing renders between calls) refused, old patch plays on whole

No block was dropped (the ring's overflow count stays 0). tests/test_graph_pump_swap.py (and its MicroPython twin) hold these. Scoped runs: test_graph_pump_swap, test_graph_live_patch, test_graph_pump and test_adapter pass on CPython (199); the three MicroPython modules pass.

What it costs. Eight swaps of a held chord per voice. "Held" is the time from asking the pump to park to letting it go, so how long it could not render:

voice nodes CPython held (worst) compile MicroPython held (worst) compile
Mono101 26 0.045 (0.067) ms 0.52 ms 0.30 (0.41) ms 1.6 ms
DuoSynth 58 0.10 (0.13) ms 0.78 ms 0.45 (0.54) ms 2.5 ms
Poly6 99 0.20 (0.32) ms 1.9 ms 0.45 (0.51) ms 5.5 ms
PolyXa 150 0.13 (0.15) ms 2.6 ms 0.69 (1.01) ms 8.6 ms
Poly8 202 0.16 (0.18) ms 2.6 ms 0.63 (0.71) ms 8.1 ms
PolyA6 676 0.42 (0.54) ms 7.0 ms 1.49 (1.73) ms 54 ms

The compile runs while the pump plays, so only "held" eats into the ring's headroom.

@bdbarnett
bdbarnett deleted the branch graph/live-patch October 31, 2026 07:08
@bdbarnett bdbarnett closed this Oct 31, 2026
@bdbarnett bdbarnett reopened this Oct 31, 2026
@bdbarnett
bdbarnett changed the base branch from graph/live-patch to main October 31, 2026 07:09
@bdbarnett
bdbarnett merged commit a586e41 into main Oct 31, 2026
1 check passed
@bdbarnett
bdbarnett deleted the graph/pump-swap branch October 31, 2026 07:24
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