loop: crossfade the Gate sustain seam, and unshadow the loop handles that made loop points look gone

This commit is contained in:
2026-07-31 17:36:36 -04:00
parent a90ccd9a00
commit 0fe4166d7d
25 changed files with 991 additions and 64 deletions
@@ -98,6 +98,17 @@ void ReaSamplerEditor::paintWaveform(LICE_IBitmap* bmp, const Rect& band) {
static_cast<float>(kLoopSpanFillAlpha), 0);
}
}
// The crossfade region, at half the loop span's weight so the two read as nested rather
// than as a second loop. Drawn before the marker bars so the bars stay on top.
if (m.hasLoop && m.crossfade > 0) {
const int fx = frameToX(overlay, frames, m.loopStart - m.crossfade);
const int lx = frameToX(overlay, frames, m.loopStart);
if (lx > fx) {
LICE_FillRect(bmp, fx, overlayRect.y, lx - fx, overlayRect.height,
toLice(roleColor(kRoleLoopMarker)),
static_cast<float>(kLoopSpanFillAlpha) * 0.5f, 0);
}
}
const std::int64_t markerFrames[3] = {m.start, m.loopStart, m.loopEnd};
const Role markerRoles[3] = {kRoleStartMarker, kRoleLoopMarker, kRoleLoopMarker};
for (int i = 0; i < 3; ++i) {
@@ -107,6 +118,15 @@ void ReaSamplerEditor::paintWaveform(LICE_IBitmap* bmp, const Rect& band) {
LICE_FillRect(bmp, mx - 1, overlayRect.y, 2, overlayRect.height,
toLice(roleColor(markerRoles[i])), alpha, 0);
}
// The crossfade's grab tab. Only offered with a loop set, matching the hit-test, and it
// is the whole affordance for a zero-length fade — nothing else marks where it sits.
if (m.hasLoop) {
const Rect tab = markerHandleRect(overlay, frames, m.loopStart - m.crossfade);
if (!tab.empty()) {
LICE_FillRect(bmp, tab.x, tab.y, tab.width, tab.height,
toLice(roleColor(kRoleLoopMarker)), 1.0f, 0);
}
}
paintEnvelopeOverlay(bmp, overlay, frames);
}