G3: 1 clock - PDC latency that + playhead engine-derived

This commit is contained in:
2026-08-28 15:51:19 +07:00
parent c72362e088
commit 6cbdbd8e9c
8 changed files with 218 additions and 10 deletions
+6 -1
View File
@@ -56,6 +56,7 @@ class SfFxRealtimeProcessor extends AudioWorkletProcessor {
this.lastOutL = 0.0; // mẫu output cuối — nguồn crossfade + điểm bắt đầu fade-out
this.lastOutR = 0.0;
this.wrapFadeBlocks = -1; // V42: số block tail pass cũ còn lại sau clear_queue('loop') — đếm ngược để crossfade đúng seam tail→head
this.latencySamples = 0; // G3: latency chain thật (engine qua WS) — align wrap fade theo processed tail
this.WRAP_FADE = 256; // V42: crossfade 256 mẫu (~5.3ms @48k) tại seam wrap — che click jump nội dung end→start (FX path)
this.fadeFromL = 0.0; // mẫu cố định khi fade-out underrun (fade tuyến tính tới 0)
this.fadeFromR = 0.0;
@@ -63,6 +64,7 @@ class SfFxRealtimeProcessor extends AudioWorkletProcessor {
const d = e.data;
if (d && d.type === 'set_param') { this.port.postMessage(d); return; }
if (d && d.type === 'flush_done') { this.flushPending = false; return; }
if (d && d.type === 'latency') { this.latencySamples = (d.samples || 0); return; }
if (d && d.type === 'flush_for_stop') {
// V26: user stop — main thread báo xóa audio tail trong queue (audio đã
// xử lí còn phát ~0.3-0.5s sau stop = tiếng "ding"). Fade-out 21ms từ
@@ -105,7 +107,10 @@ class SfFxRealtimeProcessor extends AudioWorkletProcessor {
// V42: dem so block tail pass cu con lai trong queue -> wrap crossfade
// trigger dung luc block dau pass moi len front (seam tail->head).
if (d.mode === 'loop') {
this.wrapFadeBlocks = this.outQueue.length;
// G3: align processed-tail — tail pass cũ còn in-flight trong bridge
// (FX latency thật từ engine) chưa về queue; đếm đủ block tới seam
// (block đầu pass mới lên front) → crossfade đúng chỗ jump end→start.
this.wrapFadeBlocks = this.outQueue.length + Math.ceil((this.latencySamples || 0) / this.BLOCK);
return;
}
if (d.mode !== 'play' && this.outQueue.length < this.FILL) return;