Sửa lỗi trực tiếp trên window

This commit is contained in:
2026-08-11 09:51:13 +07:00
parent aae0b05473
commit f8cb2c6a5e
16 changed files with 10409 additions and 1820 deletions
+20 -3
View File
@@ -573,7 +573,7 @@ def _carla_osc_ready() -> bool:
import socket
port = _carla_osc_port()
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("127.0.0.1", port))
s.bind(("0.0.0.0", port))
s.close()
return False
except OSError:
@@ -749,12 +749,20 @@ async def carla_stop(authorization: Optional[str] = Header(None)):
_send_carla_all_notes_off()
except Exception:
pass
# 2. Terminate tiến trình Carla đã spawn
# 2. Terminate tiến trình Carla đã spawn — Windows PHẢI giết cả process
# tree (Carla.exe spawn carla-backend child giữ cổng OSC 22752; terminate
# đơn lẻ chỉ giết cha → child sống → cổng chưa giải phóng → lần mở sau
# open_in_carla thấy already_running dù Carla đã chết → GUI không xuất
# hiện lại (Bug 3 standalone)).
killed = 0
_prune_carla_processes()
for proc in list(_CARLA_PROCESSES):
try:
proc.terminate()
if os.name == "nt" and proc.pid:
subprocess.run(["taskkill", "/PID", str(proc.pid), "/T", "/F"],
capture_output=True, timeout=15)
else:
proc.terminate()
except Exception:
pass
# Chờ tiến trình thoát (tối đa ~2s) rồi kill mạnh nếu còn sống
@@ -774,6 +782,15 @@ async def carla_stop(authorization: Optional[str] = Header(None)):
except Exception:
pass
_CARLA_PROCESSES.clear()
# 3. Chờ cổng OSC UDP thực sự được giải phóng (child có thể thoát chậm
# hơn cha) — nếu không, mở lại Carla ngay sẽ bị gate already_running.
try:
import time as _t
deadline = _t.time() + 5.0
while _t.time() < deadline and _carla_osc_ready():
_t.sleep(0.1)
except Exception:
pass
return {
"success": True,
"stopped": True,
+106 -5
View File
@@ -205,6 +205,37 @@ const shouldRouteCarla = (se) => !!(window.SonicCarlaMidi && window.SonicCarlaMi
// Mi key (thưng = track.id) mt Audio element note mi stop note cũ;
// token chng stale (response cũ không đè response mi).
const _nativeSfPreviews = {};
// WASM fallback: backend pyfluidsynth render HONG tren ban standalone
// (thieu libfluidsynth DLL) -> /soundfont-render 501 -> apiRequest throw ->
// native path cam lang. Fallback choi note TRUC TIEP qua SonicSF (Web Audio
// + libfluidsynth WASM da bundle) - am ra _gainNode -> masterBus.input.
// window.__nativeSfOk: undefined (chua biet) | true (native OK) | false (dung WASM).
const _sfWasmFallbackNote = (track, pitch, velocity, durationMs, startTime, key, token) => {
try {
const eng = track && track.synth_engine;
const sfId = (eng && eng.soundfont_id) || (track && track.instrumentId && String(track.instrumentId).startsWith('sf_') ? track.instrumentId : null);
if (!sfId) return;
const bank = (eng && eng.soundfont_bank) || 0;
const program = (eng && eng.soundfont_program) || (track && track.instrumentProgram !== undefined ? track.instrumentProgram : 0);
const k = key || (track ? track.id : 'global');
if (_nativeSfPreviews[k] && token != null && _nativeSfPreviews[k].token !== token) return; // stale
const S = window.SonicSF;
if (!S) return;
const ch = (track && track.midiChannel != null) ? track.midiChannel : 0;
const durMs = Math.max(200, durationMs || 500);
const ctx = getAudioContext();
const delaySec = startTime ? Math.max(0, (startTime - ctx.currentTime)) : 0;
_nativeSfPreviews[k] = { wasm: { ch: ch, pitch: pitch }, token: token };
const fire = function () {
Promise.resolve(S.selectInstrument(ch, bank, program, sfId)).then(function () {
S.playNote(pitch, velocity != null ? velocity : 0.8, durMs, undefined, eng ? undefined : program, null, ch, eng || undefined);
}).catch(function () {
try { S.playNote(pitch, velocity != null ? velocity : 0.8, durMs, undefined, eng ? undefined : program, null, ch, eng || undefined); } catch (e2) {}
});
};
if (delaySec > 0) { setTimeout(fire, delaySec * 1000); } else { fire(); }
} catch (e) { console.warn('[NativeSF] wasm fallback note error:', e); }
};
const playNativeSfNote = (track, pitch, velocity, durationMs, startTime, key) => {
try {
const eng = track && track.synth_engine;
@@ -215,6 +246,11 @@ const playNativeSfNote = (track, pitch, velocity, durationMs, startTime, key) =>
const k = key || (track ? track.id : 'global');
const prev = _nativeSfPreviews[k];
const token = (prev ? prev.token : 0) + 1;
// Native hong da biet (501) -> di thang WASM fallback, khong goi API.
if (window.__nativeSfOk === false) {
_sfWasmFallbackNote(track, pitch, velocity, durationMs, startTime, k, token);
return;
}
const durationSec = Math.max(0.2, (durationMs || 500) / 1000);
window.SonicAPI.soundfontRender({
soundfont_id: sfId,
@@ -223,8 +259,13 @@ const playNativeSfNote = (track, pitch, velocity, durationMs, startTime, key) =>
bpm: 120,
notes: [{ pitch: pitch, start_beat: 0, duration_beats: durationSec * 2, velocity: velocity != null ? velocity : 0.8 }],
}).then(function (res) {
if (!res || !res.success || !res.url) return;
if (!res || !res.success || !res.url) {
window.__nativeSfOk = false;
_sfWasmFallbackNote(track, pitch, velocity, durationMs, startTime, k, token);
return;
}
if (_nativeSfPreviews[k] && _nativeSfPreviews[k].token !== token) return; // stale
window.__nativeSfOk = true;
const audio = new Audio(API_BASE_URL + res.url);
_nativeSfPreviews[k] = { audio: audio, token: token };
const ctx = getAudioContext();
@@ -235,7 +276,10 @@ const playNativeSfNote = (track, pitch, velocity, durationMs, startTime, key) =>
audio._sfStopTimer && clearTimeout(audio._sfStopTimer);
audio._sfStopTimer = setTimeout(function () { try { audio.pause(); } catch (e) {} }, durationSec * 1000 + 400);
}, delay);
}).catch(function () {});
}).catch(function () {
window.__nativeSfOk = false;
_sfWasmFallbackNote(track, pitch, velocity, durationMs, startTime, k, token);
});
} catch (e) { console.warn('[NativeSF] playNativeSfNote error:', e); }
};
const stopNativeSfNote = (key) => {
@@ -245,6 +289,7 @@ const stopNativeSfNote = (key) => {
if (!prev) return;
prev.token++;
try { if (prev.audio) { prev.audio.pause(); prev.audio.currentTime = 0; } } catch (e) {}
try { if (prev.wasm && window.SonicSF) window.SonicSF.stopNote(prev.wasm.ch, prev.wasm.pitch); } catch (e) {}
} catch (e) {}
};
const stopAllNativeSfNotes = () => {
@@ -263,6 +308,10 @@ const scheduleNativeSfItem = (track, item, offsetTime, context, destNode, bpm, o
if (!sfId) return;
const notes = (item && item.notes) || [];
if (!notes.length) return;
if (window.__nativeSfOk === false) {
scheduleNativeSfItemWasm(track, item, offsetTime, context, destNode, bpm, opts);
return;
}
const secPerBeat = 60.0 / (parseInt(bpm) || 120);
const baseOffsetSec = (opts && opts.baseOffsetSec) || 0;
const itemStartAbs = baseOffsetSec + (item.startTime || 0);
@@ -278,7 +327,12 @@ const scheduleNativeSfItem = (track, item, offsetTime, context, destNode, bpm, o
bpm: parseFloat(bpm) || 120,
notes: notes.map(function (n) { return { pitch: n.pitch || 60, start_beat: n.start_beat || 0, duration_beats: n.duration_beats || 1, velocity: n.velocity != null ? n.velocity : 0.8 }; }),
}).then(function (res) {
if (!res || !res.success || !res.url) return;
if (!res || !res.success || !res.url) {
window.__nativeSfOk = false;
scheduleNativeSfItemWasm(track, item, offsetTime, context, destNode, bpm, opts);
return;
}
window.__nativeSfOk = true;
fetch(API_BASE_URL + res.url).then(function (r) { return r.arrayBuffer(); }).then(function (buf) {
context.decodeAudioData(buf, function (audioBuf) {
try {
@@ -299,11 +353,58 @@ const scheduleNativeSfItem = (track, item, offsetTime, context, destNode, bpm, o
if (opts && opts.sources && opts.sources.push) opts.sources.push(src);
} catch (e) { console.warn('[NativeSF] schedule decode error:', e); }
}, function () {});
}).catch(function () {});
}).catch(function () {});
}).catch(function () {
window.__nativeSfOk = false;
scheduleNativeSfItemWasm(track, item, offsetTime, context, destNode, bpm, opts);
});
}).catch(function () {
window.__nativeSfOk = false;
scheduleNativeSfItemWasm(track, item, offsetTime, context, destNode, bpm, opts);
});
} catch (e) { console.warn('[NativeSF] scheduleNativeSfItem error:', e); }
};
// WASM fallback cho item (backend 501): schedule TUNG NOTE qua SonicSF
// (Web Audio) dung vi tri thoi gian - thay vi render 1 file WAV ca item.
const scheduleNativeSfItemWasm = (track, item, offsetTime, context, destNode, bpm, opts) => {
try {
const eng = track && track.synth_engine;
const sfId = eng && eng.soundfont_id;
if (!sfId) return;
const notes = (item && item.notes) || [];
if (!notes.length) return;
const S = window.SonicSF;
if (!S) return;
const secPerBeat = 60.0 / (parseInt(bpm) || 120);
const baseOffsetSec = (opts && opts.baseOffsetSec) || 0;
const itemStartAbs = baseOffsetSec + (item.startTime || 0);
const now = context.currentTime;
const ch = (track && track.midiChannel != null) ? track.midiChannel : 0;
const bank = (eng && eng.soundfont_bank) || 0;
const program = (eng && eng.soundfont_program) || 0;
const fire = function () {
if (opts && typeof opts.isActive === 'function' && !opts.isActive()) return;
notes.forEach(function (n) {
try {
const noteStartSec = itemStartAbs + (n.start_beat || 0) * secPerBeat;
const noteDurSec = (n.duration_beats || 1) * secPerBeat;
const clipStart = Math.max(offsetTime, noteStartSec);
var durMs = (noteStartSec + noteDurSec - clipStart) * 1000;
if (opts && opts.limitSec) {
const limitMs = (opts.limitSec - clipStart) * 1000;
if (limitMs <= 0) return;
durMs = Math.min(durMs, limitMs);
}
if (durMs <= 0) return;
const startWall = now + Math.max(0, noteStartSec - offsetTime);
S.playNote(n.pitch || 60, n.velocity != null ? n.velocity : 0.8, durMs, startWall, undefined, destNode || null, ch, eng);
} catch (e) {}
});
};
Promise.resolve(S.selectInstrument(ch, bank, program, sfId)).then(fire).catch(fire);
} catch (e) { console.warn('[NativeSF] scheduleNativeSfItemWasm error:', e); }
};
// Handle ?sfs=<encoded> from double-clicking a .sfs file (opens domain -> loads project)
(function handleSfsDeepLink() {
try {
File diff suppressed because one or more lines are too long
+6
View File
@@ -120,6 +120,12 @@ window.SonicCarlaMidi = window.SonicCarlaMidi || {
stopBridge: function () {
var self = this;
try { self.allNotesOff(); } catch (e) {}
// Reset trạng thái Carla bridge — nếu không, sau unload các cờ stale
// (__carlaRunning=true) khiến ensureCarlaForPlayback early-return và
// GUI Carla KHÔNG mở lại được khi reload.
window.__carlaRunning = false;
window.__carlaOpening = false;
window.__carlaNoteQueue = [];
if (window.SonicAPI && window.SonicAPI.stopCarla) {
return window.SonicAPI.stopCarla().catch(function () {});
}