feat: VSTi live playback 100% client-side (không Carla) — autosample SF2 backend (pedalboard) → FluidSynth WASM qua masterBus → mastering → main out; SonicVstiAutosample ensure+dedup+cooldown; runtime gating __enableCarlaLivePlayback; âm bắt qua ensureMidiCapture → clientSideExport
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
// 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<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();
|
||||
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
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user