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:
@@ -1,12 +1,13 @@
|
||||
// param_live.h — the AUDIO-THREAD half of a host parameter write: one exposed control patched
|
||||
// into the live block, in place, with no allocation and no lock. It exists because the model
|
||||
// layer cannot run on the audio thread (PlaySeconds carries velocity curves and spline contours,
|
||||
// so resolvePlay allocates), while `IParameterChanges` is delivered there.
|
||||
// param_live.h — a host parameter write landed on BOTH sides of the model/audio split: into the
|
||||
// live block in place (RT-safe, for `IParameterChanges`, which the SDK delivers on the audio
|
||||
// thread where the model path cannot run — `resolvePlay` allocates), and into the stored
|
||||
// parameter set. One norm -> stored map serves both, so they cannot disagree.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/instrument/engine/live_params.h"
|
||||
#include "core/instrument/ui/deck_groups.h" // DeckParam
|
||||
#include "core/instrument/map/play_seconds.h" // PlaySeconds (the model-side write target)
|
||||
#include "core/instrument/ui/deck_groups.h" // DeckParam
|
||||
|
||||
namespace reasampler::instrument::param {
|
||||
|
||||
@@ -16,14 +17,25 @@ using ui::DeckParam;
|
||||
// beyond the taper's own. Returns false for a control this block does not carry — master gain,
|
||||
// which reaches the audio as the processor's own atomic, and anything unexposed.
|
||||
//
|
||||
// The value laws are NOT restated here: `ui::storedFromNorm` is the same norm -> stored map
|
||||
// `setDeckParam` writes with, and `map::secondsToFrames` the same fold `resolvePlay` uses. What
|
||||
// IS new is the routing — which member of the block a control names — and that is pinned by an
|
||||
// exhaustive equivalence test against the model path over every exposed control, rather than by
|
||||
// two tables that happen to agree.
|
||||
// The value laws are NOT restated here: `hostStoredFromNorm` is the same norm -> stored map the
|
||||
// model-side write below takes, and `map::secondsToFrames` the same fold `resolvePlay` uses. What
|
||||
// IS new is the routing — which member of the block a control names — and its switch carries no
|
||||
// `default:`, so a control promoted into the parameter list without a route here fails to COMPILE
|
||||
// rather than dropping its automation silently at a call site that discards the answer.
|
||||
//
|
||||
// `sampleRate` is the rate the loaded capture was BUILT at (the processor's builtSampleRate_),
|
||||
// so a patched stage time lands on exactly the frames the build would have resolved.
|
||||
bool applyLiveParam(engine::LiveValues& block, DeckParam deck, double normalized, int sampleRate);
|
||||
|
||||
// The MODEL-side peer: the same host write, landed in the stored parameter set instead. Sharing
|
||||
// `hostStoredFromNorm` and the field resolvers with the patch above is what makes the equivalence
|
||||
// test's claim — patch == fold-after-write — a property of one map rather than of two that agree.
|
||||
// False for a control PlaySeconds does not carry: the two instance scalars (master gain, pitch
|
||||
// key-track) are written where they live, by the shell.
|
||||
//
|
||||
// No `enforceGateUnavailableWhileDrawn` here, unlike `ui::setDeckParam`: every control that can
|
||||
// flip `splineActive` is a toggle, every toggle is Reload-tier, and no Reload-tier control is
|
||||
// exposed — so nothing reachable from a host write can open that hole.
|
||||
bool writeHostParam(DeckParam deck, map::PlaySeconds& play, double normalized);
|
||||
|
||||
} // namespace reasampler::instrument::param
|
||||
|
||||
Reference in New Issue
Block a user