V42+V42A: fix seam click khi loop master FX — worklet wrap crossfade (tail->head) + native WebAudio loop cho bypass (het click beat 0); dip bypass 5ms; PDC playhead freeze/snap
This commit is contained in:
@@ -31,7 +31,7 @@
|
||||
console.warn('[BridgeAudioNode] AudioWorklet unavailable — ScriptProcessor fallback');
|
||||
return false;
|
||||
}
|
||||
var url = '/static/js/services/bridgeAudioWorklet.js?v=202608152200';
|
||||
var url = '/static/js/services/bridgeAudioWorklet.js?v=202608272301';
|
||||
_ctx.audioWorklet.addModule(url).then(function () {
|
||||
if (!_initialized) return; // disconnect() happened while loading
|
||||
if (_useWorklet) return; // already set up (init retry)
|
||||
@@ -49,12 +49,15 @@
|
||||
_workletNode.port.postMessage({ type: 'audio', l: b.l, r: b.r });
|
||||
}
|
||||
console.log('[BridgeAudioNode] AudioWorklet active');
|
||||
try { if (window.__dbgLog) window.__dbgLog('worklet', 'active pre=' + _preBuffer.length); } catch (e) {}
|
||||
} catch (err) {
|
||||
console.warn('[BridgeAudioNode] AudioWorkletNode failed — ScriptProcessor fallback', err);
|
||||
_useWorklet = false;
|
||||
try { if (window.__dbgLog) window.__dbgLog('worklet', 'NODE_FAIL'); } catch (e) {}
|
||||
}
|
||||
}).catch(function (err) {
|
||||
console.warn('[BridgeAudioNode] addModule failed — ScriptProcessor fallback', err);
|
||||
try { if (window.__dbgLog) window.__dbgLog('worklet', 'MODULE_FAIL'); } catch (e) {}
|
||||
});
|
||||
return true; // pending — worklet may become active later
|
||||
}
|
||||
@@ -157,6 +160,16 @@
|
||||
_dest = dest;
|
||||
},
|
||||
disconnect: disconnect,
|
||||
isReady: function () { return _initialized; }
|
||||
isReady: function () { return _initialized; },
|
||||
getDiag: function () {
|
||||
return {
|
||||
initialized: _initialized,
|
||||
useWorklet: _useWorklet,
|
||||
hasWorkletNode: !!_workletNode,
|
||||
preBufferLen: _preBuffer.length,
|
||||
chunksLen: _chunks.length,
|
||||
gain: _gainNode ? _gainNode.gain.value : -1
|
||||
};
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// app/static/js/services/bridgeAudioWorklet.js
|
||||
// VSTi crackle fix: audio sink chay tren audio thread (khong jank main thread).
|
||||
// - Ring block 256 mau, toi da 64 block (~371ms @48k) — hap thu jitter pump.
|
||||
// - Ring block 256 mau, toi da 8 block (~42ms @48k) — hap thu jitter pump
|
||||
// nhung gioi han backlog: 64 block (~371ms) tao tre ~0.3-0.4s khi play/
|
||||
// keyboard preview (audio moi phai cho ring cu chay het).
|
||||
// - Underrun: fade ve 0 thay vi click (hard silence).
|
||||
// - Resume sau silence: fade vao tu 0.
|
||||
// - Overrun: drop block cu nhat + crossfade 64 mau tai diem drop (het click).
|
||||
@@ -8,7 +10,7 @@ class BridgeAudioSinkProcessor extends AudioWorkletProcessor {
|
||||
constructor() {
|
||||
super();
|
||||
this._ring = [];
|
||||
this._MAX = 64;
|
||||
this._MAX = 8;
|
||||
this._gain = 1.0;
|
||||
this._lastOutL = 0.0;
|
||||
this._lastOutR = 0.0;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
(function () {
|
||||
var MELODIC_CHANNELS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14]; // skip 9 (percussion); 15 reserved for Media Explorer preview
|
||||
|
||||
var _lastTimelineDbgTs = 0;
|
||||
|
||||
var router = {
|
||||
bridgeConnected: false,
|
||||
onFallback: null, // function(cmd, channel, pitch, velocity) — set by app.jsx
|
||||
@@ -51,11 +53,29 @@
|
||||
* data2/data3 (A12): CC value / program / PB LSB|MSB — passed to bridge only.
|
||||
*/
|
||||
pushEvent: function (opts) {
|
||||
// V28: midi bất kỳ (keyboard/pad/timeline) → restore bridge gain đang
|
||||
// giữ ~0.0001 sau stop (ding fix — V27 restore 800ms không đủ tail dài).
|
||||
try { if (window.__restorePendingFadeSession) window.__restorePendingFadeSession(); } catch (e) {}
|
||||
// V31: live note (ARM keyboard preview) — clear master FX outQueue backlog
|
||||
// (idle block ~341ms) truoc khi note moi render; timeline note (live=false)
|
||||
// KHONG clear — tranh drop audio dang phat.
|
||||
if (opts.live && opts.cmd === 'NOTE_ON') { try { if (window.__clearMasterFxQueue) window.__clearMasterFxQueue(); } catch (e) {} }
|
||||
var cmd = opts.cmd, ch = opts.channel, pitch = opts.pitch;
|
||||
var vel = (opts.velocity === undefined ? 1.0 : opts.velocity);
|
||||
var sampleOffset = opts.sampleOffset || 0;
|
||||
if (typeof ch === 'string') ch = this.allocateChannel(ch, opts.percussion);
|
||||
if (ch === undefined || ch === null) ch = 0;
|
||||
try {
|
||||
// V36: midi_push log = 1 Tauri invoke sync (dbg_log -> disk I/O tren main
|
||||
// thread Rust) moi event. Timeline phat ~380 event/s -> main thread nghet
|
||||
// -> stall 110-170ms -> crackle. Chi log live (ARM preview), timeline
|
||||
// gioi han 1/200ms.
|
||||
var _nowMs = (window.performance && performance.now) ? performance.now() : Date.now();
|
||||
if (window.__dbgLog && (opts.live || _nowMs - _lastTimelineDbgTs > 200)) {
|
||||
if (!opts.live) _lastTimelineDbgTs = _nowMs;
|
||||
window.__dbgLog('midi_push', 'cmd=' + cmd + ' ch=' + ch + ' live=' + (opts.live ? 1 : 0) + ' vel=' + Math.round((vel || 0) * 100) + (window.__dbgBridgeGain ? ' ' + window.__dbgBridgeGain() : '') + ' q=' + (window.__fxRtQueueLen ? window.__fxRtQueueLen() : 'n/a'));
|
||||
}
|
||||
} catch (e) {}
|
||||
if (this.isBridgeActive()) {
|
||||
window.NativeBridgeService.dispatchMidiEvent(cmd, ch, pitch, vel, sampleOffset, opts.data2, opts.data3, opts.live);
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user