fix(vsti): crackle fix P1-P3 — SHM 8-slot ring + drain-delta pump + AudioWorklet sink

- SharedMemoryIPC.h/main.cpp: render into ring slot (idx & ringMask), 8 slots
- shm.rs: ring fields + read_ring_audio; lib.rs pump drains FULL delta, not just latest
- bridgeAudioNode.js: AudioWorklet primary (bridge-audio-sink), ScriptProcessor fallback
- bridgeAudioWorklet.js: ring 64 blocks, underrun fade, overrun drop + crossfade
- layout tests: sizeof 27560 (8-align via u64)
This commit is contained in:
2026-08-16 20:43:50 +07:00
parent ed26e1706d
commit cead8c8c71
8 changed files with 235 additions and 29 deletions
+22 -11
View File
@@ -451,23 +451,34 @@ pub fn run() {
restart_attempts = 0;
}
// Audio emission = writeIndex advancing (audio loop alive).
let audio_advanced = guard.as_ref().map(|shm| {
let idx = shm.write_index();
// VSTi crackle fix: drain the FULL delta from the multi-slot
// ring (8 slots) — a delayed pump no longer drops whole
// blocks that a single-slot SHM would have overwritten.
let mut audio_advanced = None;
if let Some(s) = guard.as_ref() {
let idx = s.write_index();
if idx != last_index {
let delta = idx.wrapping_sub(last_index);
// Only the last 8 blocks are still intact in the ring;
// if the pump stalled longer, older blocks are lost.
let start = if delta as usize > 8 { idx - 8 } else { last_index };
for k in start..idx {
let (l, r) = s.read_ring_audio(k);
let _ = pump_handle.emit(
"bridge-audio",
AudioFrame { l: l.to_vec(), r: r.to_vec() },
);
}
if delta as usize > 8 {
eprintln!("[tauri] audio pump lost {} blocks (stalled > ring)", delta - 8);
}
last_index = idx;
true
} else {
false
audio_advanced = Some(true);
}
});
}
if audio_advanced == Some(true) {
last_audio_change = std::time::Instant::now();
stall_logged = false;
let (l, r) = guard.as_ref().map(|s| s.read_audio()).unwrap_or(([0f32; shm::AUDIO_BLOCK_SIZE], [0f32; shm::AUDIO_BLOCK_SIZE]));
let _ = pump_handle.emit(
"bridge-audio",
AudioFrame { l: l.to_vec(), r: r.to_vec() },
);
}
// G3.1 FIX: nhả guard TRƯỚC stall log / watchdog kill+spawn.
// kill_bridge -> flush_bridge_state khóa chính ShmState mutex