// SonicForge Native Audio Client (T15) — IPC JS → native engine. // Track instrument KHÔNG còn đi masterBus WebAudio: master fader + track // gain/pan áp native qua NativeMixer (SF host / VST2 / VST3 bridge DLL). // WebAudio chỉ giữ cho UI/aux. Mọi call fire-and-forget — native engine // tự âm thanh; không block UI. window.SonicNativeAudio = window.SonicNativeAudio || {}; (function () { var BASE = window.API_BASE_URL || window.location.origin; function headers() { var h = { 'Content-Type': 'application/json' }; var token = localStorage.getItem('sonic_token') || ''; if (token) h['Authorization'] = 'Bearer ' + token; return h; } async function post(path, body) { var resp = await fetch(BASE + '/api/v1/native' + path, { method: 'POST', headers: headers(), body: JSON.stringify(body || {}) }); var data = await resp.json().catch(function () { return {}; }); if (!resp.ok) throw new Error(data.detail || 'native API lỗi ' + resp.status); return data; } async function get(path) { var resp = await fetch(BASE + '/api/v1/native' + path, { headers: headers() }); var data = await resp.json().catch(function () { return {}; }); if (!resp.ok) throw new Error(data.detail || 'native API lỗi ' + resp.status); return data; } var _masterTimer = null; window.SonicNativeAudio = { status: function () { return get('/status'); }, // Master fader: debounce 40ms — fader kéo liên tục, chỉ gửi giá trị cuối. setMasterGain: function (gainDb) { if (_masterTimer) clearTimeout(_masterTimer); _masterTimer = setTimeout(function () { post('/set_master_gain', { gain_db: gainDb }).catch(function (e) { console.warn('[NativeAudio] set_master_gain:', e.message); }); }, 40); }, setTrackGainPan: function (trackId, gainDb, pan) { return post('/track_gain_pan', { track_id: trackId, gain_db: gainDb, pan: pan }).catch(function (e) { console.warn('[NativeAudio] track_gain_pan:', e.message); }); }, ensureSf: function (trackId, sfId, bank, program) { return post('/sf/ensure', { track_id: trackId, sf_id: sfId || null, bank: bank || 0, program: program || 0, live: true }).catch(function (e) { console.warn('[NativeAudio] sf/ensure:', e.message); }); }, ensureVst2: function (trackId, pluginId, pluginPath) { return post('/vst2/ensure', { track_id: trackId, plugin_id: pluginId || null, plugin_path: pluginPath || null, live: true }).catch(function (e) { console.warn('[NativeAudio] vst2/ensure:', e.message); }); }, ensureVst3: function (trackId, pluginId, pluginPath) { return post('/vst3/ensure', { track_id: trackId, plugin_id: pluginId || null, plugin_path: pluginPath || null, live: true }).catch(function (e) { console.warn('[NativeAudio] vst3/ensure:', e.message); }); }, noteOn: function (kind, trackId, channel, pitch, velocity) { var path = kind === 'vst2' ? '/vst2/note_on' : (kind === 'vst3' ? '/vst3/note_on' : '/sf/note_on'); // Rethrow sau warn: TrackInstrument can fallback autosample/WASM // khi native khong san sang (DLL/plugin loi). return post(path, { track_id: trackId, channel: channel || 0, pitch: pitch, velocity: velocity != null ? velocity : 100 }).catch(function (e) { console.warn('[NativeAudio] note_on:', e.message); throw e; }); }, noteOff: function (kind, trackId, channel, pitch) { var path = kind === 'vst2' ? '/vst2/note_off' : (kind === 'vst3' ? '/vst3/note_off' : '/sf/note_off'); return post(path, { track_id: trackId, channel: channel || 0, pitch: pitch }).catch(function (e) { console.warn('[NativeAudio] note_off:', e.message); throw e; }); }, sfAudioStop: function (trackId) { return post('/sf/audio_stop', { track_id: trackId, pitch: 0 }).catch(function () {}); } }; })();