fix: close round-3 review findings — smallest-target-first, residue test fix, extraction
Waveform overlay now resolves node/tab/marker click collisions by target area instead of check order; residue test now uses a distinguishing fixture; Gate-unavailable-while-drawn logic extracted to one pure helper shared by resolvePlay and applyControl.
This commit is contained in:
@@ -344,6 +344,25 @@ static void testTwoPointContourRisingFromZeroSoundsForItsFullSpan() {
|
||||
CHECK(v.soundingNote()); // still sounding at the midpoint, rising toward 1
|
||||
}
|
||||
|
||||
// The positive direction of the fix above: a contour whose final segment is flat at 0 (here,
|
||||
// the whole two-point span) DOES free the voice early, on its very first tick. Nothing exercises
|
||||
// this without it — a future tightening of the gate (e.g. requiring more than onFinalSegment() +
|
||||
// segmentEndValue()) could silently turn the early-free off, which is a performance regression
|
||||
// (a ringing but silent voice) rather than an audible one, so nothing else would catch it.
|
||||
static void testFlatZeroFinalSegmentStillFreesTheVoiceEarly() {
|
||||
const VelocityCurve contour =
|
||||
VelocityCurve::fromPoints({{kVelMin, 0.0}, {kVelMax, 0.0}}, CurveDomain::Unipolar);
|
||||
const std::size_t frames = 1000;
|
||||
const SampleData s = splineAmpSample(frames, contour);
|
||||
|
||||
Voice v;
|
||||
v.start(60, 100, s);
|
||||
CHECK(v.soundingNote()); // fresh note: sounding before anything is rendered
|
||||
const double y0 = v.renderFrame();
|
||||
CHECK(near(y0, 0.0, 1e-9));
|
||||
CHECK(!v.soundingNote()); // a genuine permanent terminus, not a mid-contour dip
|
||||
}
|
||||
|
||||
// --- 9. A fresh spline EG opens on the smooth y = 1 - x ----------------------
|
||||
|
||||
static void testAFreshSplineEgDefaultsToTheSmoothDownwardSlope() {
|
||||
@@ -424,6 +443,27 @@ static void testGateIsUnavailableWhileASplineEgIsActiveAndReturnsAfterwards() {
|
||||
CHECK(!deckKnobInert(DeckParam::kFilterModAmt, gates));
|
||||
}
|
||||
|
||||
// enforceGateUnavailableWhileDrawn (play_params.h) is the ONE enforcement resolvePlay and the
|
||||
// editor's applyControl both call — resolvePlay's own coverage above only exercises it through
|
||||
// the frames mirror; pin it directly over BOTH representations it is shared between, closing the
|
||||
// coverage gap the extraction was for (applyControl has no shell test target of its own).
|
||||
static void testEnforceGateUnavailableWhileDrawnForcesTriggerOnBothRepresentations() {
|
||||
PlaySeconds seconds;
|
||||
seconds.playMode = PlayMode::Gate;
|
||||
enforceGateUnavailableWhileDrawn(seconds);
|
||||
CHECK(seconds.playMode == PlayMode::Gate); // not splineActive -> untouched
|
||||
seconds.ampSpline.mode = EnvMode::Spline;
|
||||
enforceGateUnavailableWhileDrawn(seconds);
|
||||
CHECK(seconds.playMode == PlayMode::Trigger);
|
||||
|
||||
PlayParams frames;
|
||||
frames.playMode = PlayMode::Gate;
|
||||
frames.filter.enabled = true;
|
||||
frames.filterSpline.mode = EnvMode::Spline;
|
||||
enforceGateUnavailableWhileDrawn(frames);
|
||||
CHECK(frames.playMode == PlayMode::Trigger);
|
||||
}
|
||||
|
||||
// --- 11. The velocity->amp curve is the same grammar -------------------------
|
||||
|
||||
static void testTheVelocityAmpCurveGainsTheToggleAndKeepsItsDelete() {
|
||||
@@ -491,8 +531,10 @@ int main() {
|
||||
testAV12PayloadLoadsWithoutLoss();
|
||||
testAContourReplaysProportionallyOnADifferentLengthSample();
|
||||
testTwoPointContourRisingFromZeroSoundsForItsFullSpan();
|
||||
testFlatZeroFinalSegmentStillFreesTheVoiceEarly();
|
||||
testAFreshSplineEgDefaultsToTheSmoothDownwardSlope();
|
||||
testGateIsUnavailableWhileASplineEgIsActiveAndReturnsAfterwards();
|
||||
testEnforceGateUnavailableWhileDrawnForcesTriggerOnBothRepresentations();
|
||||
testTheVelocityAmpCurveGainsTheToggleAndKeepsItsDelete();
|
||||
testSplineCursorBinarySearchAgreesWithTheColdReaderOnAJumpingRead();
|
||||
if (g_fail == 0) std::printf("spline_egs: all tests passed\n");
|
||||
|
||||
Reference in New Issue
Block a user