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:
@@ -146,16 +146,18 @@ std::string ReaSamplerProcessor::reloadInstrument() {
|
||||
buildFromRef(*sel, params, projectDir, mode)) {
|
||||
sample = std::move(*decoded);
|
||||
havePlayable = true;
|
||||
// Point the built snapshot at the instance's ONE live block and seed it from
|
||||
// the very PlayParams the voices latch, so an untouched knob folds to the same
|
||||
// frames the build resolved and a note-on with a live block sounds identical
|
||||
// to one without.
|
||||
sample.live = &liveParams_;
|
||||
// Point the built snapshot at the instance's ONE engine-facing block and seed the
|
||||
// MODEL block from the very PlayParams the voices latch, so an untouched knob folds
|
||||
// to the same frames the build resolved and a note-on with a live block sounds
|
||||
// identical to one without. process() merges the seed into automationLive_ at the
|
||||
// top of the first block after this, which is where the swap below becomes visible
|
||||
// too — so the new snapshot's first note reads it.
|
||||
sample.live = &automationLive_;
|
||||
{
|
||||
// reloadMutex_ (held for this whole function) nests livePublishMutex_ here;
|
||||
// publishLiveParams never holds reloadMutex_, so this is the only nesting.
|
||||
std::lock_guard<std::mutex> lp(livePublishMutex_);
|
||||
liveParams_.publish(instrument::engine::foldLive(sample.play));
|
||||
liveParams_.publish(instrument::engine::foldLive(sample.play, sample.keyTrack));
|
||||
}
|
||||
builtSampleRate_.store(sample.sampleRate, std::memory_order_relaxed);
|
||||
resolvedId = selId; // the concrete pick that resolved
|
||||
@@ -226,6 +228,8 @@ void ReaSamplerProcessor::adoptBakedCapture(const SampleRefEntry& entry,
|
||||
// bake chain only ever runs from that tick, so the arm would be drained on the next one
|
||||
// anyway. At the tail for the same reason setState's is (see there).
|
||||
flushLatencyRestart();
|
||||
// The reset's gain notification, armed under reloadMutex_ inside that reload.
|
||||
flushGainNotify();
|
||||
}
|
||||
|
||||
void ReaSamplerProcessor::publishUsage(const SampleRefs& refs,
|
||||
@@ -289,8 +293,16 @@ void ReaSamplerProcessor::publishBuiltLocked(std::unique_ptr<LoadedInstrument> b
|
||||
// gain sitting above every snapshot, the same shape as ONE BLOCK, ONE RATE (see
|
||||
// builtSampleRate_).
|
||||
if (gainAtNextPublish_) {
|
||||
setMasterGainLinear(*gainAtNextPublish_);
|
||||
// The MIRROR is inline (that is the sound this publish belongs to); the host
|
||||
// notification is ARMED and delivered after reloadMutex_ is released. performEdit
|
||||
// reaches the host handler, a host may re-enter this object synchronously from it, and
|
||||
// setActive(false) takes this same non-recursive mutex — the identical hazard the
|
||||
// latency restart is deferred for.
|
||||
const bool moved = publishMasterGainLinear(*gainAtNextPublish_);
|
||||
gainAtNextPublish_.reset();
|
||||
// Armed only on a real change, so a reset that lands on the gain already set writes
|
||||
// nothing into a host's automation lane — the same compare setMasterGainLinear makes.
|
||||
if (moved) gainNotifyPending_.store(true, std::memory_order_release);
|
||||
}
|
||||
LoadedInstrument* evicted = draining_.exchange(prev);
|
||||
if (evicted) graveyard_.push_back(std::unique_ptr<LoadedInstrument>(evicted));
|
||||
|
||||
Reference in New Issue
Block a user