instrument: one VELOCITY deck for all three velocity curves, bipolar and off by default for pitch and filter

Payload v12 appends the new velocity->pitch curve and folds the retired filter velAmount into its now-bipolar curve, so pre-v12 projects reopen sounding identical. Preview button takes a drawn play triangle.
This commit is contained in:
2026-07-31 19:15:17 -04:00
parent 4fecb58c0a
commit 9d38f87a2d
37 changed files with 1020 additions and 320 deletions
+37
View File
@@ -6,10 +6,14 @@
#include "../src/core/instrument/ui/curve_popup.h"
#include "../src/core/instrument/engine/velocity_curve.h"
#include <cstdio>
using namespace reasampler;
using namespace reasampler::instrument::ui;
using reasampler::instrument::engine::CurveDomain;
using reasampler::instrument::engine::VelocityCurve;
static int g_fail = 0;
#define CHECK(cond) do { if(!(cond)) { \
@@ -80,7 +84,40 @@ static void testOutsideSheetDismissTest() {
CHECK(!popupOutsideSheet(pl, pl.sheet.right() - 1, pl.sheet.bottom() - 1));
}
// The sheet hosts all three curves, and its box is domain-agnostic: the SAME curveBox drives a
// unipolar and a bipolar editor, and only the curve's own y map differs. Asserted here, against
// the popup's real geometry, because that is what makes one popup code path legitimate.
static void testTheSameCurveBoxHostsBothDomains() {
const CurvePopupLayout pl = computeCurvePopup(840, 620);
// The shell insets this rect before mapping; the inset is uniform, so any box inside the
// curveBox exercises the same relationship. Use the rect itself.
const VelocityCurve::Box box{pl.curveBox.x, pl.curveBox.y, pl.curveBox.width,
pl.curveBox.height};
CHECK(box.width > 1 && box.height > 1);
const VelocityCurve amp = VelocityCurve::flat();
const VelocityCurve mod = VelocityCurve::zero();
const int top = box.top;
const int bottom = box.top + box.height - 1;
// Both domains put their MAX on the top row and their MIN on the bottom row...
CHECK(amp.pixelFromPoint(box, {0.0, 1.0}).y == top);
CHECK(amp.pixelFromPoint(box, {0.0, 0.0}).y == bottom);
CHECK(mod.pixelFromPoint(box, {0.0, 1.0}).y == top);
CHECK(mod.pixelFromPoint(box, {0.0, -1.0}).y == bottom);
// ...so value 0 is the FLOOR for the amp curve and the MIDLINE for a modulation curve.
CHECK(mod.pixelFromPoint(box, {0.0, 0.0}).y == (top + bottom) / 2);
// And a click at the vertical centre adds a point at 0 in the bipolar editor, at 0.5 in
// the unipolar one — one hit-test path, two correct answers.
const int midY = (top + bottom) / 2;
CHECK(mod.pointFromPixel(box, box.left, midY).value == 0.0);
CHECK(amp.pointFromPixel(box, box.left, midY).value > 0.49);
CHECK(amp.pointFromPixel(box, box.left, midY).value < 0.51);
}
int main() {
testTheSameCurveBoxHostsBothDomains();
testDefaultWindowMidClamp();
testMinClamp();
testMaxClamp();