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:
@@ -45,6 +45,9 @@ inline double pitchRatio(int note, int rootNote) {
|
||||
// ((note-root)*1.0 is exact in IEEE-754 for an integer-valued double, feeding the same
|
||||
// std::pow call); 0.0 means every key plays the root pitch; 2.0 doubles the tracking rate.
|
||||
// At the root note the offset is 0 regardless of keyTrack.
|
||||
// "Not supplied" for Voice::start's two snapshot-defaulted note-on latches; see start().
|
||||
inline constexpr double kLatchFromSnapshot = -1.0;
|
||||
|
||||
inline double keyTrackedRatio(int note, int rootNote, double keyTrack) {
|
||||
const double semis = static_cast<double>(note - rootNote) * keyTrack;
|
||||
return std::pow(2.0, semis / 12.0);
|
||||
@@ -124,8 +127,16 @@ public:
|
||||
// to consult gets exactly that; VoiceEngine::startVoice is what resolves the real value —
|
||||
// sample.play.playRate is NOT read here, because the published block outranks the snapshot's
|
||||
// possibly-stale copy of it.
|
||||
//
|
||||
// `keyTrack` and `lengthFraction` are the other two members of stretchRate's note-on-latched
|
||||
// class and arrive the same way, for the same structural reason. Negative = not supplied,
|
||||
// which reads the snapshot's own value (sample.keyTrack, effectiveLengthFraction(play)) —
|
||||
// both are non-negative by domain, so the sentinel can never collide with a real one.
|
||||
// VoiceEngine::startVoice always supplies them, resolved from the published block when there
|
||||
// is one; the sentinel is for a caller that has no block to consult.
|
||||
void start(int note, int velocity, const SampleData& sample, bool declickTakeover = false,
|
||||
double stretchRate = 1.0);
|
||||
double stretchRate = 1.0, double keyTrack = kLatchFromSnapshot,
|
||||
double lengthFraction = kLatchFromSnapshot);
|
||||
|
||||
// Mono legato takeover: re-pitch this active voice to `note` without touching the
|
||||
// amplitude envelope, read position, or shifter state — pitch moves, no re-attack. Both
|
||||
@@ -201,7 +212,7 @@ private:
|
||||
// shifter's transpose. Cold: note-on, legato retune, and a live block, never per frame.
|
||||
void recomputeBaseRatio() {
|
||||
if (sample_ == nullptr) return;
|
||||
baseRatio_ = keyTrackedRatio(note_, sample_->rootNote, sample_->keyTrack) *
|
||||
baseRatio_ = keyTrackedRatio(note_, sample_->rootNote, keyTrack_) *
|
||||
velPitchRatio_ * pitchOffsetRatio_ * rateRatio_;
|
||||
}
|
||||
|
||||
@@ -710,6 +721,10 @@ private:
|
||||
double velPitchRatio_ = 1.0; // the velocity->pitch factor alone; retune re-applies it
|
||||
double pitchOffsetRatio_ = 1.0; // the Pitch knob's factor — LIVE, re-applied by applyLive
|
||||
double rateRatio_ = 1.0; // Rate's factor of the read increment; start() owns when it is 1
|
||||
// Key-track, LATCHED at note-on beside the rate. Held here rather than re-read off the
|
||||
// snapshot so a legato retune and a live block re-apply the note's own value; a published
|
||||
// move reaches the next note only.
|
||||
double keyTrack_ = kKeyTrackDefault;
|
||||
// Whether this note is ACTUALLY taking the Preserve read — a Preserve voice whose shifters
|
||||
// were never sized falls back to the varispeed one, and the two domains differ. Latched at
|
||||
// note-on beside rateRatio_, which start() resolves from the same predicate.
|
||||
|
||||
Reference in New Issue
Block a user