Phase S Wave 2: three-view ReaSampler 9000 editor (Sample home / Browse modal / Zone)
Retire the flat toggle; Sample-home face wires the Wave-1 envelope overlay + draggable nodes, preview button + velocity knob, Mono/Stereo, key-track. Browse reduced to a confirm/cancel modal; Zone retained with piano-pattern strip. RT-safe off-thread preview-note mailbox added.
This commit is contained in:
+103
-14
@@ -31,6 +31,8 @@
|
||||
#include "public.sdk/source/common/pluginview.h"
|
||||
|
||||
#include "editor_geometry.h" // Rect (the shell's sub-rect type, shared with the pure modules)
|
||||
#include "envelope_edit.h" // EnvClampBounds / NodeHit (S-VIEW-3 envelope node hit-test/edit)
|
||||
#include "envelope_overlay.h" // AmpEnvelope / EnvNode (S-VIEW-3 envelope overlay draw seam)
|
||||
#include "param_slider.h" // ControlRow (the S12/S15/S16 control-surface geometry)
|
||||
#include "peaks.h" // Envelope (the cached peak thumbnail)
|
||||
#include "sample_map.h" // SampleChoice, BankChoice, PerformanceMap (the shell's snapshot)
|
||||
@@ -64,16 +66,21 @@ protected:
|
||||
Steinberg::tresult PLUGIN_API onSize(Steinberg::ViewRect* newSize) override;
|
||||
|
||||
private:
|
||||
// Which face the editor shows. The browser is the default; the Zones panel is the
|
||||
// demoted opt-in view reached by the toggle. Both draw over the same snapshotted bank.
|
||||
enum class View { kBrowser, kZones };
|
||||
// Which face the editor shows (S-VIEW-1, three-view model). Sample is the HOME/default
|
||||
// face (the loaded capture). Browse is a full-window MODAL picker overlaid on Sample
|
||||
// (select + confirm/cancel changes the loaded capture, then dismisses). Zone is the
|
||||
// dedicated multi-zone keymap surface, button-summoned. All three draw over the same
|
||||
// snapshotted bank; Browse + Zone return to Sample when dismissed.
|
||||
enum class View { kSample, kBrowse, kZone };
|
||||
|
||||
// What a mouse drag is currently editing (the drag-state machine). kNone = no drag in
|
||||
// flight. The zone-edit grabs mirror keyboard_strip::ZoneGrab; kRootMarker is the
|
||||
// single-capture root drag on the setup strip; kWaveMarker is a draggable start/loop
|
||||
// marker on the S11 waveform surface (which marker is in waveMarker_).
|
||||
// marker on the S11 waveform surface (which marker is in waveMarker_); kEnvNode is a
|
||||
// draggable envelope breakpoint on the Sample-view hero overlay (S-VIEW-3, which node in
|
||||
// envNode_).
|
||||
enum class DragKind { kNone, kRootMarker, kZoneLow, kZoneHigh, kZoneBody, kWaveMarker,
|
||||
kScrollThumb, kParamSlider };
|
||||
kScrollThumb, kParamSlider, kEnvNode };
|
||||
|
||||
// The parameter controls on the setup surface (S12 AHDSR + the S15/S16 control surfaces).
|
||||
// The int value is the ControlDesc id the pure param_slider hit-test returns; the shell
|
||||
@@ -93,6 +100,7 @@ private:
|
||||
kPitchEnvAttack, // AD pitch attack (S16)
|
||||
kPitchEnvDecay, // AD pitch decay (S16)
|
||||
kPitchEnvDepth, // AD pitch depth in +/- semitones (S16)
|
||||
kKeyTrack, // S-VIEW-6 key-tracking 0..200% (lives on PerformanceZone, not ZonePlaySeconds)
|
||||
kCount
|
||||
};
|
||||
|
||||
@@ -111,13 +119,17 @@ private:
|
||||
// bank_panel's L2 hover model.
|
||||
enum class HoverKind {
|
||||
kNone,
|
||||
kToggleBrowser, // the Browser toggle segment
|
||||
kToggleZones, // the Zones toggle segment
|
||||
kNavBrowse, // the Sample-view "Browse" title-band button (opens the Browse modal)
|
||||
kNavZone, // the Sample-view "Zone" title-band button (opens the Zone surface)
|
||||
kBack, // the Browse/Zone "back" affordance (returns to Sample)
|
||||
kSearchBox, // the browser search box
|
||||
kFilterTab, // a bank-filter tab (index = tab ordinal, 0 = All)
|
||||
kCard, // a capture card (index = visible_ index)
|
||||
kBrowseConfirm, // the Browse modal "Load" confirm button
|
||||
kBrowseCancel, // the Browse modal "Cancel" button
|
||||
kChanMono, // the mono channel-mode segment
|
||||
kChanStereo, // the stereo channel-mode segment
|
||||
kPreview, // the Sample-view preview-trigger button
|
||||
kAddZone, // the "+ Add Zone" button
|
||||
kDeleteZone, // the "Delete" zone button
|
||||
kControl, // a param-panel control row (index = ControlDesc id)
|
||||
@@ -131,16 +143,30 @@ private:
|
||||
|
||||
#ifdef _WIN32
|
||||
void paint(HDC hdc);
|
||||
void paintBrowser(LICE_IBitmap* bmp, int w, int h);
|
||||
void paintSetup(LICE_IBitmap* bmp, const Rect& area);
|
||||
void paintZones(LICE_IBitmap* bmp, int w, int h);
|
||||
void paintSample(LICE_IBitmap* bmp, int w, int h); // S-VIEW-2 home face
|
||||
void paintBrowse(LICE_IBitmap* bmp, int w, int h); // S-VIEW-5 modal picker overlay
|
||||
void paintZone(LICE_IBitmap* bmp, int w, int h); // S-VIEW-8 zone surface
|
||||
void paintEmptyState(LICE_IBitmap* bmp, const Rect& area);
|
||||
void paintControls(LICE_IBitmap* bmp, const Rect& panel); // S12/S15/S16 param surface
|
||||
void paintControls(LICE_IBitmap* bmp, const Rect& panel, const PerformanceZone& zone); // S12/S15/S16 + keyTrack
|
||||
// Trace the S-VIEW-3 amp-envelope overlay + its draggable node handles over `waveArea` for
|
||||
// `zone`'s play params, at the sample's wall-clock duration. Shared by the Sample hero band.
|
||||
void paintEnvelopeOverlay(LICE_IBitmap* bmp, const Rect& waveArea, const PerformanceZone& zone,
|
||||
std::int64_t frames);
|
||||
|
||||
void onMouseDown(int x, int y);
|
||||
void onMouseMove(int x, int y);
|
||||
void onMouseUp(int x, int y);
|
||||
|
||||
// Route a click at (x,y) into the param control panel `panel` editing map_.zones[zoneIndex]:
|
||||
// a toggle segment commits immediately, a slider grab starts a live param-drag (kParamSlider),
|
||||
// the keyTrack slider likewise (against the zone's keyTrack scalar). Returns true when the
|
||||
// click landed on a control (handled). `zoneIndex` must be a valid index into map_.zones.
|
||||
bool handleControlClick(int zoneIndex, const Rect& panel, int x, int y);
|
||||
// Apply a slider/toggle interaction to map_.zones[zoneIndex] for control `id`: routes ordinary
|
||||
// controls through applyControl against the zone's play struct, and kKeyTrack against the
|
||||
// zone's keyTrack scalar (0..200% over the slider's 0..1). Used by both the click + drag paths.
|
||||
void applyZoneControl(int zoneIndex, int id, double value, int segment);
|
||||
|
||||
// Resolve the interactive element under (x, y) into hover_ (Phase L, L3). Called from
|
||||
// WM_MOUSEMOVE (also while a drag is in flight — the resolved element just isn't used
|
||||
// for a hover repaint mid-drag). Repaints only when the hovered element changed, so an
|
||||
@@ -244,6 +270,49 @@ private:
|
||||
// into the control's stored domain) or a toggle's `segment` (0/1). Mutates `play` in place.
|
||||
void applyControl(int id, ZonePlaySeconds& play, double value, int segment) const;
|
||||
|
||||
// --- S-VIEW-3 envelope overlay seam (frames <-> fraction converter) ----------
|
||||
//
|
||||
// envelope_overlay's AmpEnvelope is a DERIVED VIEW, not a TriggerParams copy: it stores the
|
||||
// Trigger fades as FRACTIONS of the played span, while the zone stores them as SOURCE FRAMES.
|
||||
// These two members own the non-trivial conversion on BOTH paths (documented in
|
||||
// envelope_overlay.h's TRIGGER SEAM note). `frames` is the sample's total source frame count;
|
||||
// `rate` is the live sample rate (the wall-clock AHDSR seconds are rate-free and copy 1-to-1,
|
||||
// but the Trigger played-span math needs the frame count).
|
||||
|
||||
// PACK (draw): zone play params -> AmpEnvelope. Copies AHDSR seconds directly; derives the
|
||||
// Trigger fade fractions from the source-frame fades over the played span.
|
||||
AmpEnvelope packEnvelope(const ZonePlaySeconds& play, std::int64_t frames) const;
|
||||
|
||||
// UNPACK (commit): an edited AmpEnvelope -> the zone's play params. Copies AHDSR seconds
|
||||
// directly; converts the Trigger fade fractions back to source frames over the played span.
|
||||
// Mutates `play` in place; only the mode-relevant fields are written.
|
||||
void unpackEnvelope(const AmpEnvelope& env, std::int64_t frames, ZonePlaySeconds& play) const;
|
||||
|
||||
// The clamp bounds envelope_edit uses, matching the control-panel sliders' own domains (so a
|
||||
// node drag can never produce a param a slider couldn't — the S-VIEW-F2 invariant).
|
||||
EnvClampBounds envClampBounds() const;
|
||||
|
||||
// --- Sample-view resolution helpers (the ONE storage site, S15-F2) -----------
|
||||
//
|
||||
// The single-capture Sample face reads/writes the same one-zone map site as the Zone surface.
|
||||
// These resolve the effective values for the picked id: effectiveSampleZone returns the picked
|
||||
// id's one-zone override (found in map_) or a product-default PerformanceZone bound to the
|
||||
// picked id (not yet materialized — a control edit materializes it, mirroring the Zone path).
|
||||
PerformanceZone effectiveSampleZone() const;
|
||||
// The effective root: the picked id's rootOverride, else its bank intrinsic, else middle C.
|
||||
int effectiveRoot() const;
|
||||
// The live sample rate from the bridge (for the envelope overlay's seconds<->frames time base),
|
||||
// or 0 when unavailable (the caller guards). Matches the voice engine's resolution rate.
|
||||
double liveSampleRate() const;
|
||||
// The persisted preview velocity as a 0..1 slider value (MIDI 1..127 mapped onto [0,1]).
|
||||
double previewVelocity01() const;
|
||||
|
||||
// Find-or-materialize the one-zone override for the picked id and return a mutable index into
|
||||
// map_.zones (appending a product-default zone if none exists). selectedId_ must be non-empty.
|
||||
// The mirror of upsertPickedOverride for a control edit — used when a Sample-face control edit
|
||||
// needs a concrete zone to write. Returns -1 if selectedId_ is empty.
|
||||
int ensureSampleZone();
|
||||
|
||||
ReaSamplerProcessor* processor_ = nullptr;
|
||||
|
||||
// --- Snapshot of the live bank (drawn each paint; refreshed off the audio thread) ---
|
||||
@@ -255,9 +324,22 @@ private:
|
||||
ChannelMode channelMode_ = ChannelMode::Mono; // S7 mono/stereo toggle snapshot
|
||||
|
||||
// --- Transient UI state (not persisted; component state carries selection + zones) ---
|
||||
View view_ = View::kBrowser; // default face is the browser
|
||||
View view_ = View::kSample; // default face is the loaded-sample home (S-VIEW-1)
|
||||
std::string activeFilterBankId_; // "" = All; else a bank id from banks_
|
||||
int selectedZone_ = -1; // highlighted zone in the Zones panel; -1 = none
|
||||
int selectedZone_ = -1; // highlighted zone in the Zone surface; -1 = none
|
||||
|
||||
// --- S-VIEW-5 Browse modal picker (a selection PENDING confirm) ---------------
|
||||
// The Browse overlay is a select-then-confirm picker: a click marks a pending pick without
|
||||
// loading it; Confirm (or double-click) commits it to selectedId_ + reloads and returns to
|
||||
// Sample; Cancel discards it and returns to Sample unchanged. "" = nothing picked yet.
|
||||
std::string browsePendingId_;
|
||||
int lastBrowseClickCard_ = -1; // for double-click-to-load detection (visible_ index)
|
||||
|
||||
// --- S-VIEW-4 preview-trigger note (transient) -------------------------------
|
||||
// The MIDI note the preview button is currently sounding (a held Gate voice), or -1 when the
|
||||
// button is up. Set on preview-button press (note-on posted to the processor), cleared on
|
||||
// release (note-off posted). One note at a time — a fresh press releases the prior.
|
||||
int previewingNote_ = -1;
|
||||
|
||||
// --- S13 drop-to-load affordance (relay DEGRADED — transient, never persisted) ----
|
||||
// S13's cross-artifact ingest relay (editor drop -> extension ingest) is NOT shipped: the
|
||||
@@ -308,9 +390,16 @@ private:
|
||||
// from it). S12/S15/S16 param-slider drag: which control id + the panel it lives in (the
|
||||
// shell re-lays the panel each move to map x->value against the live control rect).
|
||||
int dragStartScrollOffset_ = 0;
|
||||
int dragParamId_ = -1;
|
||||
int dragParamId_ = -1; // ControlDesc id under a kParamSlider drag; -2 = preview-vel knob
|
||||
int dragParamZone_ = -1; // the zone index a kParamSlider drag edits (Sample or Zone face)
|
||||
Rect dragParamPanel_{};
|
||||
|
||||
// S-VIEW-3 envelope-node drag: which node is grabbed + the AmpEnvelope snapshotted at grab
|
||||
// (so the pixel delta is absolute, per envelope_edit's grabEnv contract). The overlay rect +
|
||||
// sample frame count are re-derived at move time from the live Sample-view layout.
|
||||
EnvNode envNode_ = EnvNode::Origin;
|
||||
AmpEnvelope dragStartEnv_{};
|
||||
|
||||
// --- Peak-thumbnail cache (mirror of bank_panel; id -> envelope at a bin width) ------
|
||||
// Keyed by "id|binCount" so a resize recomputes at the new width. Cleared on refresh so
|
||||
// a bank edit (a re-captured or deleted sample) does not show a stale thumbnail.
|
||||
|
||||
Reference in New Issue
Block a user