Report "speaking while muted" in io.lotus.call_state #37

Closed
opened 2026-09-17 15:21:20 -04:00 by jared · 1 comment
Owner

Host half is LotusGuild/cinny (toast). Fork half: while the local mic is muted, LiveKit keeps the MediaStreamTrack alive (mute publishes silence; stopMicTrackOnMute is not set) but VAD/speaking is false, so the host cannot tell the user is talking into a muted mic.

Scope

  • While the local audio track is muted, run a cheap level tap (AnalyserNode RMS on the local track, ~10 Hz, same threshold family as the AFK VAD) and add speakingWhileMuted: boolean for the local participant to the existing io.lotus.call_state payload (500 ms trailing throttle already applies).
  • Respect the denoise pipeline: tap the processed track EC publishes (post-denoise), not raw capture, so keyboard noise doesn't trigger it.
  • Zero cost when unmuted (tap torn down), zero when no host is listening (behind the existing lotus-actions gate).
  • Never exposed to other participants — local-only field.
Host half is LotusGuild/cinny (toast). Fork half: while the local mic is **muted**, LiveKit keeps the MediaStreamTrack alive (mute publishes silence; `stopMicTrackOnMute` is not set) but VAD/`speaking` is false, so the host cannot tell the user is talking into a muted mic. ### Scope - While the local audio track is muted, run a cheap level tap (AnalyserNode RMS on the local track, ~10 Hz, same threshold family as the AFK VAD) and add `speakingWhileMuted: boolean` for the **local** participant to the existing `io.lotus.call_state` payload (500 ms trailing throttle already applies). - Respect the denoise pipeline: tap the *processed* track EC publishes (post-denoise), not raw capture, so keyboard noise doesn't trigger it. - Zero cost when unmuted (tap torn down), zero when no host is listening (behind the existing lotus-actions gate). - Never exposed to other participants — local-only field.
jared added this to the Features 2026-Q4 milestone 2026-09-17 15:21:20 -04:00
jared self-assigned this 2026-09-17 15:21:20 -04:00
Author
Owner

Done in 0.25.0-lotus.9 (src/lotus/lotusMutedSpeech.ts): while a muted mic publication exists, a clone of the published track (the post-processor track when the in-source denoiser is on, so keyboard noise doesn't count) feeds an AnalyserNode sampled at 10 Hz through a hysteresis gate — 300 ms of voice on, 800 ms of quiet off, threshold ≈ −36 dBFS RMS. The result rides the existing io.lotus.call_state throttle as speakingWhileMuted on the local entry only; the tap is torn down the moment the mic is unmuted/unpublished, and nothing exists at all when the host isn't listening (same lotusCallState gate). Gate unit-tested; verified end-to-end: muted with a tone mic → true within 2.5 s, silence → false, unmute → false. Host half: cinny #117.

Done in `0.25.0-lotus.9` (`src/lotus/lotusMutedSpeech.ts`): while a muted mic publication exists, a **clone** of the published track (the post-processor track when the in-source denoiser is on, so keyboard noise doesn't count) feeds an AnalyserNode sampled at 10 Hz through a hysteresis gate — 300 ms of voice on, 800 ms of quiet off, threshold ≈ −36 dBFS RMS. The result rides the existing `io.lotus.call_state` throttle as `speakingWhileMuted` on the **local** entry only; the tap is torn down the moment the mic is unmuted/unpublished, and nothing exists at all when the host isn't listening (same `lotusCallState` gate). Gate unit-tested; verified end-to-end: muted with a tone mic → `true` within 2.5 s, silence → `false`, unmute → `false`. Host half: cinny #117.
jared closed this issue 2026-09-19 23:19:58 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/element-call#37