One taper, one modifier law: extract param_taper, raise the stage ceiling to 10 s, and make the AHDSR schematic axis the taper itself

This commit is contained in:
2026-08-01 19:09:19 -04:00
parent e589addc54
commit 3eb72d01c4
23 changed files with 1208 additions and 193 deletions
+22 -8
View File
@@ -245,22 +245,35 @@ static void testKnobNeedlePointOnCircle() {
static void testKnobDragUpIncreases() {
// Up (negative dy) increases, down decreases, scaled by the drag range.
CHECK(approx(knobDragValue(0.5, -32, 128), 0.75));
CHECK(approx(knobDragValue(0.5, +32, 128), 0.25));
CHECK(approx(knobDragValue(0.5, -32, {}, 128), 0.75));
CHECK(approx(knobDragValue(0.5, +32, {}, 128), 0.25));
// A full-range upward drag from 0 lands exactly at 1.
CHECK(approx(knobDragValue(0.0, -128, 128), 1.0));
CHECK(approx(knobDragValue(0.0, -128, {}, 128), 1.0));
// Default sensitivity applies when the range is omitted.
CHECK(approx(knobDragValue(0.0, -kKnobDragRangePixels), 1.0));
}
static void testKnobDragClamps() {
CHECK(approx(knobDragValue(0.9, -64, 128), 1.0)); // over-drag up clamps at 1
CHECK(approx(knobDragValue(0.1, +64, 128), 0.0)); // over-drag down clamps at 0
CHECK(approx(knobDragValue(0.9, -64, {}, 128), 1.0)); // over-drag up clamps at 1
CHECK(approx(knobDragValue(0.1, +64, {}, 128), 0.0)); // over-drag down clamps at 0
// The start value itself is clamped before the delta applies.
CHECK(approx(knobDragValue(1.5, 0, 128), 1.0));
CHECK(approx(knobDragValue(-0.5, 0, 128), 0.0));
CHECK(approx(knobDragValue(1.5, 0, {}, 128), 1.0));
CHECK(approx(knobDragValue(-0.5, 0, {}, 128), 0.0));
// A degenerate drag range yields the clamped start value.
CHECK(approx(knobDragValue(0.7, -50, 0), 0.7));
CHECK(approx(knobDragValue(0.7, -50, {}, 0), 0.7));
}
// Ctrl scales the drag rate; Shift+Ctrl is Shift, so the rate goes back to coarse. The snap
// itself is not this module's — only the rate is.
static void testCtrlScalesTheDragRateAndShiftOverridesIt() {
const DragModifiers fine{false, true};
const DragModifiers both{true, true};
CHECK(approx(knobDragValue(0.5, -32, fine, 128), 0.5 + 0.25 * kFineDragScale));
CHECK(approx(knobDragValue(0.5, -32, both, 128), 0.75));
CHECK(approx(knobDragValue(0.5, -32, DragModifiers{true, false}, 128), 0.75));
// Continuity across a transition is the CALLER's re-anchor, not this function's: at a
// zero delta both rates agree, which is exactly the state a re-anchor establishes.
CHECK(knobDragValue(0.42, 0, fine, 128) == knobDragValue(0.42, 0, {}, 128));
}
// --- controlAtPoint routing ---------------------------------------------------
@@ -321,6 +334,7 @@ int main() {
testKnobNeedlePointOnCircle();
testKnobDragUpIncreases();
testKnobDragClamps();
testCtrlScalesTheDragRateAndShiftOverridesIt();
testControlAtPointRoutes();
testControlAtPointMisses();