FIX: 6 lỗi âm thanh/Carla/temp-save/Ctrl-S/FX Chain Carla bridge

- Soundfont preview: hủy note đang chờ load soundfont (stopNote/stopAll/panic) — hết âm loop không dừng với MIDI Keyboard; preview dùng channel riêng (applyAITrackInstrument) — hết sai instrument
- Carla bridge: endpoint /carla-stop (all-notes-off OSC + terminate process) + stopBridge() khi track chuyển VSTi -> soundfont — hết âm play qua Carla cũ
- MIDI items play qua Carla khi VSTi loaded: scheduleCarlaNote route vào startTrackPlayback + startLocalTrackPlayback + ghost notes
- Tự động lưu temp khi tắt app: beforeunload/pagehide sendBeacon + autosave 30s + ghi storage/temp/autosave.json + khôi phục khi load lại
- Ctrl-S: desktop -> save-to-disk (Documents/SonicForgeDAW/Projects); docker -> Cloud/local
- FX Chain (Mastering + FX Rack): module Carla Bridge (VST FX) — load/openInCarla + stop/unload, pass-through trong graph
This commit is contained in:
2026-08-10 05:34:32 +00:00
parent d20932a68b
commit c08233a163
10 changed files with 622 additions and 45 deletions
+35 -1
View File
@@ -29,6 +29,13 @@
let _scheduledNotes = [];
let _loadPromises = {};
let _sfloadSeq = 0;
// Note đang CHỜ load soundfont (chưa noteon) — 'ch:pitch' → số lần chờ.
// Đăng ký TRƯỚC khi load để stopNote/stopAll/panic hủy được note này;
// trước đây note deferred bắn TRỄ sau khi thả phím / sau Stop → âm loop
// không dừng (keybed preview âm soundfont).
let _pendingNoteOns = {};
// Tăng mỗi lần stopAll/panic — deferred doNote của generation cũ thành no-op.
let _noteGeneration = 0;
const getCtx = function () {
if (_audioCtx) {
@@ -468,8 +475,11 @@
stopNote: function (channel, pitch) {
if (channel < 0 || channel > 15) return;
var key = channel + ':' + pitch;
// Hủy note đang CHỜ LOAD soundfont (chưa noteon) — trước đây note
// này vẫn bắn TRỄ sau khi thả phím → âm loop không dừng được.
if (_pendingNoteOns[key]) delete _pendingNoteOns[key];
if (_initialized && _fluidModule) {
var key = channel + ':' + pitch;
var mappedChs = _activeNotes[key];
if (mappedChs === undefined) mappedChs = [channel];
for (var i = 0; i < mappedChs.length; i++) {
@@ -564,7 +574,24 @@
return;
}
console.log('[SonicSF] soundfont not loaded yet, loading:', finalSfId);
// Đăng ký note CHỜ LOAD trước khi load — để stopNote (thả
// phím) / stopAll / panic hủy được note này. Trước đây
// note deferred vẫn bắn TRỄ sau khi thả phím hoặc sau
// Stop → âm loop không dừng với preview MIDI Keyboard.
var _pendKey = ch + ':' + midiPitch;
var _gen = _noteGeneration;
_pendingNoteOns[_pendKey] = (_pendingNoteOns[_pendKey] || 0) + 1;
self.loadSoundFont(finalSfId).then(function (ok) {
// stopAll/panic chạy trong lúc load → generation đổi → bỏ
if (_gen !== _noteGeneration) return;
// stopNote (thả phím) đã hủy → KHÔNG bắn note trễ nữa
var _pend = _pendingNoteOns[_pendKey];
if (_pend) {
_pendingNoteOns[_pendKey] = _pend - 1;
if (_pendingNoteOns[_pendKey] <= 0) delete _pendingNoteOns[_pendKey];
} else {
return;
}
console.log('[SonicSF] loadSoundFont result:', ok, 'for:', finalSfId);
if (ok) {
doNote();
@@ -666,6 +693,9 @@
panic: function () {
_scheduledNotes.forEach(function (sn) { if (sn.on) { clearTimeout(sn.on); sn.on = null; } });
_scheduledNotes = [];
// Hủy mọi note đang CHỜ LOAD soundfont (deferred) — note cũ thành no-op
_noteGeneration++;
_pendingNoteOns = {};
if (_initialized && _fluidModule) {
// noteoff TỪNG note đang ngân (binding _fluid_synth_noteoff chắc
// chắn tồn tại — đã dùng cho duration hết) — all_notes_off có
@@ -743,6 +773,10 @@
},
stopAll: function () {
// Hủy mọi note đang CHỜ LOAD soundfont (deferred) — note cũ thành
// no-op sau Stop (âm loop không dừng khi preview MIDI Keyboard).
_noteGeneration++;
_pendingNoteOns = {};
if (_initialized && _fluidModule) {
// noteoff từng note đang ngân (binding chắc chắn tồn tại) —
// phòng all_notes_off không có trong WASM exports.