// 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 {}; } } // 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 — nhiều note cùng lúc chỉ 1 request // Đảm bảo SF2 autosampled tồn tại. Trả Promise; 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(); if (fails[k] && (Date.now() - fails[k]) < FAIL_COOLDOWN_MS) { return Promise.resolve(null); } if (!window.SonicAPI || !window.SonicAPI.autosampleVsti) { 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 { resolve(null); } }).catch(function () { delete _inFlight[k]; var fails2 = readFails(); fails2[k] = Date.now(); try { localStorage.setItem(LS_FAIL_KEY, JSON.stringify(fails2)); } catch (e) {} resolve(null); }); }); return _inFlight[k]; } // 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, clearCache: clearCache }; })();