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 => {