Q-W1 pt2: core/shell/app relocation + sub-namespaces; one concrete ui::Rect (LTRB fork retired); slot_map split from bank_book; BankIndex→BankModel; 59/59 green

This commit is contained in:
2026-07-28 20:48:56 -04:00
parent 67a41728f3
commit 847936f813
222 changed files with 2247 additions and 2079 deletions
+37 -36
View File
@@ -1,4 +1,4 @@
// Standalone tests for reasampler::vst::waveform_view — no VST3, no REAPER, no framework.
// Standalone tests for reasampler::instrument::ui::waveform_view — no VST3, no REAPER, no framework.
// Same fast assert loop as the sibling pure tests. Assert the S11 waveform surface's
// frame<->pixel mapping, marker grab regions, drag-delta frame resolver (with clamps), and
// the zero-crossing snap — the geometry + snap that back the draggable start/loop markers.
@@ -9,61 +9,62 @@
// no-ops); nearestZeroCrossing (nearest sign-change, sample-on-zero, equidistant-tie-to-lower,
// no-crossing keeps target, target clamp, degenerate buffers).
#include "../src/vst/waveform_view.h"
#include "../src/core/instrument/ui/waveform_view.h"
#include <cstdio>
#include <vector>
using namespace reasampler::vst;
using reasampler::AudioSample;
using namespace reasampler;
using namespace reasampler::instrument::ui;
using reasampler::audio::AudioSample;
static int g_fail = 0;
#define CHECK(cond) do { if(!(cond)) { \
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
// A comfortable waveform area: 1000px wide, offset so left != 0 (catches origin bugs).
static Rect wideArea() { return Rect{20, 10, 1020, 90}; } // width 1000
static Rect wideArea() { return Rect::ltrb(20, 10, 1020, 90); } // width 1000
// --- frameToX / xToFrame ------------------------------------------------------
static void testFrameToXEndpoints() {
const Rect a = wideArea();
CHECK(frameToX(a, 1000, 0) == a.left); // frame 0 -> left edge
CHECK(frameToX(a, 1000, 1000) == a.right); // frameCount -> right edge
CHECK(frameToX(a, 1000, 500) == a.left + 500); // midpoint (1:1 here)
CHECK(frameToX(a, 1000, 0) == a.x); // frame 0 -> left edge
CHECK(frameToX(a, 1000, 1000) == a.right()); // frameCount -> right edge
CHECK(frameToX(a, 1000, 500) == a.x + 500); // midpoint (1:1 here)
}
static void testFrameToXClampsOutOfRange() {
const Rect a = wideArea();
CHECK(frameToX(a, 1000, -50) == a.left); // below 0 pins left
CHECK(frameToX(a, 1000, 5000) == a.right); // above count pins right
CHECK(frameToX(a, 1000, -50) == a.x); // below 0 pins left
CHECK(frameToX(a, 1000, 5000) == a.right()); // above count pins right
}
static void testFrameToXDegenerate() {
const Rect a = wideArea();
CHECK(frameToX(a, 0, 100) == a.left); // no frames -> left
const Rect z = Rect{5, 5, 5, 45}; // zero width
CHECK(frameToX(z, 1000, 500) == z.left);
CHECK(frameToX(a, 0, 100) == a.x); // no frames -> left
const Rect z = Rect::ltrb(5, 5, 5, 45); // zero width
CHECK(frameToX(z, 1000, 500) == z.x);
}
static void testXToFrameInverse() {
const Rect a = wideArea();
CHECK(xToFrame(a, 1000, a.left) == 0);
CHECK(xToFrame(a, 1000, a.right) == 1000);
CHECK(xToFrame(a, 1000, a.left + 250) == 250); // 1:1 map here
CHECK(xToFrame(a, 1000, a.x) == 0);
CHECK(xToFrame(a, 1000, a.right()) == 1000);
CHECK(xToFrame(a, 1000, a.x + 250) == 250); // 1:1 map here
}
static void testXToFrameClampsOutside() {
const Rect a = wideArea();
CHECK(xToFrame(a, 1000, a.left - 100) == 0); // left of area -> 0
CHECK(xToFrame(a, 1000, a.right + 100) == 1000); // right of area -> frameCount
CHECK(xToFrame(a, 0, a.left + 10) == 0); // no frames -> 0
CHECK(xToFrame(a, 1000, a.x - 100) == 0); // left of area -> 0
CHECK(xToFrame(a, 1000, a.right() + 100) == 1000); // right of area -> frameCount
CHECK(xToFrame(a, 0, a.x + 10) == 0); // no frames -> 0
}
static void testFrameToXRoundTrip() {
// Round-trip at a non-1:1 scale: 800px area over 2000 frames (2.5 frames/px). frameToX then
// xToFrame should land within a couple frames (rounding both directions).
const Rect a = Rect{0, 0, 800, 60};
const Rect a = Rect::ltrb(0, 0, 800, 60);
for (std::int64_t f = 0; f <= 2000; f += 137) {
const int x = frameToX(a, 2000, f);
const std::int64_t back = xToFrame(a, 2000, x);
@@ -77,39 +78,39 @@ static void testMarkerAtPointGrabsWithinBand() {
const Rect a = wideArea();
// Markers at frames 100, 500, 900 -> x = left+100, left+500, left+900.
const std::int64_t frames[3] = {100, 500, 900};
const int midY = a.top + a.height() / 2;
CHECK(markerAtPoint(a, 1000, frames, 3, a.left + 100, midY) == 0);
CHECK(markerAtPoint(a, 1000, frames, 3, a.left + 500, midY) == 1);
CHECK(markerAtPoint(a, 1000, frames, 3, a.left + 900, midY) == 2);
const int midY = a.y + a.height / 2;
CHECK(markerAtPoint(a, 1000, frames, 3, a.x + 100, midY) == 0);
CHECK(markerAtPoint(a, 1000, frames, 3, a.x + 500, midY) == 1);
CHECK(markerAtPoint(a, 1000, frames, 3, a.x + 900, midY) == 2);
// Within the grab band on either side of the line.
CHECK(markerAtPoint(a, 1000, frames, 3, a.left + 500 + kMarkerGrabWidth, midY) == 1);
CHECK(markerAtPoint(a, 1000, frames, 3, a.left + 500 - kMarkerGrabWidth, midY) == 1);
CHECK(markerAtPoint(a, 1000, frames, 3, a.x + 500 + kMarkerGrabWidth, midY) == 1);
CHECK(markerAtPoint(a, 1000, frames, 3, a.x + 500 - kMarkerGrabWidth, midY) == 1);
}
static void testMarkerAtPointMissesBetween() {
const Rect a = wideArea();
const std::int64_t frames[3] = {100, 500, 900};
const int midY = a.top + a.height() / 2;
const int midY = a.y + a.height / 2;
// Well away from any marker line.
CHECK(markerAtPoint(a, 1000, frames, 3, a.left + 300, midY) == -1);
CHECK(markerAtPoint(a, 1000, frames, 3, a.x + 300, midY) == -1);
// Off the area vertically.
CHECK(markerAtPoint(a, 1000, frames, 3, a.left + 500, a.top - 5) == -1);
CHECK(markerAtPoint(a, 1000, frames, 3, a.x + 500, a.y - 5) == -1);
}
static void testMarkerAtPointFirstMatchOnOverlap() {
const Rect a = wideArea();
// Two markers at the same frame -> first in order wins.
const std::int64_t frames[2] = {400, 400};
const int midY = a.top + a.height() / 2;
CHECK(markerAtPoint(a, 1000, frames, 2, a.left + 400, midY) == 0);
const int midY = a.y + a.height / 2;
CHECK(markerAtPoint(a, 1000, frames, 2, a.x + 400, midY) == 0);
}
static void testMarkerAtPointRejectsNullEmpty() {
const Rect a = wideArea();
const int midY = a.top + a.height() / 2;
CHECK(markerAtPoint(a, 1000, nullptr, 3, a.left + 100, midY) == -1);
const int midY = a.y + a.height / 2;
CHECK(markerAtPoint(a, 1000, nullptr, 3, a.x + 100, midY) == -1);
const std::int64_t frames[1] = {100};
CHECK(markerAtPoint(a, 1000, frames, 0, a.left + 100, midY) == -1);
CHECK(markerAtPoint(a, 1000, frames, 0, a.x + 100, midY) == -1);
}
// --- resolveDragFrame ---------------------------------------------------------
@@ -130,7 +131,7 @@ static void testResolveDragFrameClamps() {
static void testResolveDragFrameRounds() {
// 500px area over 1000 frames -> 2 frames/px. A +3px drag -> round(6.0)=6; the rounding is
// at the frame centre. Use a scale where a fractional result appears.
const Rect a = Rect{0, 0, 300, 60}; // 1000 frames / 300px = 3.33 frames/px
const Rect a = Rect::ltrb(0, 0, 300, 60); // 1000 frames / 300px = 3.33 frames/px
// +3px -> 3*1000/300 = 10.0 -> 10 frames.
CHECK(resolveDragFrame(a, 1000, 100, 3) == 110);
// +1px -> 1000/300 = 3.33 -> rounds to 3.
@@ -138,7 +139,7 @@ static void testResolveDragFrameRounds() {
}
static void testResolveDragFrameDegenerate() {
const Rect z = Rect{0, 0, 0, 60}; // zero width
const Rect z = Rect::ltrb(0, 0, 0, 60); // zero width
CHECK(resolveDragFrame(z, 1000, 300, 100) == 300); // pinned to start
const Rect a = wideArea();
CHECK(resolveDragFrame(a, 0, 300, 100) == 0); // no frames -> clamp(start)=0