fix(voice): CC 120 hard-stops voices (incl. Trigger); Mono sizes voices_ to 1; comments corrected
CC 120 -> allSoundsOff/hardStop (immediate silence, stops Trigger one-shots); CC 123 -> allNotesOff/releaseAll (release, unchanged). Mono VoiceEngine sizes voices_ to 1 structurally. Legato same-note re-press edge documented. Three new tests.
This commit is contained in:
+20
-7
@@ -428,6 +428,11 @@ public:
|
||||
// TRIGGER mode it is a NO-OP (Trigger ignores note-off and plays through to its play length).
|
||||
void release();
|
||||
|
||||
// HARD STOP — CC 120 (All Sounds Off) semantics. Immediately silences this voice regardless
|
||||
// of play mode: sets active_ = false with no release ramp. Stops a ringing Trigger one-shot
|
||||
// instantly (which release() cannot do). RT-safe: no allocation, no lock.
|
||||
void hardStop();
|
||||
|
||||
// True while this voice is producing (or about to produce) sound.
|
||||
bool active() const { return active_; }
|
||||
// The note this voice was started on (for note-off routing). Meaningless if idle.
|
||||
@@ -568,14 +573,19 @@ public:
|
||||
// the older tail to ring — matches hardware behavior). No-op if none match.
|
||||
void noteOff(int note);
|
||||
|
||||
// PANIC / MIDI All-Notes-Off (CC 123). Clears the MONO held stack and releases EVERY
|
||||
// active voice: Gate voices enter their release tail; Trigger one-shots ignore release
|
||||
// by design and play through their bounded play length (they can never be stuck). This
|
||||
// is the mono stack's ONLY reset path — a phantom entry left by a lost note-off would
|
||||
// otherwise be resurrected by the next fallback and sustain forever with no key held.
|
||||
// RT-safe (no allocation, bounded by maxVoices); callable from the audio thread.
|
||||
// CC 123 — MIDI All-Notes-Off: clears the MONO held stack and RELEASES every active voice
|
||||
// (Gate voices enter their AHDSR release tail; Trigger one-shots ignore release and play
|
||||
// through their bounded play length). This is the mono stack's ONLY reset path — a phantom
|
||||
// entry left by a lost note-off would otherwise be resurrected by the fallback and sustain
|
||||
// forever with no key held. RT-safe (no allocation, bounded by maxVoices).
|
||||
void allNotesOff();
|
||||
|
||||
// CC 120 — MIDI All-Sounds-Off: hard-stops EVERY voice immediately (active_ = false, no
|
||||
// release ramp), clears the MONO held stack, and silences even Trigger one-shots that would
|
||||
// ignore a release. Use for panic; CC 123 for the softer "let gates release" behavior.
|
||||
// RT-safe (no allocation, bounded by maxVoices); callable from the audio thread.
|
||||
void allSoundsOff();
|
||||
|
||||
// REAL-TIME render (S4): sums all active voices into the caller-provided buffer
|
||||
// `out[0..frameCount)`, ADDING to whatever is there (the caller clears or mixes —
|
||||
// this never touches memory it does not own and NEVER allocates). This is the
|
||||
@@ -680,9 +690,12 @@ public:
|
||||
// Release the preview IF `note` is the one sounding (a stale off for a replaced note is a
|
||||
// no-op). Gate zones enter release; Trigger zones ignore note-off and play through.
|
||||
void noteOff(int note);
|
||||
// PANIC peer of VoiceEngine::allNotesOff: release the ringing preview UNCONDITIONALLY,
|
||||
// CC 123 peer of VoiceEngine::allNotesOff: release the ringing preview UNCONDITIONALLY,
|
||||
// whatever note it is on. Gate enters release; Trigger plays through (bounded). RT-safe.
|
||||
void releaseAll();
|
||||
// CC 120 peer of VoiceEngine::allSoundsOff: HARD-STOP the preview immediately (no release
|
||||
// ramp, silences Trigger one-shots too). RT-safe.
|
||||
void hardStop();
|
||||
|
||||
bool active() const { return voice_.active(); }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user