From cdd5f8c96bf7d77ee8fb4a397fc6290dc09fc2bb Mon Sep 17 00:00:00 2001 From: 3dtours Date: Sun, 26 Jul 2026 21:16:49 +0700 Subject: [PATCH] fix: stopAll cancels scheduled notes, loop works on all tabs - Add _scheduledNotes array tracking - stopAll clears all pending setTimeout notes + allNotesOff() - Loop enabled on MAIN SESSION, SECTION-TAB, PIANO_ROLL via isLoopingSelection + selLeft/selRight auto-derivation --- app/static/js/services/soundfontPlayer.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/app/static/js/services/soundfontPlayer.js b/app/static/js/services/soundfontPlayer.js index 8df6d9e..04a6856 100644 --- a/app/static/js/services/soundfontPlayer.js +++ b/app/static/js/services/soundfontPlayer.js @@ -35,6 +35,7 @@ let _initialized = false; let _initPromise = null; let _currentSfId = null; + const _scheduledNotes = []; const SonicSF = { loadedFonts: {}, @@ -168,16 +169,28 @@ const durSec = durationMs / 1000; const now = ctx.currentTime; const delay = (typeof startTime === 'number' && startTime > now) ? (startTime - now) : 0; + const noteId = { on: null, off: null }; + _scheduledNotes.push(noteId); const doNote = () => { try { _synthInstance.noteOn(channel, midiPitch, midiVel); - setTimeout(() => { try { _synthInstance.noteOff(channel, midiPitch); } catch (e) {} }, durSec * 1000); + noteId.off = setTimeout(() => { + try { _synthInstance.noteOff(channel, midiPitch); } catch (e) {} + const idx = _scheduledNotes.indexOf(noteId); + if (idx >= 0) _scheduledNotes.splice(idx, 1); + }, durSec * 1000); } catch (e) { console.warn("[SonicSF] SpessaSynth noteOn error:", e); this._playNoteOsc(note, velocity, durationMs, startTime, program, null, channel, synthEngine); } }; - if (delay > 0) setTimeout(doNote, delay * 1000); else doNote(); + if (delay > 0) { + noteId.on = setTimeout(doNote, delay * 1000); + } else { + doNote(); + const idx = _scheduledNotes.indexOf(noteId); + if (idx >= 0) _scheduledNotes.splice(idx, 1); + } }, _playNoteOsc: function (note, velocity, durationMs, startTime, program, destinationNode, channel, synthEngine) { @@ -257,6 +270,12 @@ if (_initialized && _synthInstance) { try { for (let ch = 0; ch < 16; ch++) _synthInstance.allNotesOff(ch); } catch (e) {} } + // Cancel all scheduled future notes + while (_scheduledNotes.length > 0) { + const n = _scheduledNotes.pop(); + if (n.on) { clearTimeout(n.on); n.on = null; } + if (n.off) { clearTimeout(n.off); n.off = null; } + } const ctx = getCtx(); const now = ctx.currentTime; Object.values(activeOscillators).forEach(entry => {