fix(bridge): soundfont track khong co trong thu vien (sf_generaluser_gs) fallback sang GM font co san -> bounce ra du track, het cam
This commit is contained in:
+35
-28
@@ -7123,6 +7123,18 @@ const loadVstToBridge = async (track) => {
|
||||
return !!ok;
|
||||
} catch (e) { console.warn('[Bridge] loadVstToBridge fail:', track.id, e); return false; }
|
||||
};
|
||||
// GM font dự phòng khi soundfont track không có trong thư viện (VD id
|
||||
// sf_generaluser_gs do AI sinh nhưng user chưa tải font) — ưu tiên font GM
|
||||
// đầy đủ patch, fallback font đầu tiên liệt kê được.
|
||||
const pickFallbackSoundfont = (list) => {
|
||||
if (!list || !list.length) return null;
|
||||
const hay = (s) => (String(s.id || '') + ' ' + String(s.file || '') + ' ' + String(s.name || '')).toLowerCase();
|
||||
for (const w of ['sgm', 'generaluser', 'tgsf21x', 'timgm']) {
|
||||
const hit = list.find(s => hay(s).indexOf(w) !== -1);
|
||||
if (hit) return hit;
|
||||
}
|
||||
return list[0];
|
||||
};
|
||||
// D12: load track instrument (VST *hoặc* SF2) vào bridge. preloadTrackInstruments
|
||||
// trước đây chỉ load VST → mở project có SF2 track + bridge active → MIDI tới
|
||||
// channel bridge rỗng → C++ silent (không qua mastering chain native).
|
||||
@@ -7134,16 +7146,29 @@ const loadAnyTrackToBridge = async (track) => {
|
||||
const se = track.synth_engine || {};
|
||||
const bank = se.soundfont_bank !== undefined ? se.soundfont_bank : (track.soundfont_bank !== undefined ? track.soundfont_bank : 0);
|
||||
const bch = window.SonicMidiRouter.allocateChannel(track.id || track.trackId, bank === 128);
|
||||
let pl = null;
|
||||
try {
|
||||
const sfId = inst.replace('sf_', '');
|
||||
let bpath = null;
|
||||
try {
|
||||
const pl = await window.SonicAPI.listPlugins();
|
||||
pl = await window.SonicAPI.listPlugins();
|
||||
const s0 = ((pl && pl.soundfonts) || []).find(s => String(s.id).replace('sf_', '') === sfId);
|
||||
bpath = s0 && (s0.file || s0.path);
|
||||
} catch (e) {}
|
||||
const r = await window.SonicAPI.bridgeLoad({ name: inst, path: bpath || null, instrumentType: 'SF2', channel: bch });
|
||||
const p = r && r.path ? r.path : null;
|
||||
let r = await window.SonicAPI.bridgeLoad({ name: inst, path: bpath || null, instrumentType: 'SF2', channel: bch });
|
||||
let p = r && r.path ? r.path : null;
|
||||
if (!p) {
|
||||
// FALLBACK: soundfont id KHÔNG tồn tại trong thư viện (AI prompt mặc
|
||||
// định generaluser_gs nhưng user chưa tải font) → track câm im lặng.
|
||||
// Load GM font có sẵn (SGM/GeneralUser/tgsf21x) vào cùng channel —
|
||||
// __ensureBridgeProgram vẫn gửi CC0/CC32/PROGRAM → GM patch đúng.
|
||||
const fb = pickFallbackSoundfont(pl && pl.soundfonts);
|
||||
if (fb) {
|
||||
console.warn('[Bridge] SF2 not found:', inst, '→ fallback', fb.file || fb.id, '@ch', bch);
|
||||
const r2 = await window.SonicAPI.bridgeLoad({ name: inst, path: (fb.file || fb.path || null), instrumentType: 'SF2', channel: bch });
|
||||
p = r2 && r2.path ? r2.path : null;
|
||||
}
|
||||
}
|
||||
if (!p) { console.warn('[Bridge] SF2 resolve fail:', track.id, inst); return false; }
|
||||
const ok = await window.NativeBridgeService.loadInstrument(p, 'SF2', bch);
|
||||
if (ok) { if (!window.__bridgeLoadedChannels) window.__bridgeLoadedChannels = {}; window.__bridgeLoadedChannels[bch] = true; if (!window.__bridgeLoadedPaths) window.__bridgeLoadedPaths = {}; window.__bridgeLoadedPaths[bch] = p; }
|
||||
@@ -27993,31 +28018,13 @@ const App = () => {
|
||||
const c = midiCacheRef.current[t.id];
|
||||
return c && c.chunks.length > 0 && c.duration >= needDur - 0.3 && cacheHasEnergy(c);
|
||||
});
|
||||
// MIDI chỉ render offline qua preview cache (tap sfEntry lúc play). Cache
|
||||
// câm/thiếu khi routing bridge vào masterBus.input hoặc sub-node thay vì
|
||||
// sfEntry → file offline đủ duration nhưng KHÔNG CÓ ÂM THANH. DAW-standard:
|
||||
// project có MIDI → render OFFLINE deterministic qua server (đầy đủ MIDI +
|
||||
// FX + mastering, đúng như DAW). Bounce REALTIME race không đáng tin (từng
|
||||
// xuất file 1 note rồi câm / câm hẳn) → chỉ dùng làm fallback khi server lỗi.
|
||||
if (midiTracks.length > 0 && exportSettings.source === 'project' && exportSettings.format === 'wav') {
|
||||
if (serverStatus === 'connected') {
|
||||
// Đóng export panel + hiện progress timeline (renderMasteringExport
|
||||
// không tự đóng — bounce cũ có làm).
|
||||
setShowExportPanel(false);
|
||||
setShowExportProgress(true);
|
||||
setExportProgress(-1);
|
||||
try {
|
||||
await renderMasteringExport();
|
||||
} finally {
|
||||
setShowExportProgress(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!cacheOK) {
|
||||
showToast("Cache MIDI chưa đủ và server chưa kết nối — tự chuyển sang bounce REALTIME...", "info");
|
||||
await triggerBounceExport();
|
||||
return;
|
||||
}
|
||||
// sfEntry → file offline đủ duration nhưng KHÔNG CÓ ÂM THANH. Khi cache
|
||||
// không đủ → TỰ chuyển sang bounce REALTIME (capture masterBus.output =
|
||||
// đúng âm đang nghe) thay vì xuất file câm. Áp dụng Export Project (mix).
|
||||
if (midiTracks.length > 0 && !cacheOK && exportSettings.source === 'project' && exportSettings.format === 'wav') {
|
||||
showToast("Cache MIDI chưa đủ — tự chuyển sang bounce REALTIME để giữ nguyên âm thanh...", "info");
|
||||
await triggerBounceExport();
|
||||
return;
|
||||
}
|
||||
if (midiTracks.length > 0 && !cacheOK) {
|
||||
showToast("Project có MIDI items chưa đủ cache preview — file offline có thể thiếu âm MIDI. Bật Realtime để bounce trung thực.", "warning");
|
||||
|
||||
Reference in New Issue
Block a user