fix(midi): route MIDI live via masterBus, fix media explorer drag, add action logging

- TrackInstrument: gate native live path (SF/VSTi WASAPI bridge bypassed
  masterBus -> mastering FX + main out VU dead). Fallback to SonicSF WASM
  through masterBus.input; native kept for offline render only.
- MEDIA_LIBRARY_SAMPLES: rename MIDI_Loop_02_Bass/03_Lead/05_Bass to match
  actual /static/midi files (drag was 404 -> parse error).
- Add SonicAppLogger (console + POST /api/v1/logs) wired to: instrument load
  error, VSTi autosample fail, MIDI item -> mastering fx chain, play -> main
  out, drag MIDI errors (media explorer + window explorer).
- Add backend /api/v1/logs endpoint (server-side log, no DB).
This commit is contained in:
2026-08-11 21:48:06 +07:00
parent e3c9d6e4ab
commit 59cf77bcbc
7 changed files with 121 additions and 15 deletions
+21 -5
View File
@@ -6284,7 +6284,9 @@ const preloadTrackInstruments = async (tracks) => {
const ch = t.midiChannel !== undefined ? t.midiChannel : (i % 16);
try {
await window.SonicSF.selectInstrument(ch, bank, prog, sfId);
} catch (e) {}
} catch (e) {
try { window.SonicAppLogger && window.SonicAppLogger.error('INSTRUMENT', 'Load instrument fail', { track: t.name, sfId: sfId, bank: bank, prog: prog, err: e && e.message }); } catch (e2) {}
}
}
};
@@ -13203,10 +13205,10 @@ const MasteringModal = ({ isOpen, onClose, masteringSettings, setMasteringSettin
// Media Explorer Panel (from md/51_MEDIA_EXPLORER.md)
const MEDIA_LIBRARY_SAMPLES = [
{ name: "MIDI_Loop_01.mid", events: 95, lengthQn: 16, time: "0:08.000", tpqn: 480, bpm: 130, kind: "midi" },
{ name: "MIDI_Loop_02_Bass.mid", events: 48, lengthQn: 16, time: "0:08.000", tpqn: 480, bpm: 130, kind: "midi" },
{ name: "MIDI_Loop_03_Lead.mid", events: 110, lengthQn: 16, time: "0:08.000", tpqn: 480, bpm: 130, kind: "midi" },
{ name: "MIDI_Loop_02.mid", events: 48, lengthQn: 16, time: "0:08.000", tpqn: 480, bpm: 130, kind: "midi" },
{ name: "MIDI_Loop_03.mid", events: 110, lengthQn: 16, time: "0:08.000", tpqn: 480, bpm: 130, kind: "midi" },
{ name: "MIDI_Loop_04.mid", events: 76, lengthQn: 16, time: "0:08.000", tpqn: 480, bpm: 130, kind: "midi" },
{ name: "MIDI_Loop_05_Bass.mid", events: 52, lengthQn: 16, time: "0:08.000", tpqn: 480, bpm: 130, kind: "midi" },
{ name: "MIDI_Loop_05.mid", events: 52, lengthQn: 16, time: "0:08.000", tpqn: 480, bpm: 130, kind: "midi" },
{ name: "MIDI_Loop_06.mid", events: 88, lengthQn: 16, time: "0:08.000", tpqn: 480, bpm: 130, kind: "midi" }
];
@@ -15592,7 +15594,10 @@ const App = () => {
window.SonicVstiAutosample.ensure(_warmEngine).then(_sfId => {
if (!_sfId && window.SonicVstiAutosample.failReasonFor) {
const _reason = window.SonicVstiAutosample.failReasonFor(_warmEngine);
if (_reason && window.showToast) window.showToast('Autosample VSTi that bai: ' + _reason + ' - dung am default', 'warning');
if (_reason) {
try { window.SonicAppLogger && window.SonicAppLogger.error('VSTI', 'Autosample VSTi fail', { plugin_id: instrumentId, reason: _reason }); } catch (e2) {}
if (window.showToast) window.showToast('Autosample VSTi that bai: ' + _reason + ' - dung am default', 'warning');
}
}
});
}
@@ -21879,6 +21884,11 @@ const App = () => {
const routeCarla = shouldRouteCarla(track.synth_engine);
const nativeSf = isStandaloneSf() && !routeCarla && isSfTrackEngine(track.synth_engine);
if ((track.type === 'MIDI' || midiItems.length > 0) && (window.SonicSF || routeCarla || nativeSf)) {
try {
const _mActive = !!(masterBus && masterBus.masteringActive);
window.SonicAppLogger && window.SonicAppLogger.info('MIDI_ITEM', nativeSf ? 'render_offline_native -> WAV -> mastering fx chain' : 'WASM -> masterBus.input -> mastering fx chain', { track: track.id, name: track.name, items: midiItems.length, masteringActive: _mActive, nativeSf: !!nativeSf });
window.SonicAppLogger && window.SonicAppLogger.info('MIDI_ITEM', 'play_qua_main_out', { track: track.id, name: track.name, items: midiItems.length, dest: 'masterBus.analyser -> ctx.destination' });
} catch (e2) {}
if (nativeSf) {
midiItems.forEach(item => scheduleNativeSfItem(track, item, offsetTime, context, gainNode, bpm, { sources: activeSourcesRef.current, isActive: () => !!isPlayingRef.current }));
} else {
@@ -24540,9 +24550,11 @@ const App = () => {
return new File([blob], mef.name, { type: 'audio/midi' });
}
} catch (e) {
try { window.SonicAppLogger && window.SonicAppLogger.error('DRAG_MIDI', 'Media Explorer drag resolve fail', { name: mef && mef.name, kind: mef && mef.kind, err: e && e.message }); } catch (e2) {}
showToast('Không thể nạp file từ Media Explorer: ' + e.message, 'error');
return null;
}
try { window.SonicAppLogger && window.SonicAppLogger.error('DRAG_MIDI', 'Media Explorer drag missing file data', { name: mef && mef.name, kind: mef && mef.kind }); } catch (e2) {}
showToast('Không thể nạp file từ Media Explorer: thiếu dữ liệu file.', 'error');
return null;
};
@@ -24557,6 +24569,7 @@ const App = () => {
var arrayBuffer = await file.arrayBuffer();
var midiResult = parseMidiFile(arrayBuffer);
if (!midiResult || midiResult.length === 0) {
try { window.SonicAppLogger && window.SonicAppLogger.error('DRAG_MIDI', 'Window explorer drag parse empty', { name: fileName, size: file.size }); } catch (e2) {}
showToast('Không tìm thấy nốt nhạc trong file MIDI.', 'error');
return;
}
@@ -24612,6 +24625,7 @@ const App = () => {
} : t));
showToast(`Nạp file thành công: ${fileName} (${channelInfo.label})`, 'success');
} catch (err) {
try { window.SonicAppLogger && window.SonicAppLogger.error('DRAG_MIDI', 'Window explorer drag load fail', { name: fileName, isMidi: isMidi, err: err && err.message }); } catch (e2) {}
showToast(isMidi ? "Lỗi giải mã MIDI." : "Lỗi giải mã âm thanh. Định dạng file không tương thích.", 'error');
}
};
@@ -24624,6 +24638,7 @@ const App = () => {
var arrayBuffer = await file.arrayBuffer();
var midiResult = parseMidiFile(arrayBuffer);
if (!midiResult || midiResult.length === 0) {
try { window.SonicAppLogger && window.SonicAppLogger.error('DRAG_MIDI', 'Drag MIDI parse empty', { name: file && file.name, size: file && file.size }); } catch (e2) {}
showToast('Không tìm thấy nốt nhạc trong file MIDI.', 'error');
return;
}
@@ -24645,6 +24660,7 @@ const App = () => {
if (newTracks.length > 0) setSelectedTrackId(newTracks[0].id);
showToast('Đã tải MIDI: ' + newTracks.length + ' track(s) từ ' + file.name, 'success');
} catch (err) {
try { window.SonicAppLogger && window.SonicAppLogger.error('DRAG_MIDI', 'Drag MIDI decode fail', { name: file && file.name, err: err && err.message }); } catch (e2) {}
showToast('Lỗi giải mã MIDI.', 'error');
}
};