instrument: one staged-envelope system — per-segment curves, the sustain-less AHD, and a shared overlay for all three envelopes

Trigger's fade pair folds into the AHD (and goes live); the release anchors right;
Preserve rings its synthetic tail out instead of cutting it. Payload v10.
This commit is contained in:
2026-07-31 08:37:57 -04:00
parent 87d7ceb066
commit 13e8c5c4d9
51 changed files with 3406 additions and 1812 deletions
+11 -12
View File
@@ -28,11 +28,12 @@ bool ReaSamplerEditor::mouseDownWaveform(const FaceLayout& fl, int x, int y) {
if (frames <= 0) return false;
const OverlayArea overlay = waveformOverlayArea(fl.bands.waveform);
// Envelope nodes first (they sit on top of the markers), then the wave markers.
// Envelope nodes first (they sit on top of the markers), then the wave markers. With no
// envelope overlay-active there are no nodes at all and the markers take every grab.
const double rate = liveSampleRate();
if (rate > 0.0) {
if (rate > 0.0 && overlayEnv_ != OverlayEnv::kNone) {
const std::int64_t startFrame = params_.startPoint.value_or(0);
const AmpEnvelope env = packEnvelope(params_.play, frames, startFrame);
const StageEnvelope env = packEnvelope(overlayEnv_, params_.play, frames, startFrame);
const double totalSeconds = static_cast<double>(frames) / rate;
const NodeHit nh = nodeAtPoint(env, overlay, totalSeconds, x, y);
if (nh.hit) {
@@ -42,7 +43,6 @@ bool ReaSamplerEditor::mouseDownWaveform(const FaceLayout& fl, int x, int y) {
dragStartY_ = y;
dragStartEnv_ = env;
dragSampleFrames_ = frames;
dragStartFrame_ = startFrame;
dragStartParams_ = params_;
return true; // node moves once the cursor drags
}
@@ -68,18 +68,17 @@ void ReaSamplerEditor::dragWaveform(const FaceLayout& fl, int x, int y) {
if (drag_ == DragKind::kEnvNode) {
// Resolve the grabbed envelope node's new params from the pixel delta (through the
// pure envelope_edit inverse map, clamped + monotonic), then unpack them back onto
// the parameter set. The AmpEnvelope was snapshotted at grab (dragStartEnv_) so the
// delta is absolute.
// pure envelope_edit inverse map, clamped), then unpack them back onto the parameter
// set. The StageEnvelope was snapshotted at grab (dragStartEnv_) so the delta is
// absolute.
const std::int64_t frames = dragSampleFrames_;
const double rate = liveSampleRate();
if (frames <= 0 || rate <= 0.0) return;
const double totalSeconds = static_cast<double>(frames) / rate;
const AmpEnvelope edited = resolveNodeDrag(dragStartEnv_, envNode_, overlay, totalSeconds,
envClampBounds(), dx, y - dragStartY_);
unpackEnvelope(edited, frames, dragStartFrame_, params_.play);
// In Gate the node IS a live AHDSR control, so the sounding note follows the drag;
// Trigger's nodes rewrite the play span and still commit on release.
const StageEnvelope edited = resolveNodeDrag(dragStartEnv_, envNode_, overlay,
totalSeconds, envClampBounds(), dx,
y - dragStartY_);
unpackEnvelope(overlayEnv_, edited, params_.play);
if (dragCommitsLive(DragKind::kEnvNode)) commitLive();
invalidate(); // live feedback; commit on WM_LBUTTONUP
return;