Commit the limiter's audible state on the click and defer only the host's latency restart
setInstrumentParams now arms a sticky pending restart that flushLatencyRestart drains from the sync tick; setState and the bake's adopt flush at their own tails.
This commit is contained in:
@@ -86,6 +86,10 @@ tresult PLUGIN_API ReaSamplerProcessor::setState(IBStream* state) {
|
||||
// A new blob is new facts — the legacy lift gets one fresh run per restored state.
|
||||
legacyLiftConcluded_.store(false, std::memory_order_relaxed);
|
||||
reloadInstrument();
|
||||
// This caller has no editor to flush for it. At the TAIL on purpose: a host that services the
|
||||
// restart synchronously deactivates/reactivates, and our setActive(true) reloads — from the
|
||||
// refs above, which are only fully restored once this function has run to here.
|
||||
flushLatencyRestart();
|
||||
return kResultOk;
|
||||
}
|
||||
|
||||
@@ -157,27 +161,40 @@ void ReaSamplerProcessor::setInstrumentParams(const InstrumentParams& params) {
|
||||
}
|
||||
// Every writer of the parameter set — setState, the editor's commits, the bake's adopt —
|
||||
// funnels through here, so mirroring the limiter flag at this one point is what keeps the
|
||||
// audio thread's copy and the latency report from ever lagging what is persisted, and
|
||||
// requesting the restart here (not just from setLimiterEnabled) is what keeps the host's
|
||||
// PDC from lagging it too. Coalesced: writing the value already held requests nothing.
|
||||
// audio thread's copy and the latency report from ever lagging what is persisted. The MIRROR
|
||||
// is inline, because that is the sound the user clicked for; the host notification is not,
|
||||
// because this funnel is reachable from inside a mouse handler and restartComponent is not
|
||||
// safe there (see this directory's CLAUDE.md).
|
||||
publishLimiterEnabled(params.limiterEnabled);
|
||||
if (limiterFlagChanged && componentHandler) {
|
||||
// The SDK requires this on the UI thread and answers getLatencySamples only after the
|
||||
// host's own deactivate/reactivate — so the flag above is already committed by the time
|
||||
// the host asks. This is a kLatencyChanged restart with the bus untouched, NOT the
|
||||
// retired per-mode kIoChanged bus renegotiation (see initialize()); do not conflate.
|
||||
componentHandler->restartComponent(kLatencyChanged);
|
||||
// Armed AFTER the mirror, so getLatencySamples already answers the new value for the whole
|
||||
// window the arm stays outstanding. Sticky and idempotent: any number of changes before one
|
||||
// flush cost one restart, and the flush is the only thing that clears it.
|
||||
if (limiterFlagChanged) {
|
||||
latencyRestartPending_.store(true, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
void ReaSamplerProcessor::flushLatencyRestart() {
|
||||
// Cleared only once it can actually be delivered — an arm raised before the host connected
|
||||
// its handler waits for a later flush instead of evaporating.
|
||||
if (!componentHandler) return;
|
||||
if (!latencyRestartPending_.exchange(false, std::memory_order_acquire)) return;
|
||||
// The SDK requires this on the UI thread and answers getLatencySamples only after the host's
|
||||
// own deactivate/reactivate — so the flag is long committed by the time the host asks. This
|
||||
// is a kLatencyChanged restart with the bus untouched, NOT the retired per-mode kIoChanged
|
||||
// bus renegotiation (see initialize()); do not conflate.
|
||||
componentHandler->restartComponent(kLatencyChanged);
|
||||
}
|
||||
|
||||
void ReaSamplerProcessor::publishLimiterEnabled(bool on) {
|
||||
limiterEnabled_.store(on, std::memory_order_relaxed);
|
||||
limiter_.setEnabled(on);
|
||||
}
|
||||
|
||||
void ReaSamplerProcessor::setLimiterEnabled(bool on) {
|
||||
// Thin wrapper: setInstrumentParams is the one funnel that mirrors the flag AND requests
|
||||
// the restart, so every writer of the parameter set — this one included — agrees.
|
||||
// Rebased off the PROCESSOR's copy rather than taking a caller-supplied set: an editor
|
||||
// snapshot may carry edits it has not committed, and writing one back here would clobber
|
||||
// them. Everything else is setInstrumentParams', the one funnel every writer agrees through.
|
||||
InstrumentParams params = instrumentParams();
|
||||
params.limiterEnabled = on;
|
||||
setInstrumentParams(params);
|
||||
|
||||
Reference in New Issue
Block a user