Gap 8: PDC realtime — WS latency push + dry-path delay compensation
Backend: /ws/fx-realtime pump drain get_latencies (SHM ring, bridge da bao REPORT_LATENCY) -> push {cmd:'latency',total,latencies} khi tong doi. Frontend: pdcDelay (DelayNode) giua dryOutput->output bu delay master chain (round-trip 2 block + latency VST3), fxRtApplyPdc cap nhat tu WS frame, reset khi chain off. Test test_63 fake session 1024 -> frame total 1024; full 175 passed.
This commit is contained in:
+38
-2
@@ -1061,7 +1061,13 @@ function initMasterBus(ctx) {
|
||||
const dryInput = ctx.createGain();
|
||||
const dryOutput = ctx.createGain();
|
||||
dryInput.connect(dryOutput);
|
||||
dryOutput.connect(output);
|
||||
// Gap 8 (PDC realtime): dry path bu delay master chain realtime — worklet
|
||||
// round-trip ~2 block + latency VST3 (bridge bao qua WS {cmd:'latency'}).
|
||||
// Bypassed tracks align voi mastered (fxRtApplyPdc cap nhat delayTime).
|
||||
const pdcDelay = ctx.createDelay(5.0);
|
||||
pdcDelay.delayTime.value = 0;
|
||||
dryOutput.connect(pdcDelay);
|
||||
pdcDelay.connect(output);
|
||||
|
||||
const analyser = ctx.createAnalyser();
|
||||
analyser.fftSize = 256;
|
||||
@@ -1101,6 +1107,7 @@ function initMasterBus(ctx) {
|
||||
masteringActive: false,
|
||||
dryInput,
|
||||
dryOutput,
|
||||
pdcDelay,
|
||||
splitter,
|
||||
volSplit,
|
||||
volLGain,
|
||||
@@ -1593,7 +1600,7 @@ function encodeFloatWavBlob(pcm, sampleRate, channels) {
|
||||
// worklet → analyser (sau master fader, trước metering/destination — khớp thứ
|
||||
// tự offline export). Track: mỗi track 1 session/worklet splice vào CẢ 2 đường
|
||||
// chain (audio-clips + MIDI/SF) — mọi luồng track qua bridge trước main out.
|
||||
const fxRt = { active: false, sessionId: null, ws: null, node: null, inQueue: [], pumpTimer: null, chainKey: null };
|
||||
const fxRt = { active: false, sessionId: null, ws: null, node: null, inQueue: [], pumpTimer: null, chainKey: null, latencyTotal: 0 };
|
||||
// Signature của chain VST realtime — phát hiện chain ĐỔI (insert/remove/bypass/
|
||||
// reorder VST) → restart session với chain mới (fxRtStart early-return khi
|
||||
// active, nếu không so chainKey thì VST mới insert không bao giờ được áp).
|
||||
@@ -1684,6 +1691,18 @@ async function fxRtStart() {
|
||||
ws.binaryType = 'arraybuffer';
|
||||
ws.onopen = () => { fxRt.active = true; try { fxRtPushAllParams(); } catch (e) {} };
|
||||
ws.onmessage = (ev) => {
|
||||
// Gap 8 (PDC realtime): text frame {cmd:'latency', total} tu server ->
|
||||
// cap nhat delay dry-bypass path (bu latency chain VST3 realtime).
|
||||
if (typeof ev.data === 'string') {
|
||||
try {
|
||||
const d = JSON.parse(ev.data);
|
||||
if (d && d.cmd === 'latency' && typeof d.total === 'number') {
|
||||
fxRt.latencyTotal = d.total;
|
||||
fxRtApplyPdc();
|
||||
}
|
||||
} catch (e) {}
|
||||
return;
|
||||
}
|
||||
if (!(ev.data instanceof ArrayBuffer) || !fxRt.node) return;
|
||||
const f = new Float32Array(ev.data);
|
||||
const half = f.length / 2;
|
||||
@@ -1749,11 +1768,28 @@ function fxRtStop(silent) {
|
||||
fxRtGuiPushShm();
|
||||
fxRt.active = false;
|
||||
fxRt.chainKey = null;
|
||||
fxRt.latencyTotal = 0;
|
||||
fxRtApplyPdc();
|
||||
if (sid) {
|
||||
try { window.SonicAPI.fxRealtimeStop({ session_id: sid }).catch(() => {}); } catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Gap 8: PDC realtime — bù delay dry-bypass path (master chain) ──────────
|
||||
// Master chain (worklet → bridge VST3) trễ ~2 block round-trip + latency
|
||||
// plugin. Track bypass mastering đi dry path (dryInput→dryOutput→output) KHÔNG
|
||||
// qua worklet → lệch pha với mastered. fxRtApplyPdc trễ dry path đúng lượng
|
||||
// để align. FX_RT_ROUNDTRIP_SAMPLES là ước lượng (~2 block 256 @44.1k) —
|
||||
// ponytail: đo RTT thật (tag seq trên block) khi cần sample-accurate.
|
||||
const FX_RT_ROUNDTRIP_SAMPLES = 512;
|
||||
function fxRtApplyPdc() {
|
||||
const ctx = getAudioContext();
|
||||
const bus = masterBus;
|
||||
if (!ctx || !bus || !bus.pdcDelay) return;
|
||||
const l = (fxRt && fxRt.active) ? (FX_RT_ROUNDTRIP_SAMPLES + (fxRt.latencyTotal || 0)) : 0;
|
||||
try { bus.pdcDelay.delayTime.setTargetAtTime(l / (ctx.sampleRate || 44100), ctx.currentTime, 0.05); } catch (e) {}
|
||||
}
|
||||
|
||||
// ── Realtime VST FX per-track (SHM bridge) — mọi luồng track qua bridge ─────
|
||||
// Mỗi track: session riêng (1 WS/SHM) cho TỪNG đường chain có âm — audio-clips
|
||||
// (path 'audio', splice fxEntry→mods→worklet→fxLegacyIn) và MIDI/SF (path 'sf',
|
||||
|
||||
Reference in New Issue
Block a user