Summary
- Dead / orphaned state in LocalVoiceStateStore
LocalVoiceStateStore has its own noiseSuppressionEnabled field (line 35), a toggleNoiseSuppression() method, and it's even persisted to localStorage. But it is never read by any code in the actual audio pipeline. The audio path exclusively reads from VoiceSettingsStore.getNoiseSuppression(). This is genuinely confusing — there are two independent sources of truth for the same concept, only one of which does anything. The LocalVoiceStateStore version should either be removed, or the architecture needs to be explicit about which one is authoritative.
- The re-application logic lives in a UI component, not the service layer
This is the most architecturally problematic issue. When noiseSuppression changes during an active call, the actual microphone restart is triggered by a useEffect in VoiceControlBar.tsx — a React component. This works as long as VoiceControlBar is mounted, but it inverts the correct dependency direction. Audio processing concerns belong in VoiceMediaManager or MediaEngineFacade, not in a UI component. If VoiceControlBar were ever conditionally rendered or replaced, changing noise suppression would silently fail. The fix is a MobX reaction() in MediaEngineFacade (or VoiceMediaManager) that watches VoiceSettingsStore.noiseSuppression and calls the restart directly.
- Weak error handling when re-applying settings mid-call
The VoiceControlBar useEffect that restarts the mic on settings change logs failures with console.error(). Contrast this with VoiceMediaManager.enableMicrophone(), which has proper permission-denied modals and state cleanup. If the settings re-application fails — e.g., the OS revokes mic permission mid-call — the user gets no feedback at all.
- The MicTest stream doesn't reflect settings changes while running
useMicTest captures all settings at the time the test starts (start() is called). If the user toggles noise suppression while the mic test is active, the test stream doesn't update — so the test is no longer representative of actual call quality. This is a minor UX issue but a real one if a user is testing to see what noise suppression sounds like.
- The core suppression is entirely browser-native, which is weak
This is more of a capability gap than a bug, but worth naming directly. The noiseSuppression: true WebRTC constraint uses whatever the browser's underlying audio processing library implements (typically Chromium's WebRTC Audio Processing). It's a basic signal processing approach — not ML-based. For keyboard typing, air conditioning, and background speech, it's noticeably weaker than what products like Discord, Krisp, or even browser-based wasm builds of RNNoise or DeepFilterNet offer. An AudioWorkletProcessor implementation of RNNoise could be inserted into the chain without changing the LiveKit architecture significantly, and it's MIT-licensed.
Steps to reproduce
i have modified 4 files attached to address these errors. please review and let me know if the proposed correction is acceptable.
noise_suppression_correction.zip
4 comments
Comment by Rex
Comment by @LNAhri
Comment by @nagaconnie
Comment by @mekandaapp