fix(lotus): restart the shared mic sampler only when the LiveKit rooms change (#45) #51

Merged
jared merged 1 commits from lotus-mic-sampler-churn into lotus 2026-10-18 07:38:41 -04:00
Owner

Fixes #45.

observeLocalMicSample$ was allConnections$.pipe(switchMap(...), share()). Each allConnections$ emission tore the tap down: it stopped the clone, closed the AudioContext, and emitted null. allConnections$ re-emits far more often than the set of LiveKit rooms changes. Each null reset the MutedSpeechGate, so speakingWhileMuted never stayed true for the host's 1.5 s hold, and the "You're muted" nudge could never fire.

Change: map to the list of LiveKit rooms, then distinctUntilChanged(sameRooms) (same rooms, same order, by identity) before the switchMap. The tap now restarts only when a room is actually added or removed. Track changes within a room are still handled by the existing reconcile().

Verified in a voice room with a continuous 220 Hz tone as the fake mic, with instrumentation in the call frame:

pinned lotus.22 this PR
AudioContexts / mic clones created in 10 s 1 / 1 0 / 0
muted → "You're muted" nudge 2 s 1 s

In this environment allConnections$ re-emits far less often than in the issue's trace (≈1/10 s vs ~1/s), so the nudge also fired before. The fix removes the restart path either way.

Unit test for sameRooms. All src/lotus tests pass (118). tsc and oxlint are clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5

Fixes #45. `observeLocalMicSample$` was `allConnections$.pipe(switchMap(...), share())`. Each `allConnections$` emission tore the tap down: it stopped the clone, closed the `AudioContext`, and **emitted `null`**. `allConnections$` re-emits far more often than the set of LiveKit rooms changes. Each `null` reset the `MutedSpeechGate`, so `speakingWhileMuted` never stayed true for the host's 1.5 s hold, and the **"You're muted" nudge could never fire**. **Change:** map to the list of LiveKit rooms, then `distinctUntilChanged(sameRooms)` (same rooms, same order, by identity) before the `switchMap`. The tap now restarts only when a room is actually added or removed. Track changes within a room are still handled by the existing `reconcile()`. **Verified** in a voice room with a continuous 220 Hz tone as the fake mic, with instrumentation in the call frame: | | pinned lotus.22 | this PR | |---|---|---| | AudioContexts / mic clones created in 10 s | 1 / 1 | **0 / 0** | | muted → "You're muted" nudge | 2 s | 1 s | In this environment `allConnections$` re-emits far less often than in the issue's trace (≈1/10 s vs ~1/s), so the nudge also fired before. The fix removes the restart path either way. Unit test for `sameRooms`. All `src/lotus` tests pass (118). `tsc` and oxlint are clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared added 1 commit 2026-10-18 01:17:07 -04:00
fix(lotus): restart the shared mic sampler only when the LiveKit rooms change (#45)
CI / Publish to Gitea npm registry (pull_request) Skipped
CI / Build embedded bundle (pull_request) Successful in 3m16s
73bf2133dc
observeLocalMicSample$ switchMapped on every allConnections$ emission,
which re-emits far more often than the set of rooms changes. Each one
stopped the clone, closed the AudioContext and emitted null, and the
null reset the muted-speech gate, so the "You're muted" nudge could
never stay true long enough to fire. Map to the room list and only
restart when it actually changes.

Fixes #45

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared merged commit 22f9083768 into lotus 2026-10-18 07:38:41 -04:00
Sign in to join this conversation.