Bound the automation hold to the window the model has not caught up on, and make that authority model stated, enforced and tested
This commit is contained in:
@@ -342,9 +342,15 @@ the GUI-update channel (*"should update the according GUI element(s) only"*,
|
||||
`ivsteditcontroller.h`), and `ProcessData::inputParameterChanges` is the audio-side one — the
|
||||
SDK's own `SingleComponentEffect` sample services BOTH
|
||||
(`public.sdk/samples/vst/again/source/againsimple.cpp`), and so do we. The `setState` ordering
|
||||
half dissolves with it: an automation point held by the audio thread is re-applied over every
|
||||
merge, so a written lane outranks the restore whichever way round the two arrive, which is
|
||||
VST3's own authority rule rather than a race. **The audio thread cannot run the model path**
|
||||
half dissolves with it, but only because the hold is BOUNDED: an automation point held by the
|
||||
audio thread is re-applied over every merge until the UI thread folds it into the model, so a
|
||||
lane that is genuinely driving outranks the restore whichever way round the two arrive, while a
|
||||
lane that sent one point and had it folded does not. That is the authority rule read correctly —
|
||||
and note it is reasoning from the host's replay behaviour, not a header quote: the SDK does not
|
||||
state it. An unbounded hold makes the ordering claim true by making every later writer
|
||||
permanently deaf, which is not the same property. `shell/instrument/CLAUDE.md`'s Authority
|
||||
section is the model, and `core/instrument/param/param_merge` is where it is enforced.
|
||||
**The audio thread cannot run the model path**
|
||||
(`resolvePlay` copies velocity curves and spline contours, so it allocates), so the drain patches
|
||||
the live block in place through one pure RT-safe function whose routing is pinned by an
|
||||
exhaustive equivalence test against the model path.
|
||||
@@ -913,8 +919,12 @@ makes them automatable, since otherwise they would re-decode a WAV per automatio
|
||||
`InstrumentParams`, not `PlaySeconds`, so the host's write path could not reach it and id 1000
|
||||
would have no-oped in both directions with nothing failing to compile; both controls also had to
|
||||
reach the engine, which widened `LiveValues` and `foldLive`'s input and gave `Voice::start` the
|
||||
two latched values as arguments beside the rate. `param::valueHomeFor`, asserted over the
|
||||
exposed set, is what makes the next promotion of this shape a test failure instead of a silence.
|
||||
two latched values as arguments beside the rate. `param::valueHomeFor` is what makes the next
|
||||
promotion of this shape a test failure instead of a silence, and it earns that claim in three
|
||||
places rather than one: `test_param_live` asserts every exposed control HAS a home and that the
|
||||
instance-scalar set has exactly two members, and the shell's own read and write paths
|
||||
(`modelParamNormalized`, `writeDeckParamToModel`) now BRANCH on it rather than on a hardcoded
|
||||
control id — so a third instance scalar cannot appear without failing that count.
|
||||
|
||||
**Not promoted, and not proposed for promotion: Rate to Live.** §3.5 records the cost;
|
||||
that paragraph is the first thing to read if it is ever proposed.
|
||||
|
||||
Reference in New Issue
Block a user