fix: close review findings on the velocity-curve deck and bipolar curves
Cancels the curve-node drag whenever the popup closes so Esc mid-drag can't alias the amp curve; generalizes CurveTarget routing to one switch; fixes stale/overstated comments; clamps a pre-v12 depth fold; adds deck-inertness and filter-fold test coverage.
This commit is contained in:
@@ -859,21 +859,36 @@ static void testPriorPayloadVersionsLiftToAHardSeam() {
|
||||
in.params.play.pitchVelocityCurve = reasampler::instrument::engine::VelocityCurve::fromPoints(
|
||||
{VelocityPoint{0.0, 0.5}, VelocityPoint{127.0, 1.0}},
|
||||
reasampler::instrument::engine::CurveDomain::Bipolar);
|
||||
// The filter's pre-v12 depth fold (see testPreV12FilterVelocityDepthFoldsIntoTheCurve for
|
||||
// the single-version proof); planted here too so EVERY version that carries a filter tail
|
||||
// (v9..v11) is walked, not just v11 — the fold's branch condition is pv < kParamsVelocityVersion.
|
||||
in.params.play.filter.enabled = true;
|
||||
in.params.play.filter.modAmount = -0.6251953125; // distinct, exactly representable anchor
|
||||
const reasampler::instrument::engine::VelocityCurve filterShape =
|
||||
reasampler::instrument::engine::VelocityCurve::fromPoints(
|
||||
{VelocityPoint{0.0, 0.0}, VelocityPoint{64.0, 0.25}, VelocityPoint{127.0, 1.0}},
|
||||
reasampler::instrument::engine::CurveDomain::Bipolar);
|
||||
in.params.play.filter.velocityCurve = filterShape;
|
||||
constexpr double kPlantedDepth = -0.75;
|
||||
|
||||
struct Case {
|
||||
std::uint32_t pv;
|
||||
std::size_t cut;
|
||||
bool keepsCurveTail;
|
||||
bool keepsFilterTail;
|
||||
};
|
||||
const Case cases[] = {
|
||||
{11, kVelocityTailBytes, true},
|
||||
{10, kVelocityTailBytes + kLoopTailBytes, true},
|
||||
{9, kVelocityTailBytes + kLoopTailBytes + kCurveTailBytes, false},
|
||||
{8, kVelocityTailBytes + kLoopTailBytes + kCurveTailBytes + kFilterTailBytes, false},
|
||||
{11, kVelocityTailBytes, true, true},
|
||||
{10, kVelocityTailBytes + kLoopTailBytes, true, true},
|
||||
{9, kVelocityTailBytes + kLoopTailBytes + kCurveTailBytes, false, true},
|
||||
{8, kVelocityTailBytes + kLoopTailBytes + kCurveTailBytes + kFilterTailBytes, false, false},
|
||||
};
|
||||
for (const Case& c : cases) {
|
||||
const ComponentState out =
|
||||
deserializeComponentState(payloadDowngradedTo(in, c.pv, c.cut), 48000.0);
|
||||
std::vector<std::uint8_t> bytes = payloadDowngradedTo(in, c.pv, c.cut);
|
||||
if (c.keepsFilterTail) {
|
||||
plantPreV12FilterDepth(bytes, in.params.play.filter.modAmount, kPlantedDepth);
|
||||
}
|
||||
const ComponentState out = deserializeComponentState(bytes, 48000.0);
|
||||
// The span itself has been in the format since v2 and must survive untouched.
|
||||
CHECK(out.params.loopOverride && out.params.loopOverride->hasLoop);
|
||||
CHECK(out.params.loopOverride && out.params.loopOverride->start == 2000);
|
||||
@@ -889,6 +904,20 @@ static void testPriorPayloadVersionsLiftToAHardSeam() {
|
||||
// And the cut landed on the tail boundary the ladder claims, not somewhere inside it.
|
||||
CHECK(out.params.play.adsr.attackCurve ==
|
||||
(c.keepsCurveTail ? 4.0 : reasampler::util::kCurveNeutral));
|
||||
// The filter's own velocity curve: folded by the planted depth where the tail survives
|
||||
// (v9..v11), the off/neutral default where it was cut away entirely (v8).
|
||||
if (c.keepsFilterTail) {
|
||||
CHECK(out.params.play.filter.enabled);
|
||||
for (int v = 0; v <= 127; ++v) {
|
||||
CHECK(std::fabs(out.params.play.filter.velocityCurve.eval(v) -
|
||||
kPlantedDepth * filterShape.eval(v)) < 1e-12);
|
||||
}
|
||||
} else {
|
||||
CHECK(!out.params.play.filter.enabled);
|
||||
for (int v = 0; v <= 127; ++v) {
|
||||
CHECK(out.params.play.filter.velocityCurve.eval(v) == 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user