Files
SonicForgeStudio/app/static/js/services/vstiAutosample.js
T
admin fa2f96dbe3 T3: Autosample UX - fail reason + toast + pre-warm feedback
vstiAutosample: fail map thanh object {ts, reason} (cooldown check
object-aware), recordFail o API-missing/response loi/catch, export
failReasonFor. soundfontPlayer: toast ly do khi ensure null (throttle
10s) roi fallback oscillator. app.jsx: pre-warm VSTi hien thi toast khi
autosample fail.
2026-08-11 15:36:10 +07:00

123 lines
5.0 KiB
JavaScript

// SonicForge VSTi Autosample Service
// Live playback 100% client-side (không Carla): VSTi note → SF2 autosampled
// (backend pedalboard, render 1 lần / plugin+preset) → FluidSynth WASM qua
// masterBus → mastering → main out. ensure() dedup in-flight request + cooldown
// lỗi (404/501/no pedalboard) để không spam server.
window.SonicVstiAutosample = window.SonicVstiAutosample || {};
(function () {
var LS_MAP_KEY = 'sf_vsti_autosample_map';
var LS_FAIL_KEY = 'sf_vsti_autosample_fail';
var FAIL_COOLDOWN_MS = 60000;
function readMap() {
try { return JSON.parse(localStorage.getItem(LS_MAP_KEY) || '{}') || {}; } catch (e) { return {}; }
}
function writeMap(map) {
try { localStorage.setItem(LS_MAP_KEY, JSON.stringify(map)); } catch (e) {}
}
function readFails() {
try { return JSON.parse(localStorage.getItem(LS_FAIL_KEY) || '{}') || {}; } catch (e) { return {}; }
}
function recordFail(k, reason) {
try {
var fails2 = readFails();
fails2[k] = { ts: Date.now(), reason: reason || 'Autosample that bai' };
localStorage.setItem(LS_FAIL_KEY, JSON.stringify(fails2));
} catch (e) {}
}
// Key = plugin + preset → autosample lại khi đổi preset (âm preset mới).
function keyFor(synthEngine) {
if (!synthEngine) return '';
return String(synthEngine.plugin_id || '') + '|' + String(synthEngine.preset_id || synthEngine.presetId || '');
}
// SF id đã autosample cho engine (cache localStorage) — đồng bộ, không request.
function sfIdFor(synthEngine) {
var k = keyFor(synthEngine);
if (!k) return null;
var map = readMap();
return (map[k] && map[k].sf_id) ? map[k].sf_id : null;
}
function entryFor(synthEngine) {
var k = keyFor(synthEngine);
if (!k) return null;
return readMap()[k] || null;
}
var _inFlight = {}; // key → Promise<sf_id|null> — nhiều note cùng lúc chỉ 1 request
// Đảm bảo SF2 autosampled tồn tại. Trả Promise<sf_id|null>; null =
// không autosample được → caller fallback (oscillator / không phát).
function ensure(synthEngine) {
var k = keyFor(synthEngine);
if (!k) return Promise.resolve(null);
var cached = sfIdFor(synthEngine);
if (cached) return Promise.resolve(cached);
if (_inFlight[k]) return _inFlight[k];
var fails = readFails();
var _f = fails[k];
var _lastTs = (_f && typeof _f === 'object') ? _f.ts : _f;
if (_lastTs && (Date.now() - _lastTs) < FAIL_COOLDOWN_MS) {
return Promise.resolve(null);
}
if (!window.SonicAPI || !window.SonicAPI.autosampleVsti) {
recordFail(k, 'API autosample khong kha dung');
return Promise.resolve(null);
}
_inFlight[k] = new Promise(function (resolve) {
var payload = {
instrument_id: synthEngine.plugin_id,
preset_id: synthEngine.preset_id || synthEngine.presetId || null,
preset_path: synthEngine.preset_path || null,
preset_data: synthEngine.preset_data || null
};
window.SonicAPI.autosampleVsti(payload).then(function (res) {
delete _inFlight[k];
if (res && res.success && res.sf_id) {
var map = readMap();
map[k] = { sf_id: res.sf_id, size_bytes: res.size_bytes || 0, note_count: res.note_count || 0, plugin_id: synthEngine.plugin_id, preset_id: synthEngine.preset_id || synthEngine.presetId || '', ts: Date.now() };
writeMap(map);
resolve(res.sf_id);
} else {
recordFail(k, (res && res.error) ? String(res.error) : 'Server khong tra sf_id');
resolve(null);
}
}).catch(function (err) {
delete _inFlight[k];
recordFail(k, (err && err.message) ? String(err.message) : 'Loi mang / server autosample');
resolve(null);
});
});
return _inFlight[k];
}
// Lý do autosample thất bại gần nhất (fail map) — null nếu chưa từng fail
// (hoặc entry cũ định dạng number). Dùng cho UI toast khi ensure() trả null.
function failReasonFor(synthEngine) {
var k = keyFor(synthEngine);
if (!k) return null;
var fails = readFails();
var f = fails[k];
if (!f || typeof f !== 'object') return null;
return f.reason || null;
}
// Xoá cache + cooldown — cho phép re-sample (đổi preset/plugin hoặc debug).
function clearCache() {
try { localStorage.removeItem(LS_MAP_KEY); } catch (e) {}
try { localStorage.removeItem(LS_FAIL_KEY); } catch (e) {}
}
window.SonicVstiAutosample = {
keyFor: keyFor,
sfIdFor: sfIdFor,
entryFor: entryFor,
ensure: ensure,
failReasonFor: failReasonFor,
clearCache: clearCache
};
})();