instrument: latch the note done at the read-head run-off, fit the migrated fades, and lift the curve dial and overlay selection into pure modules
This commit is contained in:
@@ -90,8 +90,16 @@ void Voice::start(int note, int velocity, const SampleData& sample, bool declick
|
||||
}
|
||||
|
||||
// The pitch AHD's Hold fraction is taken against the whole playable span, so its three
|
||||
// stages lay 1:1 over the waveform from the start point.
|
||||
pitchEnv_.configure(postStart, p.pitchEnv);
|
||||
// stages lay 1:1 over the waveform from the start point. postStart is a SOURCE-frame count
|
||||
// and this envelope counts OUTPUT frames (envelopes.h), so Varispeed — which consumes
|
||||
// baseRatio_ source frames per output frame — needs the span converted, or a transposed
|
||||
// note's envelope outruns (or outlives) the note it shapes. Preserve reads at the source
|
||||
// rate, so its two domains already coincide.
|
||||
const double pitchSpan =
|
||||
(pitchEngine_ == PitchEngine::Preserve || !(baseRatio_ > 0.0))
|
||||
? static_cast<double>(postStart)
|
||||
: static_cast<double>(postStart) / baseRatio_;
|
||||
pitchEnv_.configure(static_cast<std::int64_t>(pitchSpan + 0.5), p.pitchEnv);
|
||||
pitchEnv_.noteOn();
|
||||
|
||||
// A restart lands every live glide back on the new note's own values, at a step derived
|
||||
|
||||
@@ -348,6 +348,12 @@ private:
|
||||
// play span ends within the ramp). With no declick (the common case) this is
|
||||
// byte-identical to the plain idle-out.
|
||||
if (triggerRanOff || readPos_ >= static_cast<double>(frameCount)) {
|
||||
// The NOTE is over the moment the read head leaves its span, whether or not a ramp
|
||||
// still rings: no later frame can carry envelope output. Latching here is what keeps
|
||||
// a ringing-out voice out of soundingNote() — the Preserve cap would otherwise
|
||||
// refuse a new onset, and mono legato would retune a voice already past its end
|
||||
// (silencing the new note) for the whole ~4 ms ramp.
|
||||
amplitudeDone_ = true;
|
||||
if (declickPending_) seedDeclick();
|
||||
if (!declickActive_) seedTerminalDeclick();
|
||||
if (declickActive_) {
|
||||
@@ -376,8 +382,11 @@ private:
|
||||
// Peer of the read-head exhaustion path above: a Trigger AHD whose stages end BEFORE
|
||||
// the play span (a zero decay, which the shape deliberately keeps expressible) cuts the
|
||||
// same synthetic Preserve tail at whatever level it was at. Seeded from lastOut, which
|
||||
// still holds the PREVIOUS frame — this one is already silent. Gate is left out on
|
||||
// purpose: its amplitude reaches zero through a release, so there is no cut to ring out.
|
||||
// still holds the PREVIOUS frame — this one is already silent. Gate is left out of THIS
|
||||
// site only: its amplitude reaches zero through a release, so nothing here is cut
|
||||
// mid-level. The exhaustion path above deliberately does NOT exclude Gate — a held Gate
|
||||
// note whose source runs out with no loop is cut at its sustain level, and under
|
||||
// Preserve that cut lands on the same recycled synthetic tail.
|
||||
if (amplitudeDone_ && amp == 0.0 && !declickActive_ &&
|
||||
playMode_ == PlayMode::Trigger) {
|
||||
seedTerminalDeclick();
|
||||
|
||||
Reference in New Issue
Block a user