Service both VST3 parameter channels, and promote pitch key-track and Trigger length so all 44 ids issue
The SDK's own single-component sample drains inputParameterChanges in process() and implements setParamNormalized; automation was reading the GUI channel alone. The audio thread now patches a block it solely owns.
This commit is contained in:
@@ -91,20 +91,26 @@ tresult PLUGIN_API ReaSamplerProcessor::setState(IBStream* state) {
|
||||
legacyLiftConcluded_.store(false, std::memory_order_relaxed);
|
||||
reloadInstrument();
|
||||
paramNotifySuppressed_ = false;
|
||||
// Every exposed parameter now reads the blob's value. Ordering against the host's first
|
||||
// parameter block is irrelevant BY CONSTRUCTION rather than by assumption: there is one
|
||||
// model and one funnel per control, so whichever of the two writes last simply wins, and
|
||||
// the host's display follows the model either way.
|
||||
// Every exposed parameter now reads the blob's value. Its ordering against the host's first
|
||||
// parameter block does not need to be known: an automation point held by the audio thread is
|
||||
// re-applied over every merge, so a lane outranks this restore whichever way round the two
|
||||
// arrive. That is VST3's own rule — a written lane outranks anything the plug-in sets — not
|
||||
// a race we lost.
|
||||
syncParamsFromModel();
|
||||
// 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) resumes or reloads
|
||||
// against the refs above, which are only fully restored once this function has run to here.
|
||||
flushLatencyRestart();
|
||||
flushGainNotify();
|
||||
return kResultOk;
|
||||
}
|
||||
|
||||
tresult PLUGIN_API ReaSamplerProcessor::getState(IBStream* state) {
|
||||
if (!state) return kResultFalse;
|
||||
// Before the snapshot, not after: the blob is authoritative, so anything the host's
|
||||
// automation wrote must be in the model by the time it is serialised. This is the one drain
|
||||
// site that is not an optimisation — a save with no editor open still has to see it.
|
||||
drainAutomationToModel();
|
||||
// Persists the full instance state — never written to the "reasampler" bank ext-state.
|
||||
// No pick serializes to {"", default params}, restoring as silence (never auto-playing
|
||||
// sample #1).
|
||||
@@ -163,10 +169,15 @@ InstrumentParams ReaSamplerProcessor::instrumentParams() {
|
||||
}
|
||||
|
||||
void ReaSamplerProcessor::setInstrumentParams(const InstrumentParams& params) {
|
||||
InstrumentParams before;
|
||||
// The exposed values alone, not the whole set: this funnel fires per mouse move on every live
|
||||
// knob and node drag, and InstrumentParams owns seven vectors — copying all of them to diff
|
||||
// 44 doubles is the cost, and the diff is what the notification actually needs.
|
||||
double before[kDeckParamSlots];
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(paramsMutex_);
|
||||
before = params_;
|
||||
for (const instrument::param::ParamRow& row : instrument::param::exposedParams()) {
|
||||
before[static_cast<std::size_t>(row.deck)] = modelParamNormalized(params_, row.deck);
|
||||
}
|
||||
params_ = params;
|
||||
}
|
||||
// Every writer of the parameter set — setState, the editor's commits, the bake's adopt —
|
||||
@@ -250,8 +261,9 @@ void ReaSamplerProcessor::clearMasterBusClip() {
|
||||
void ReaSamplerProcessor::publishLiveParams() {
|
||||
const int rate = builtSampleRate_.load(std::memory_order_relaxed);
|
||||
if (rate <= 0) return;
|
||||
const InstrumentParams params = instrumentParams();
|
||||
const instrument::engine::LiveValues block =
|
||||
instrument::engine::foldLive(resolvePlay(instrumentParams().play, rate));
|
||||
instrument::engine::foldLive(resolvePlay(params.play, rate), params.keyTrack);
|
||||
// livePublishMutex_ enforces the seqlock's single-writer contract (live_params.h) against
|
||||
// reloadInstrument's publish — held for the publish call only, not the fold above.
|
||||
std::lock_guard<std::mutex> lock(livePublishMutex_);
|
||||
@@ -329,20 +341,32 @@ void ReaSamplerProcessor::setMonoTrigger(MonoTrigger trigger) {
|
||||
rebuildVoiceEngine();
|
||||
}
|
||||
|
||||
void ReaSamplerProcessor::setMasterGainLinear(double linear) {
|
||||
bool ReaSamplerProcessor::publishMasterGainLinear(double linear) {
|
||||
// Clamp to the master_gain taper (0 = silence, cap = +24 dB). One relaxed atomic
|
||||
// store — no rebuild, no lock (a post-sum trim is not a keymap fact).
|
||||
if (!(linear >= 0.0)) linear = 0.0; // also catches NaN
|
||||
const double maxLin = masterGainMaxLinear();
|
||||
if (linear > maxLin) linear = maxLin;
|
||||
const float value = static_cast<float>(linear);
|
||||
const float previous = masterGain_.exchange(value, std::memory_order_relaxed);
|
||||
return masterGain_.exchange(value, std::memory_order_relaxed) != value;
|
||||
}
|
||||
|
||||
void ReaSamplerProcessor::setMasterGainLinear(double linear) {
|
||||
const bool moved = publishMasterGainLinear(linear);
|
||||
// Gain's own notification funnel — it is the one exposed control that does not ride the
|
||||
// parameter set, so setInstrumentParams' diff cannot see it. Compared for a real change so a
|
||||
// reload's republish of an unmoved gain writes nothing into a host's automation lane.
|
||||
if (paramNotifySuppressed_ || previous == value) return;
|
||||
if (paramNotifySuppressed_ || !moved) return;
|
||||
notifyParamChanged(instrument::param::kParamMasterGain,
|
||||
instrument::engine::masterGainNormFromLinear(linear));
|
||||
instrument::engine::masterGainNormFromLinear(masterGainLinear()));
|
||||
}
|
||||
|
||||
void ReaSamplerProcessor::flushGainNotify() {
|
||||
if (!componentHandler) return; // an arm raised before the handler connected waits
|
||||
if (!gainNotifyPending_.exchange(false, std::memory_order_acquire)) return;
|
||||
if (paramNotifySuppressed_) return;
|
||||
notifyParamChanged(instrument::param::kParamMasterGain,
|
||||
instrument::engine::masterGainNormFromLinear(masterGainLinear()));
|
||||
}
|
||||
|
||||
void ReaSamplerProcessor::previewNoteOn(int note) {
|
||||
|
||||
Reference in New Issue
Block a user