Files
cinny/src/app/features/settings
jaredandClaude Opus 4.8 c9d9d91415 fix(denoise-tester): dispose model nodes on playback stop; guard async lifecycle
Settings → Calls A/B denoise tester leaked audio resources:

- play() built a denoise model node (DeepFilterNet/DTLN worker/WASM) + optional
  gate but stopPlayback only closed the AudioContext, never disposing them —
  each A/B playback-through-a-model leaked a worker. stopPlayback now mirrors
  stopLive (gate.disconnect → model.dispose → node.disconnect).

- A generation token (playGenRef, bumped by stopPlayback) makes play() discard
  what it built if superseded during the async WASM/worklet load — closing the
  same leak in the rapid-Play-click race, the Stop-during-load case, and the
  unmount-during-load case, and stopping a superseded rejection from tearing
  down the winning playback.

- A mountedRef guards the getUserMedia paths (startLive/startRecord) so closing
  Settings during the mic permission prompt doesn't create untracked
  resources / setState-after-unmount; its effect sets true on mount (not only
  false on cleanup) so it survives a StrictMode/Activity same-fiber remount.

Bug-hunt findings from LOTUS_TODO. Three review passes: the first two confirmed
the base fixes and surfaced the concurrent-load leak + StrictMode fragility; a
third traced all six play() interleavings of the generation token. Gate-green
(tsc, eslint, prettier, 914 tests, build).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 16:42:32 -04:00
..