fix(host): BakedGraphProcessor survives in-place buffers + resets node state on prepare#6408
Merged
Merged
Conversation
…e state on prepare Two correctness fixes for baked-graph plugins, both found via a downstream consumer but affecting any Pulp plugin built on BakedGraphProcessor: 1. In-place (aliased) host buffers no longer produce silence. Hosts like Logic (AUv2, some AUv3) hand process() input and output views over the SAME memory. process_routed() zeroes the main output bus before its AudioInput gather reads the input bus, so the zero destroyed the aliased input and the plugin emitted total silence. process() now detects any input-channel/output-channel overlap and reads the input from a scratch copy sized in prepare() — the audio thread does only pointer compares and copy_n, no allocation. Handles mono, multi-channel, and differing in/out channel counts. 2. prepare() re-inits stateful Custom-node DSP state. bake() captured only the opaque process callback, so a re-prepare rebuilt the routing snapshot and buffer pool but never re-ran the custom instance's prepare/reset — stale state (e.g. a delay line's contents) recirculated into the fresh stream, violating the header's documented starts-from-zero contract. bake() now also captures per-node CustomNodeLifecycle closures (the type's prepare/reset bound to the instance) and prepare() runs them: prepare at the host's REAL rate/block (load_baked previously left instances prepared at its nominal 48k/512), then reset. Mirrors SignalGraph::prepare()'s own instance step. Both fixes ship with fail-without-fix-verified tests in test_baked_graph_processor_parity.cpp: aliased mono/stereo blocks must be the processed signal (not silence, and allocation-free under the RT probe), and a silent block after re-prepare must carry no residue from a stateful delay node. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015tUppDEybTgFDxS5BGKBWV
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two correctness fixes for baked-graph plugins, both found via a downstream
consumer but affecting any Pulp plugin built on BakedGraphProcessor:
In-place (aliased) host buffers no longer produce silence. Hosts like
Logic (AUv2, some AUv3) hand process() input and output views over the
SAME memory. process_routed() zeroes the main output bus before its
AudioInput gather reads the input bus, so the zero destroyed the aliased
input and the plugin emitted total silence. process() now detects any
input-channel/output-channel overlap and reads the input from a scratch
copy sized in prepare() — the audio thread does only pointer compares and
copy_n, no allocation. Handles mono, multi-channel, and differing in/out
channel counts.
prepare() re-inits stateful Custom-node DSP state. bake() captured only
the opaque process callback, so a re-prepare rebuilt the routing snapshot
and buffer pool but never re-ran the custom instance's prepare/reset —
stale state (e.g. a delay line's contents) recirculated into the fresh
stream, violating the header's documented starts-from-zero contract.
bake() now also captures per-node CustomNodeLifecycle closures (the
type's prepare/reset bound to the instance) and prepare() runs them:
prepare at the host's REAL rate/block (load_baked previously left
instances prepared at its nominal 48k/512), then reset. Mirrors
SignalGraph::prepare()'s own instance step.
Both fixes ship with fail-without-fix-verified tests in
test_baked_graph_processor_parity.cpp: aliased mono/stereo blocks must be
the processed signal (not silence, and allocation-free under the RT probe),
and a silent block after re-prepare must carry no residue from a stateful
delay node.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_015tUppDEybTgFDxS5BGKBWV
🔎 Provenance
claudem5~/Code/pulp-baked-fix14ef2f39-9113-4e61-bd8b-8faab97cab00Resume
Jump to this tab
Relaunch (any agent)
Restore URL — https://claude.ai/code/session_015tUppDEybTgFDxS5BGKBWV
stamped 2026-07-20 20:33 UTC