// 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/map/play_seconds.h" // PlaySeconds (the model-side write target) #include "core/instrument/ui/deck_groups.h" // DeckParam namespace reasampler::instrument::param { using ui::DeckParam; // Writes `normalized` for `deck` into `block`. RT-SAFE: no allocation, no lock, no transcendental // 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: `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