loop: fix the crossfade seam's residual discontinuity, plus six review minors
Normalizes crossfadeWeight over crossfade-1 so the last rendered frame lands at exactly the incoming tap instead of a residual step; corrects the CLAUDE.md invariant and seam test to match. Shares lerpSource/crossfadedSource/maxCrossfade, fixes stale docs/constants, and clears crossfade on the loop-OFF gesture.
This commit is contained in:
@@ -361,6 +361,28 @@ static void testMarkerHandleOnDegenerateAreas() {
|
||||
CHECK(markerHandleRect(overlayOf(thin), 1000, 500).height == 4);
|
||||
}
|
||||
|
||||
// The shell (editor_input_waveform.cpp) checks the loop crossfade's own grab handle — at
|
||||
// loopStart - crossfade — before it iterates the ordinary marker array, because a zero-length
|
||||
// fade puts that handle exactly on the loop-start marker's frame. The same coincidence recurs
|
||||
// whenever ANY marker shares that frame, most plausibly the START marker dragged up against the
|
||||
// fade edge: this module can't exercise the shell's check-order itself, but it can prove the
|
||||
// geometric ambiguity that makes the ordering load-bearing — the array's own first-match rule
|
||||
// would otherwise resolve the top strip to the START marker, not the fade handle.
|
||||
static void testStartMarkerSharesTheHandleStripWhenItSitsAtTheFadeEdge() {
|
||||
const Rect a = wideArea();
|
||||
const std::int64_t loopStart = 400, crossfade = 30;
|
||||
const std::int64_t fadeEdge = loopStart - crossfade; // where the crossfade handle sits
|
||||
const std::int64_t markers[3] = {fadeEdge, loopStart, loopStart + 100}; // start dialled here
|
||||
const int mx = frameToX(overlayOf(a), 1000, fadeEdge);
|
||||
const int topY = a.y; // inside the handle's top strip
|
||||
// Without the shell's priority check, the array's own first-match rule already resolves the
|
||||
// column to the start marker (index 0) at this x/y...
|
||||
CHECK(markerAtPoint(overlayOf(a), 1000, markers, 3, mx, topY) == 0);
|
||||
// ...and the fade handle's rect claims the exact same pixel — the ambiguity the shell
|
||||
// resolves by asking the handle first, same as it does for the zero-fade/loop-start case.
|
||||
CHECK(contains(markerHandleRect(overlayOf(a), 1000, fadeEdge), mx, topY));
|
||||
}
|
||||
|
||||
// --- Per-lane envelope content -------------------------------------------------
|
||||
|
||||
static void testAsymmetricStereoLanesCarryDifferentContent() {
|
||||
@@ -435,6 +457,7 @@ int main() {
|
||||
testCoincidentMarkersStayIndependentlyGrabbable();
|
||||
testMarkerHandleClipsIntoTheArea();
|
||||
testMarkerHandleOnDegenerateAreas();
|
||||
testStartMarkerSharesTheHandleStripWhenItSitsAtTheFadeEdge();
|
||||
|
||||
testAsymmetricStereoLanesCarryDifferentContent();
|
||||
testLaneEnvelopeRejectsOutOfRangeLane();
|
||||
|
||||
Reference in New Issue
Block a user