Loop: an explicit enable, four named marks with grabbable caps, and the crossfade painted where it is actually heard

hasLoop becomes user-owned with the gestures as shortcuts onto it; no format change. START uses overlay/trace, not accent/primary, which is the waveform's own fill.
This commit is contained in:
2026-08-02 05:18:53 -04:00
parent ef59265e7a
commit a7c3c7a828
26 changed files with 1190 additions and 191 deletions
+55
View File
@@ -0,0 +1,55 @@
// loop_marks.cpp — see loop_marks.h. Pure value folds; no host types.
#include "core/instrument/ui/loop_marks.h"
#include "core/instrument/engine/loop/loop_span.h" // defaultLoopBounds
namespace reasampler::instrument::ui {
namespace {
// The engine's own acceptance test, restated over the editor's two integers: resolveLoop
// refuses an inverted, empty or out-of-range span rather than repairing it, so a span it would
// refuse is one the handles must be re-parked out of.
bool spanUsable(std::int64_t loopStart, std::int64_t loopEnd, std::int64_t frameCount) {
return loopStart >= 0 && loopEnd > loopStart && loopEnd <= frameCount;
}
} // namespace
LoopMarks resolveLoopMarks(const StoredLoop& stored, std::int64_t frameCount) {
LoopMarks m;
if (stored.override_) {
m.hasLoop = stored.override_->hasLoop;
m.loopStart = stored.override_->start;
m.loopEnd = stored.override_->end;
} else if (stored.intrinsic && stored.intrinsic->hasLoop) {
m.hasLoop = true;
m.loopStart = stored.intrinsic->start;
m.loopEnd = stored.intrinsic->end;
}
if (stored.startPoint) m.start = *stored.startPoint;
m.crossfade = stored.crossfade > 0 ? stored.crossfade : 0;
if (!spanUsable(m.loopStart, m.loopEnd, frameCount)) {
m.hasLoop = false;
m.parked = true;
const engine::loop::LoopBounds d = engine::loop::defaultLoopBounds(frameCount);
m.loopStart = d.start;
m.loopEnd = d.end;
}
return m;
}
LoopWrite applyLoopMarks(const LoopMarks& m) {
LoopWrite w;
const bool spanAlive = m.loopEnd > m.loopStart;
w.loop.hasLoop = m.hasLoop && spanAlive;
w.loop.start = m.loopStart;
w.loop.end = m.loopEnd;
w.crossfade = (spanAlive && m.crossfade > 0) ? m.crossfade : 0;
w.start = m.start;
return w;
}
} // namespace reasampler::instrument::ui