Γ-W1-T5: a real Preserve time-stretcher — write rate is duration, tap rate is pitch
Generalizes the correlation-aligned SOLA delay line so the feed and the shift are independent rates over one ring. Unity is bit-identical to the shipped read, asserted against a hash baseline captured pre-change.
This commit is contained in:
@@ -18,7 +18,8 @@ void Voice::presizePreserveShifters(std::int64_t windowFrames) {
|
||||
primeBuf_.assign(windowFrames > 1 ? static_cast<std::size_t>(windowFrames) : 0, 0.0f);
|
||||
}
|
||||
|
||||
void Voice::start(int note, int velocity, const SampleData& sample, bool declickTakeover) {
|
||||
void Voice::start(int note, int velocity, const SampleData& sample, bool declickTakeover,
|
||||
double stretchRate) {
|
||||
// Before any state reset, record the pre-cut reference (last rendered output) and mark
|
||||
// the compensation pending iff this start is a takeover/steal of a sounding voice and the
|
||||
// caller opted in. The ramp is seeded on the first frame rendered after the restart, from
|
||||
@@ -59,6 +60,9 @@ void Voice::start(int note, int velocity, const SampleData& sample, bool declick
|
||||
baseRatio_ = keyTrackedRatio(note, sample.rootNote, sample.keyTrack) * velPitchRatio_;
|
||||
playMode_ = p.playMode;
|
||||
pitchEngine_ = p.pitchEngine;
|
||||
// Clamped once here so the read head's increment and the feed cursor's debt accumulate the
|
||||
// SAME value — they must stay exactly one window apart for the note's whole life.
|
||||
stretchRate_ = instrument::engine::clampStretchRate(stretchRate);
|
||||
|
||||
// Clamp into [0, frames): a start at or past the end degrades to 0 (play from the top)
|
||||
// rather than starting a voice already off the end.
|
||||
@@ -234,7 +238,9 @@ void Voice::start(int note, int velocity, const SampleData& sample, bool declick
|
||||
}
|
||||
// Per-frame feed continues at `p` (the feed bound when the prime exhausted the
|
||||
// playable span).
|
||||
feedPos_ = p;
|
||||
stretch_.start(p);
|
||||
shiftL_.setFeedRate(stretchRate_);
|
||||
shiftR_.setFeedRate(stretchRate_);
|
||||
if (!loopWrap && primeCount < w) {
|
||||
// Sub-window playable span: the source is already exhausted at prime time.
|
||||
shiftL_.freezeTail();
|
||||
|
||||
Reference in New Issue
Block a user