fix: 3 bug standalone — soundfont preview/play, Carla bridge play item, realtime sync LAN/Tauri

Bug1: dispatcher CREATE_MIDI_ITEM track_id number vs state string -> String(); play+preview native soundfont verified.
Bug2: Carla play item — chờ OSC ready (poll carla-status, queue nốt khi cold start), chống spawn trùng (carla-status dò port bind, open-in-carla skip khi đã chạy).
Bug3: realtime sync — GET /temp/revision (updated_at+client_id), poll 3s, apply qua deserializeProjectFromSchema; hash-skip autosave + client_id chống ping-pong.
This commit is contained in:
2026-08-10 22:10:12 +07:00
parent d31e226f00
commit aa1cd8d5de
7 changed files with 238 additions and 32 deletions
+23 -3
View File
@@ -61,6 +61,21 @@
let autoSaveTimer = null;
let lastGetProjectStateCallback = null;
// ⚠️ FIX (Bug 3): hash-skip — nếu state serialize GIỐNG HỆT bản đã lưu
// thì KHÔNG ghi lại (server + localStorage). Chặn ping-pong realtime sync:
// client B apply state của A rồi autosave lại → server bump updated_at →
// A tưởng mới → apply → ... vô hạn. Bản giống hệt → bỏ qua → hội tụ.
let lastSavedJson = '';
function getClientId() {
try {
let c = localStorage.getItem('sonic_client_id');
if (!c) {
c = 'c_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 10);
localStorage.setItem('sonic_client_id', c);
}
return c;
} catch (e) { return ''; }
}
function scheduleTempAutoSave(getProjectStateCallback) {
if (getProjectStateCallback) lastGetProjectStateCallback = getProjectStateCallback;
if (autoSaveTimer) clearTimeout(autoSaveTimer);
@@ -69,9 +84,11 @@
const state = lastGetProjectStateCallback ? lastGetProjectStateCallback() : null;
if (!state || (!state.tracks && !state.main_session)) return;
const dataJson = JSON.stringify(state);
if (dataJson === lastSavedJson) return; // không đổi → không ghi
lastSavedJson = dataJson;
localStorage.setItem('sonic_temp_project', dataJson);
if (window.SonicAPI && localStorage.getItem('sonic_token')) {
await window.SonicAPI.saveTempProject(dataJson).catch(() => {});
await window.SonicAPI.saveTempProject(dataJson, getClientId()).catch(() => {});
}
} catch (e) {
console.warn("Auto-save temp project warning:", e);
@@ -85,9 +102,11 @@
const state = lastGetProjectStateCallback ? lastGetProjectStateCallback() : null;
if (!state || (!state.tracks && !state.main_session)) return;
const dataJson = JSON.stringify(state);
if (dataJson === lastSavedJson) return; // không đổi → không ghi
lastSavedJson = dataJson;
localStorage.setItem('sonic_temp_project', dataJson);
if (window.SonicAPI && localStorage.getItem('sonic_token')) {
await window.SonicAPI.saveTempProject(dataJson).catch(() => {});
await window.SonicAPI.saveTempProject(dataJson, getClientId()).catch(() => {});
}
} catch (e) {
console.warn("Flush temp project warning:", e);
@@ -98,6 +117,7 @@
exportProjectToSFS,
importProjectFromSFSFile,
scheduleTempAutoSave,
flushTempAutoSave
flushTempAutoSave,
getClientId
};
})();