Merge Γ-W2-T2: an explicit loop enable, four named marks with grabbable caps, and the crossfade painted where it is heard
This commit is contained in:
@@ -19,7 +19,9 @@
|
||||
#include "core/instrument/ui/envelope_edit.h" // EnvClampBounds / NodeHit (envelope node hit-test/edit)
|
||||
#include "core/instrument/ui/envelope_overlay.h" // StageEnvelope / EnvNode (envelope overlay draw seam)
|
||||
#include "core/instrument/ui/knob_deck.h" // DeckGroupDesc / DeckLayout (the deck band)
|
||||
#include "core/instrument/ui/loop_marks.h" // LoopMarks (the loop enable's state machine)
|
||||
#include "core/instrument/ui/sample_bands.h" // SampleBands (the band-stack allocator)
|
||||
#include "core/instrument/ui/waveform_view.h" // WaveMark / WaveMarks (the overlay's marks)
|
||||
#include "core/instrument/ui/spline_edit.h" // the shared point-editing grammar
|
||||
#include "core/instrument/ui/sample_chrome.h" // ChromeRects (chrome-band interior)
|
||||
#include "core/audio/peaks.h" // Envelope (the cached peak thumbnail)
|
||||
@@ -104,12 +106,13 @@ private:
|
||||
// [0, DeckParam::kCount) is what keeps liveCommitFor answering "not a live control".
|
||||
static constexpr int kBakeHoldKnobId = -3;
|
||||
|
||||
// The waveform markers on the waveform band: start-point + the sustain loop's two ends,
|
||||
// in draw + hit order, then the crossfade handle. The crossfade is NOT part of the
|
||||
// full-height column hit-test — it answers only in its top-strip handle (waveform_view's
|
||||
// The waveform band's four marks, in draw + hit order. The crossfade is NOT part of the
|
||||
// full-height column hit-test — it answers only in its cap (waveform_view's
|
||||
// markerHandleRect), because at a zero fade it sits exactly on the loop start.
|
||||
enum class WaveMarker { kStart = 0, kLoopStart = 1, kLoopEnd = 2, kLoopXfade = 3,
|
||||
kCount = 4 };
|
||||
using WaveMarker = instrument::ui::WaveMark;
|
||||
|
||||
// What those four marks are showing — see pickedMarkers.
|
||||
using SetupMarkers = instrument::ui::LoopMarks;
|
||||
|
||||
// The interactive element under the pointer, resolved live in WM_MOUSEMOVE. `index`
|
||||
// disambiguates within a kind (tab ordinal, visible-card index, control-row id); -1 when
|
||||
@@ -125,6 +128,9 @@ private:
|
||||
kBrowseCancel, // the Browse modal "Cancel" button
|
||||
kChanMono, // the mono channel-mode segment
|
||||
kChanStereo, // the stereo channel-mode segment
|
||||
kLoopOff, // the loop enable's Off segment
|
||||
kLoopOn, // the loop enable's On segment
|
||||
kWaveMark, // a waveform overlay mark (index = WaveMark ordinal); promotes its label
|
||||
kPreview, // the preview-trigger button
|
||||
kBake, // the resample-bake trigger
|
||||
kControl, // a knob-deck element (index = control id)
|
||||
@@ -162,12 +168,14 @@ private:
|
||||
// --- Band painters (one TU each, mirroring the input side) ---
|
||||
// Chrome: title band + Browse nav + the control row (root strip, preview, velocity knob,
|
||||
// channel toggle).
|
||||
void paintChrome(LICE_IBitmap* bmp, const FaceLayout& fl, bool empty);
|
||||
void paintChrome(LICE_IBitmap* bmp, const FaceLayout& fl, bool empty,
|
||||
const SetupMarkers& marks);
|
||||
// The hovered piano key's note-name chip. Drawn after every band — it overhangs the
|
||||
// chrome into whatever is below it.
|
||||
void paintChromeTooltip(LICE_IBitmap* bmp, const FaceLayout& fl, int w, int h);
|
||||
// Waveform: the channel lane(s), the loop/start markers, and the envelope overlay.
|
||||
void paintWaveform(LICE_IBitmap* bmp, const Rect& band);
|
||||
// Waveform: the channel lane(s), the four marks, and the envelope overlay. `marks` is
|
||||
// resolved once per paint by paintSample — see there.
|
||||
void paintWaveform(LICE_IBitmap* bmp, const Rect& band, const SetupMarkers& marks);
|
||||
// Decks: the group fence + caption + compact caption toggles + radial knobs with
|
||||
// label<->value swap on hover/drag.
|
||||
void paintDeck(LICE_IBitmap* bmp, const FaceLayout& fl);
|
||||
@@ -243,6 +251,9 @@ private:
|
||||
// mouse-down branches but are read-only. Windows-only.
|
||||
void resolveHover(int x, int y);
|
||||
HoverTarget hoverChrome(const FaceLayout& fl, int x, int y) const;
|
||||
// Marks only — the cap strip. Everything else in the band already reports its own state
|
||||
// through the drag, so nothing else on the overlay needs a hover.
|
||||
HoverTarget hoverWaveform(const FaceLayout& fl, int x, int y);
|
||||
HoverTarget hoverDeck(const FaceLayout& fl, int x, int y) const;
|
||||
HoverTarget hoverBrowse(int w, int h, int x, int y) const;
|
||||
HoverTarget hoverCurvePopup(int w, int h, int x, int y) const;
|
||||
@@ -326,20 +337,28 @@ private:
|
||||
// instance's own SampleRefs as the self-contained fallback. "" when unresolvable.
|
||||
std::string samplePathFor(const std::string& sampleId) const;
|
||||
|
||||
// The effective loop + start markers for the loaded capture: the parameter set's
|
||||
// override when one is set, else the bank's loop intrinsic / frame 0. With no loop set,
|
||||
// the loop handles park on loop_span's defaultLoopBounds so both stay grabbable — the
|
||||
// frame-0 default they replace put loopStart under the start marker, where nothing could
|
||||
// reach it.
|
||||
struct SetupMarkers {
|
||||
std::int64_t start = 0;
|
||||
std::int64_t loopStart = 0;
|
||||
std::int64_t loopEnd = 0;
|
||||
std::int64_t crossfade = 0; // pre-seam fade, SOURCE frames; handle at loopStart - this
|
||||
bool hasLoop = false; // whether a sustain loop is set (drives the "no loop" affordance)
|
||||
};
|
||||
// The effective loop + start markers for the loaded capture; the state machine behind them
|
||||
// is the pure loop_marks module's. With no override set this costs a bridge read plus a bank
|
||||
// parse — hoverWaveform calls it on every WM_MOUSEMOVE, so the answer is memoized (see
|
||||
// HoldNeedKey below, which this shares) rather than paid per move.
|
||||
SetupMarkers pickedMarkers(std::int64_t frames) const;
|
||||
|
||||
// Whether the loop controls answer at all: the sustain loop is Gate-only, so in Trigger the
|
||||
// marks and the chrome enable draw Disabled and inert. Reads the mode AFTER the drawn-EG
|
||||
// fold, so a drawn envelope disables them through the same predicate.
|
||||
bool loopControlsLive() const;
|
||||
|
||||
// Which marks the band DRAWS, and which of those accept a grab. They differ in exactly one
|
||||
// place — Trigger, where the loop marks stay drawn (hiding a set loop on a mode flip would
|
||||
// destroy information the user put there) but refuse every gesture, because that refusal
|
||||
// comes from the engine and no drag can talk it out of it.
|
||||
instrument::ui::WaveMarks waveMarksFor(const SetupMarkers& m) const;
|
||||
instrument::ui::WaveMarks grabbableMarks(const SetupMarkers& m) const;
|
||||
|
||||
// Flips the enable. `on` false retains the span and the crossfade — that retention is the
|
||||
// whole difference between a toggle and a delete button.
|
||||
void setLoopEnabled(bool on);
|
||||
|
||||
// Whether the loaded sound's bake window needs the user's Hold — the pure predicate
|
||||
// (bake_plan.h) answered against the markers this face is showing. Decodes and reads the
|
||||
// bank, so it is called on the sync tick, not per paint, and memoized against the inputs
|
||||
@@ -347,16 +366,22 @@ private:
|
||||
bool resolveBakeHoldNeeded();
|
||||
|
||||
// What that answer was last computed against. Invalidated wholesale by refreshFromBank,
|
||||
// which is where the bank half of the input changes.
|
||||
// which is where the bank half of the input changes. pickedMarkers' own memo (below) shares
|
||||
// this key shape rather than declaring a second one; resolveBakeHoldNeeded leaves startPoint
|
||||
// at its default (nullopt) since its own answer doesn't depend on it.
|
||||
struct HoldNeedKey {
|
||||
std::string sampleId;
|
||||
std::optional<SampleLoop> loopOverride;
|
||||
std::int64_t crossfade = 0;
|
||||
std::optional<std::int64_t> startPoint;
|
||||
bool operator==(const HoldNeedKey& other) const;
|
||||
};
|
||||
HoldNeedKey holdNeedKey_;
|
||||
bool holdNeedValid_ = false;
|
||||
bool holdNeedAnswer_ = false;
|
||||
mutable HoldNeedKey marksKey_;
|
||||
mutable SetupMarkers marksCache_;
|
||||
mutable bool marksValid_ = false;
|
||||
|
||||
// Writes `m` into params_ as the loop/start override. Does NOT call commitAndReload —
|
||||
// callers decide live-drag vs final commit.
|
||||
|
||||
Reference in New Issue
Block a user