Fix waveform_view comment accuracy, cut duplicate comments, align xToFrame to OverlayArea

This commit is contained in:
2026-07-30 10:35:27 -04:00
parent fb12c53522
commit dfed1c77bb
5 changed files with 33 additions and 25 deletions
+10 -7
View File
@@ -33,8 +33,10 @@ WaveformSurface waveformSurface(const Rect& band, bool stereoMode, int sourceCha
waveformLanes(band, twoLanes ? LaneSplit::Stereo : LaneSplit::Single); waveformLanes(band, twoLanes ? LaneSplit::Stereo : LaneSplit::Single);
s.upper = lanes.upper; s.upper = lanes.upper;
s.lower = lanes.lower; s.lower = lanes.lower;
// Derived from the resolved lanes, not `twoLanes`, so it can never contradict them (a // Derived from the resolved lanes, not `twoLanes` — a stereo split's integer division
// band barely over the two-lane floor can still yield an empty lower lane). // rounds the lower lane to empty for a band this thin (height <= 3), far below the
// allocator's kWaveformMinHeight floor but reachable if this is called directly with an
// arbitrary rect (as tests do).
s.laneCount = lanes.lower.empty() ? 1 : 2; s.laneCount = lanes.lower.empty() ? 1 : 2;
return s; return s;
} }
@@ -53,12 +55,13 @@ int frameToX(const OverlayArea& area, std::int64_t frameCount, std::int64_t fram
return area.rect.x + static_cast<int>(num / frameCount); return area.rect.x + static_cast<int>(num / frameCount);
} }
std::int64_t xToFrame(const Rect& area, std::int64_t frameCount, int x) { std::int64_t xToFrame(const OverlayArea& area, std::int64_t frameCount, int x) {
const int w = std::max(0, area.width); const Rect& r = area.rect;
const int w = std::max(0, r.width);
if (frameCount <= 0 || w <= 0) return 0; if (frameCount <= 0 || w <= 0) return 0;
if (x <= area.x) return 0; if (x <= r.x) return 0;
if (x >= area.right()) return frameCount; if (x >= r.right()) return frameCount;
const std::int64_t dx = static_cast<std::int64_t>(x - area.x); const std::int64_t dx = static_cast<std::int64_t>(x - r.x);
// Inverse of frameToX: frame = round(dx * frameCount / w). // Inverse of frameToX: frame = round(dx * frameCount / w).
const std::int64_t num = dx * frameCount + static_cast<std::int64_t>(w) / 2; const std::int64_t num = dx * frameCount + static_cast<std::int64_t>(w) / 2;
return clampFrame(num / static_cast<std::int64_t>(w), frameCount); return clampFrame(num / static_cast<std::int64_t>(w), frameCount);
+6 -5
View File
@@ -28,10 +28,11 @@ using audio::AudioSample;
struct WaveformSurface { struct WaveformSurface {
Rect upper; // lane 0 -> channel 0 (LEFT); the whole band when single-lane Rect upper; // lane 0 -> channel 0 (LEFT); the whole band when single-lane
Rect lower; // lane 1 -> channel 1 (RIGHT); empty() when single-lane Rect lower; // lane 1 -> channel 1 (RIGHT); empty() when single-lane
OverlayArea overlay; // the full band, both modes — a distinct type (not Rect) so an OverlayArea overlay; // the full band, both modes
// overlay-only API can't accept a lane rect by mistake int laneCount = 0; // 0 on a degenerate band, else 1 or 2 — matches `lower`'s emptiness
int laneCount = 0; // 0 on a degenerate band, else 1 or 2 — derived from the resolved // (2 iff lower non-empty). For a non-empty band <= 2px tall, `upper`
// lanes (never contradicts upper/lower) // can be empty too while this still reports 1 — unreachable through
// the band-stack allocator's kWaveformMinHeight floor.
}; };
// Resolves the surface for a waveform band. Two lanes need BOTH stereo mode and a source // Resolves the surface for a waveform band. Two lanes need BOTH stereo mode and a source
@@ -62,7 +63,7 @@ int frameToX(const OverlayArea& area, std::int64_t frameCount, std::int64_t fram
// Inverse of frameToX: the frame a point x maps to, clamped to [0, frameCount]. A point left of // Inverse of frameToX: the frame a point x maps to, clamped to [0, frameCount]. A point left of
// area.x yields 0; right of area.right() yields frameCount. // area.x yields 0; right of area.right() yields frameCount.
std::int64_t xToFrame(const Rect& area, std::int64_t frameCount, int x); std::int64_t xToFrame(const OverlayArea& area, std::int64_t frameCount, int x);
// Which marker (index into the caller's parallel `frames` array, in draw order) a grab at // Which marker (index into the caller's parallel `frames` array, in draw order) a grab at
// (x, y) lands on, or -1 for a miss. A marker is grabbed when x is within kMarkerGrabWidth of // (x, y) lands on, or -1 for a miss. A marker is grabbed when x is within kMarkerGrabWidth of
-2
View File
@@ -45,8 +45,6 @@ static bool findNode(const std::vector<EnvVertex>& poly, EnvNode node, EnvVertex
static Rect wideArea() { return Rect::ltrb(20, 10, 1020, 110); } static Rect wideArea() { return Rect::ltrb(20, 10, 1020, 110); }
static constexpr double kTotal = 2.0; static constexpr double kTotal = 2.0;
// nodeAtPoint/resolveNodeDrag take the overlay type, not a bare Rect (the distinct-type
// enforcement in editor_geometry.h) — this wraps a plain test Rect for them.
static OverlayArea overlayOf(const Rect& r) { return OverlayArea{r}; } static OverlayArea overlayOf(const Rect& r) { return OverlayArea{r}; }
static const double kGateSecPerPx = 1.0 / gatePxPerSecond(wideArea()); static const double kGateSecPerPx = 1.0 / gatePxPerSecond(wideArea());
-2
View File
@@ -26,8 +26,6 @@ static int g_fail = 0;
#define CHECK(cond) do { if(!(cond)) { \ #define CHECK(cond) do { if(!(cond)) { \
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0) std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
// buildEnvelopePolyline takes the overlay type, not a bare Rect (the distinct-type
// enforcement in editor_geometry.h) — this wraps a plain test Rect for it.
static OverlayArea overlayOf(const Rect& r) { return OverlayArea{r}; } static OverlayArea overlayOf(const Rect& r) { return OverlayArea{r}; }
// A comfortable overlay area: 1000px wide, 100px tall, offset so left/top != 0 (catches origin // A comfortable overlay area: 1000px wide, 100px tall, offset so left/top != 0 (catches origin
+17 -9
View File
@@ -26,8 +26,6 @@ static int g_fail = 0;
#define CHECK(cond) do { if(!(cond)) { \ #define CHECK(cond) do { if(!(cond)) { \
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0) std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
// frameToX/markerAtPoint/resolveDragFrame take the overlay type, not a bare Rect (the
// distinct-type enforcement in editor_geometry.h) — this wraps a plain test Rect for them.
static OverlayArea overlayOf(const Rect& r) { return OverlayArea{r}; } static OverlayArea overlayOf(const Rect& r) { return OverlayArea{r}; }
// A comfortable waveform area: 1000px wide, offset so left != 0 (catches origin bugs). // A comfortable waveform area: 1000px wide, offset so left != 0 (catches origin bugs).
@@ -57,16 +55,16 @@ static void testFrameToXDegenerate() {
static void testXToFrameInverse() { static void testXToFrameInverse() {
const Rect a = wideArea(); const Rect a = wideArea();
CHECK(xToFrame(a, 1000, a.x) == 0); CHECK(xToFrame(overlayOf(a), 1000, a.x) == 0);
CHECK(xToFrame(a, 1000, a.right()) == 1000); CHECK(xToFrame(overlayOf(a), 1000, a.right()) == 1000);
CHECK(xToFrame(a, 1000, a.x + 250) == 250); // 1:1 map here CHECK(xToFrame(overlayOf(a), 1000, a.x + 250) == 250); // 1:1 map here
} }
static void testXToFrameClampsOutside() { static void testXToFrameClampsOutside() {
const Rect a = wideArea(); const Rect a = wideArea();
CHECK(xToFrame(a, 1000, a.x - 100) == 0); // left of area -> 0 CHECK(xToFrame(overlayOf(a), 1000, a.x - 100) == 0); // left of area -> 0
CHECK(xToFrame(a, 1000, a.right() + 100) == 1000); // right of area -> frameCount CHECK(xToFrame(overlayOf(a), 1000, a.right() + 100) == 1000); // right of area -> frameCount
CHECK(xToFrame(a, 0, a.x + 10) == 0); // no frames -> 0 CHECK(xToFrame(overlayOf(a), 0, a.x + 10) == 0); // no frames -> 0
} }
static void testFrameToXRoundTrip() { static void testFrameToXRoundTrip() {
@@ -75,7 +73,7 @@ static void testFrameToXRoundTrip() {
const Rect a = Rect::ltrb(0, 0, 800, 60); const Rect a = Rect::ltrb(0, 0, 800, 60);
for (std::int64_t f = 0; f <= 2000; f += 137) { for (std::int64_t f = 0; f <= 2000; f += 137) {
const int x = frameToX(overlayOf(a), 2000, f); const int x = frameToX(overlayOf(a), 2000, f);
const std::int64_t back = xToFrame(a, 2000, x); const std::int64_t back = xToFrame(overlayOf(a), 2000, x);
CHECK(back >= f - 3 && back <= f + 3); CHECK(back >= f - 3 && back <= f + 3);
} }
} }
@@ -269,6 +267,15 @@ static void testSurfaceDegenerateBandDrawsNothing() {
CHECK(waveformOverlayArea(Rect{10, 10, 0, 0}).rect.empty()); CHECK(waveformOverlayArea(Rect{10, 10, 0, 0}).rect.empty());
} }
static void testSurfaceThinBandRoundsLowerLaneEmpty() {
// Height 3 is the edge where the stereo split's integer division rounds the lower lane to
// empty even though the band itself isn't degenerate — pins the laneCount derivation.
const WaveformSurface s = waveformSurface(Rect{0, 0, 100, 3}, true, 2);
CHECK(s.laneCount == 1);
CHECK(!s.upper.empty());
CHECK(s.lower.empty());
}
// --- Hit-testing across the stacked lanes ------------------------------------- // --- Hit-testing across the stacked lanes -------------------------------------
static void testMarkerGrabReachesTheLowerStereoLane() { static void testMarkerGrabReachesTheLowerStereoLane() {
@@ -367,6 +374,7 @@ int main() {
testSurfaceMonoSourceInStereoModeStaysOneLane(); testSurfaceMonoSourceInStereoModeStaysOneLane();
testSurfaceOverlayIsFullStackedHeightInBothModes(); testSurfaceOverlayIsFullStackedHeightInBothModes();
testSurfaceDegenerateBandDrawsNothing(); testSurfaceDegenerateBandDrawsNothing();
testSurfaceThinBandRoundsLowerLaneEmpty();
testMarkerGrabReachesTheLowerStereoLane(); testMarkerGrabReachesTheLowerStereoLane();
testMarkerGrabInMonoSpansTheBand(); testMarkerGrabInMonoSpansTheBand();