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.
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
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 lotus2026-10-18 07:38:41 -04:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fixes #45.
observeLocalMicSample$wasallConnections$.pipe(switchMap(...), share()). EachallConnections$emission tore the tap down: it stopped the clone, closed theAudioContext, and emittednull.allConnections$re-emits far more often than the set of LiveKit rooms changes. Eachnullreset theMutedSpeechGate, sospeakingWhileMutednever 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 theswitchMap. The tap now restarts only when a room is actually added or removed. Track changes within a room are still handled by the existingreconcile().Verified in a voice room with a continuous 220 Hz tone as the fake mic, with instrumentation in the call frame:
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. Allsrc/lotustests pass (118).tscand oxlint are clean.🤖 Generated with Claude Code
https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5