fabric::Ramp::Set overflows on a balance moved hard left to hard right: a step, not a ramp #172
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
bcox/tymbal#172
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
fabric::Ramp::Setinfabric/inserts.hcomputes its step asraw()is the value's 32-bit raw fordsp::Sampleandfabric::Gain, so the subtraction is done inintbefore it is widened intostep_.What goes wrong
Ramp<dsp::Sample>(StripCore::balance_), Q1.30, so hard left is -2^30 and hard right is +2^30.int: signed overflow, undefined behavior.Ramp<Gain>stays in range for non-negative gains.Reachability
project/model.hadmits a balance in [-1, 1], and the fabric sets it withSetBalance(Sample::FromDouble(event.value))with no clamp short of the ends. An automation lane, a MIDI pan sweep or a control dragged end to end in one event produces it.Evidence
Found on 2026-10-07 while cosimulating
rtl/fabric/strip_skeletonagainstfabric::ChannelStrip(branchde25-va-build-budget). The task's reviewer reproduced it with a twelve-line program underclang++ -fsanitize=undefined:at
Ramp::Set. The repo's--config=ubsanlane has not reported it, so no test moves a balance end to end in one event.Fix shape
Widen to
std::int64_tbefore subtracting.step_is already 64 bits, andTickadds it tocurrent_.raw()and lands on the target exactly on the last tick, so nothing else changes.A test: set a balance hard left, snap, set hard right, and hold each of the 64 ticks to the line between them; inject the 32-bit subtraction and watch it fail.
Where else it is recorded
docs/superpowers/ledgers/2026-10-03-fabric-prices.md, "Minors left unfixed".rtl/fabric/strip_skeleton/sim/tb_strip_skeleton.cckeeps every ramp target inside 2^31 of where its ramp stands, so the cosim does not hold the header past that. A mixer's RTL takes the corrected arithmetic.