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:
@@ -149,6 +149,13 @@ public:
|
||||
// The bridge, for the editor's live-state readout + sample list. Owned here; the
|
||||
// editor borrows it (outlives the editor).
|
||||
ReaperBridge& bridge() { return bridge_; }
|
||||
|
||||
// The live host sample rate latched from setupProcessing (the SAME rate reloadFromBank
|
||||
// resolves seconds->frames against). The editor's S-VIEW-3 envelope overlay reads it to place
|
||||
// its wall-clock seconds on the same time base the voice engine plays them over. 0.0 before
|
||||
// setupProcessing runs (the editor guards). Read on the UI thread; a plain load — sampleRate_
|
||||
// is set once by setupProcessing before any audio and does not change under the editor.
|
||||
double sampleRate() const { return sampleRate_; }
|
||||
// The current single-capture selection id (main/UI thread reads for the editor). Guarded
|
||||
// by selectionMutex_ — never touched on the audio thread. Since S10 this is the ONE picked
|
||||
// capture the default face plays chromatically when the performance map is empty; an EMPTY
|
||||
@@ -173,6 +180,22 @@ public:
|
||||
// (same mode) does neither. UI thread only.
|
||||
void setChannelMode(ChannelMode mode);
|
||||
|
||||
// The per-instance preview-trigger velocity (S-VIEW-4, MIDI 1..127). Read/written on the
|
||||
// UI thread (the Sample-view velocity knob) and by getState/setState (host load-save thread);
|
||||
// guarded by previewMutex_. Persisted in component state (v6). NOT read on the audio thread.
|
||||
std::uint8_t previewVelocity();
|
||||
void setPreviewVelocity(std::uint8_t velocity);
|
||||
|
||||
// Fire a one-shot PREVIEW note-on / note-off through the live voice engine (S-VIEW-4), OFF
|
||||
// the audio thread (the editor's preview-trigger button drives these on the UI thread). The
|
||||
// request is handed to process() via a lock-free single-slot mailbox drained at block start —
|
||||
// no allocation, no lock on the audio thread. previewNoteOn plays `note` at the current
|
||||
// previewVelocity(); previewNoteOff releases it (Gate) — Trigger zones ignore note-off and
|
||||
// play through. A momentary button (down = on, up = off) reads as a natural key press. This
|
||||
// is PLAYBACK ONLY: it never captures, never inserts a timeline item.
|
||||
void previewNoteOn(int note);
|
||||
void previewNoteOff(int note);
|
||||
|
||||
private:
|
||||
// Apply `mode` to the output audio bus's SpeakerArrangement (kMono / kStereo). Called from
|
||||
// initialize (topology) and setChannelMode (runtime change). Does NOT re-negotiate — the
|
||||
@@ -251,11 +274,30 @@ private:
|
||||
std::int64_t lastSeenBankGeneration_ = -1;
|
||||
|
||||
// S-VIEW-4 preview-trigger velocity (MIDI 1..127). Persisted in component state (v6) so the
|
||||
// user's chosen strike velocity survives a project save/reload. No concurrent writer before
|
||||
// Wave 2 (no editor knob yet) — setState and getState are the sole accessors, both on the
|
||||
// load/save thread (host-serialized). Default kPreviewVelocityDefault (64).
|
||||
// user's chosen strike velocity survives a project save/reload. Since Wave 2 the Sample-view
|
||||
// velocity knob writes it on the UI thread, so it is guarded by previewMutex_; setState and
|
||||
// getState (load/save thread) share the same guard. Default kPreviewVelocityDefault (64). NOT
|
||||
// read on the audio thread.
|
||||
std::mutex previewMutex_;
|
||||
std::uint8_t previewVelocity_ = kPreviewVelocityDefault;
|
||||
|
||||
// --- S-VIEW-4 preview-trigger mailbox (off-thread -> audio thread, lock-free) ---------
|
||||
// The editor's preview-trigger button posts a note-on/off request from the UI thread; process()
|
||||
// drains it at block start and drives the live engine. ONE slot per direction, each a packed
|
||||
// request whose high bits are a monotonically-incrementing sequence so process() detects a NEW
|
||||
// request by comparing against the last sequence it consumed (never re-firing a stale one). The
|
||||
// low 8 bits carry the note (on) / note (off); the on request also carries the velocity in the
|
||||
// next 8 bits, latched at post time so the audio thread reads no shared velocity field. A single
|
||||
// relaxed atomic load per block on the audio thread — RT-safe (no alloc, no lock).
|
||||
// packed = (seq << 16) | (velocity << 8) | note [note-on]
|
||||
// packed = (seq << 16) | note [note-off]
|
||||
std::atomic<std::uint32_t> previewOnRequest_{0}; // 0 = no request posted yet
|
||||
std::atomic<std::uint32_t> previewOffRequest_{0};
|
||||
std::uint16_t previewOnSeq_ = 0; // UI-thread post counter (never 0 after first post)
|
||||
std::uint16_t previewOffSeq_ = 0;
|
||||
std::uint16_t previewOnConsumed_ = 0; // audio-thread: last on-seq fired
|
||||
std::uint16_t previewOffConsumed_ = 0; // audio-thread: last off-seq fired
|
||||
|
||||
// Latched from setupProcessing so setActive/reload can size against it. Read
|
||||
// off-thread only. 0.0 is explicitly invalid — setupProcessing sets the real host rate
|
||||
// before any audio, and reloadFromBank guards on it before use.
|
||||
|
||||
Reference in New Issue
Block a user