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