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:
2026-07-31 19:46:37 -04:00
parent 9d38f87a2d
commit cfb53aade3
22 changed files with 223 additions and 99 deletions
+30 -6
View File
@@ -53,7 +53,9 @@ namespace {
// The ceiling is READ from the overlay's schematic scale rather than restated: the AHDSR
// schematic anchors a maxed knob at the canvas edge, which only holds while the two agree.
constexpr double kEnvTimeMaxSeconds = instrument::ui::kGateStageMaxSeconds;
constexpr double kPitchDepthMaxSemis = 24.0; // pitch depth throw: +/-24 st, centered
// Pitch depth throw: +/-kVelocityPitchRangeSemitones, centered. The one throw the pitch
// envelope's peak and the velocity->pitch curve's full scale both speak (play_params.h).
constexpr double kPitchDepthMaxSemis = kVelocityPitchRangeSemitones;
constexpr double kKeyTrackMax = 2.0; // key-track slider ceiling (0..200%)
// The raw stored curve exponent for a curve-dial control id, read DIRECTLY off the field —
@@ -510,21 +512,43 @@ void ReaSamplerEditor::unpackEnvelope(OverlayEnv which, const StageEnvelope& env
}
}
const VelocityCurve& ReaSamplerEditor::editedCurve() const {
switch (curvePopup_) {
const VelocityCurve& ReaSamplerEditor::curveFor(CurveTarget target) const {
switch (target) {
case CurveTarget::kPitch: return params_.play.pitchVelocityCurve;
case CurveTarget::kFilter: return params_.play.filter.velocityCurve;
case CurveTarget::kAmp:
case CurveTarget::kNone:
// kNone only reaches here from a paint/hover racing the close; the amp curve is a
// valid, harmless read rather than a branch every caller would have to repeat.
// kNone reads as amp — a valid, harmless choice for a target-agnostic caller (a
// disabled deck cell still needs SOME curve to draw). editedCurve()'s mutable
// overload below refuses kNone rather than relying on this fallback to protect amp.
return params_.velocityCurve;
}
return params_.velocityCurve;
}
VelocityCurve& ReaSamplerEditor::curveFor(CurveTarget target) {
return const_cast<VelocityCurve&>(std::as_const(*this).curveFor(target));
}
const VelocityCurve& ReaSamplerEditor::editedCurve() const { return curveFor(curvePopup_); }
VelocityCurve& ReaSamplerEditor::editedCurve() {
return const_cast<VelocityCurve&>(std::as_const(*this).editedCurve());
// Refuses kNone rather than aliasing amp (see curvePopup_'s declaration for why); should be
// unreachable now that every writer of kNone also cancels the drag, but this is the second,
// independent line of defense.
if (curvePopup_ == CurveTarget::kNone) {
static VelocityCurve sink = VelocityCurve::flat();
return sink;
}
return curveFor(curvePopup_);
}
void ReaSamplerEditor::closeCurvePopup() {
curvePopup_ = CurveTarget::kNone;
if (drag_ == DragKind::kCurveNode) {
drag_ = DragKind::kNone;
curvePointIndex_ = -1;
}
}
void ReaSamplerEditor::applyParamControl(int id, double value, int segment) {