Skip to content

fix(calls): wire voice processing presets to the published mic track#2409

Open
rozagerardo wants to merge 4 commits into
mainfrom
rozagerardo/feat/GH-2383-sound-isolation
Open

fix(calls): wire voice processing presets to the published mic track#2409
rozagerardo wants to merge 4 commits into
mainfrom
rozagerardo/feat/GH-2383-sound-isolation

Conversation

@rozagerardo

@rozagerardo rozagerardo commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • voiceProcessingPreset (standard / voice_isolation / music) was a fully working UI (radio menu, checkmarks, persisted to localStorage) but was never actually applied to the published LiveKit audio track — constraintsForVoicePreset() had no caller in the repo besides its own unit test.
  • Wires the preset to the real mic track both at join (setMicrophoneEnabled(true, constraintsForVoicePreset(...))) and on a mid-call switch (applyConstraints() on the live published track), so a change takes effect without leaving/rejoining or muting/unmuting.
  • Fixes the constraint table: standard now {autoGainControl: true, echoCancellation: true, noiseSuppression: false} (previously noiseSuppression: true, which made it differ from voice_isolation on only one axis).
  • Removes the isScreensharing AGC override, which forced autoGainControl: true regardless of preset — this made the existing screenshare-auto-switch-to-voice_isolation behavior (WCUX-SHARE-VOICE-1) a complete no-op. It now has a real, audible effect for the first time.
  • A persisted music preset resets to standard when read back at join — music is situational to one call's content and shouldn't silently carry into an unrelated future call.
  • Adds a one-time getCapabilities() check on the mic track at join; if a device/browser can't actually adjust noiseSuppression or autoGainControl, the corresponding preset button (voice_isolation / music) is disabled with an inline note instead of silently no-opping.

GH-2383 item (c). Items (b), (d), (e) are separate, not touched here.

Test plan

  • vitest run screenshare-voice-boost — updated constraint-table + capability assertions pass
  • tsc --noEmit on packages/core and packages/epics — no new errors (confirmed against baseline via git stash diff)
  • Live 2-person call test on preview deploy: switch each preset (join + mid-call) and confirm via track.getSettings() in devtools that the requested constraints actually landed
  • Confirm starting a screenshare from standard disables AGC without introducing distracting volume/level issues (previously masked by the removed no-op override)
  • Confirm a device/browser lacking support for a constraint shows the disabled preset + inline note, not a false "success" indication

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Voice-processing presets now auto-apply on mic enable and remain in sync when joining, rejoining, or toggling mute during calls.
    • In-call audio settings now detect device limitations, disable unsupported presets, and show targeted explanations for unsupported noise suppression and automatic gain control.
  • Bug Fixes

    • Voice-processing preset selection is now normalized more reliably (including persisted “music” reverting to the standard preset) to avoid stale behavior.
  • Tests

    • Updated voice-preset constraint assertions for improved coverage.

`voiceProcessingPreset` (standard/voice_isolation/music) was fully built in the
UI and persisted to localStorage but never applied to the actual LiveKit audio
track - constraintsForVoicePreset() had no caller besides its own test. Wires it
at join and on mid-call switch (via applyConstraints on the live track), fixes
the standard/voice_isolation constraint table so they differ on both axes,
removes the isScreensharing AGC override that made the existing
screenshare-auto-isolation behavior a no-op, resets a persisted `music` choice
to `standard` on join, and greys out any preset menu option whose constraint
isn't supported on the current device/browser.

GH-2383 item (c).
@github-actions

Copy link
Copy Markdown

🔗 Custom preview URL: https://pr-2409.preview-app.hypha.earth

…ge load

Live-testing on the PR #2409 preview build found that switching to `music`,
leaving, and rejoining without a page reload still showed `music` as active —
the mount-time readVoiceProcessingPreset() effect that resolves a stale
`music` value only runs once per page load, so it never caught an in-session
rejoin. Resolve music -> standard at the actual join point instead, in
addition to the existing mount-time check.
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Voice-processing presets now configure LiveKit microphone tracks, detect unsupported browser capabilities, normalize persisted presets, and propagate capability state to disable incompatible in-call options with localized explanations.

Changes

Voice processing capability handling

Layer / File(s) Summary
Preset constraint definitions and tests
packages/core/src/matrix/client/hooks/voice-processing-constraints.ts, packages/core/src/matrix/client/hooks/__tests__/screenshare-voice-boost.test.ts
Preset mappings now return direct constraint objects, with updated standard-preset noise suppression behavior and exact test assertions.
Microphone constraint application
packages/core/src/matrix/client/hooks/use-space-group-call.ts
Call joining applies preset constraints, normalizes persisted music values, records unsupported capabilities, updates active tracks when presets change, and exposes the capability state.
Unsupported preset UI and propagation
packages/epics/src/common/global-call-dock-overlay.tsx, packages/epics/src/common/human-chat-panel/*, packages/epics/src/common/human-right-panel.tsx, packages/i18n/src/messages/*.json
Unsupported capability state is passed to call controls, where incompatible presets are disabled and localized hints are displayed.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant useSpaceGroupCall
  participant LiveKitMicrophoneTrack
  participant HumanChatPanelInCallControls
  Caller->>useSpaceGroupCall: Join call with selected voice preset
  useSpaceGroupCall->>LiveKitMicrophoneTrack: Publish microphone with preset constraints
  LiveKitMicrophoneTrack-->>useSpaceGroupCall: Report constraint capabilities
  useSpaceGroupCall->>HumanChatPanelInCallControls: Pass unsupported constraint state
  HumanChatPanelInCallControls-->>Caller: Disable unsupported presets and show hints
Loading

Possibly related PRs

  • hypha-dao/hypha-web#2310: Both update microphone constraint application and existing microphone-track handling in use-space-group-call.ts.

Suggested reviewers: cursoragent

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title uses conventional commits form and accurately summarizes the call voice-processing preset wiring changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rozagerardo/feat/GH-2383-sound-isolation

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/core/src/matrix/client/hooks/use-space-group-call.ts (2)

2070-2079: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Pass the active preset constraints when unmuting the microphone.

When the microphone is unmuted (!muted === true), LiveKit may occasionally need to acquire a new media track from the browser (e.g., if the previous track was released by the OS or stopped). Because setMicrophoneEnabled is called here without options, a newly acquired track would revert to the original connection constraints instead of the currently active voice processing preset.

Pass the current preset constraints to ensure they are preserved across track restarts.

💡 Proposed fix
   const setMicrophoneMuted = useCallback(
     async (muted: boolean) => {
       const lkRoom = liveKitRoomRef.current;
       if (!lkRoom) return;
-      await lkRoom.localParticipant.setMicrophoneEnabled(!muted);
+      await lkRoom.localParticipant.setMicrophoneEnabled(
+        !muted,
+        constraintsForVoicePreset(voiceProcessingPresetRef.current)
+      );
       syncLocalMuteState(lkRoom);
       scheduleFeedBatched();
     },
     [scheduleFeedBatched, syncLocalMuteState],
   );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/matrix/client/hooks/use-space-group-call.ts` around lines
2070 - 2079, Update setMicrophoneMuted to pass the currently active
voice-processing preset constraints as options when calling
localParticipant.setMicrophoneEnabled during unmute, while preserving the
existing behavior for mute and subsequent state synchronization.

1336-1339: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Apply constraints to the microphone track when the preset is changed by the screenshare flow.

Both sites update the internal preset state during the screenshare lifecycle, but miss applying the newly mapped constraints to the active hardware track. Without this step, the microphone continues using the old constraints, defeating the purpose of the automatic voice boost.

  • packages/core/src/matrix/client/hooks/use-space-group-call.ts#L1336-L1339: call applyConstraints on the mic track with plan.effectivePreset to enforce the boost.
  • packages/core/src/matrix/client/hooks/use-space-group-call.ts#L1413-L1417: call applyConstraints on the mic track with the restore preset to revert the boost.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/matrix/client/hooks/use-space-group-call.ts` around lines
1336 - 1339, Update both screenshare preset transition sites in
use-space-group-call.ts: at lines 1336-1339, apply constraints to the active
microphone track using plan.effectivePreset after updating the preset state; at
lines 1413-1417, apply constraints using the restore preset when reverting. Use
the existing microphone track and applyConstraints flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/src/matrix/client/hooks/use-space-group-call.ts`:
- Around line 2287-2296: Update the preset handling near the micTrack constraint
application to also set the LiveKit room’s audioCaptureDefaults using
constraintsForVoicePreset(preset), so newly created microphone tracks preserve
the selected preset after device changes; keep the existing active-track
applyConstraints behavior unchanged.

---

Outside diff comments:
In `@packages/core/src/matrix/client/hooks/use-space-group-call.ts`:
- Around line 2070-2079: Update setMicrophoneMuted to pass the currently active
voice-processing preset constraints as options when calling
localParticipant.setMicrophoneEnabled during unmute, while preserving the
existing behavior for mute and subsequent state synchronization.
- Around line 1336-1339: Update both screenshare preset transition sites in
use-space-group-call.ts: at lines 1336-1339, apply constraints to the active
microphone track using plan.effectivePreset after updating the preset state; at
lines 1413-1417, apply constraints using the restore preset when reverting. Use
the existing microphone track and applyConstraints flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: fe12498d-e7c4-440e-acce-315607fca8a5

📥 Commits

Reviewing files that changed from the base of the PR and between dda9eb1 and c3ad232.

📒 Files selected for processing (13)
  • packages/core/src/matrix/client/hooks/__tests__/screenshare-voice-boost.test.ts
  • packages/core/src/matrix/client/hooks/use-space-group-call.ts
  • packages/core/src/matrix/client/hooks/voice-processing-constraints.ts
  • packages/epics/src/common/global-call-dock-overlay.tsx
  • packages/epics/src/common/human-chat-panel/human-chat-panel-call-banner.tsx
  • packages/epics/src/common/human-chat-panel/human-chat-panel-in-call-controls.tsx
  • packages/epics/src/common/human-right-panel.tsx
  • packages/i18n/src/messages/de.json
  • packages/i18n/src/messages/en.json
  • packages/i18n/src/messages/es.json
  • packages/i18n/src/messages/fr.json
  • packages/i18n/src/messages/mk.json
  • packages/i18n/src/messages/pt.json

Comment thread packages/core/src/matrix/client/hooks/use-space-group-call.ts
Addresses CodeRabbit review on PR #2409:

- The screenshare auto-switch (WCUX-SHARE-VOICE-1's applyPresenterVoiceBoostForScreenshare)
  and its restore counterpart updated the preset state but never called
  applyConstraints() on the live mic track, so the auto-switch changed the UI
  checkmark without changing any actual audio behavior — the same bug this PR
  otherwise fixes for the manual preset switch, just missed for the automatic
  path (major finding).
- setMicrophoneMuted's setMicrophoneEnabled(!muted) call on unmute passed no
  options, so a track re-acquired after being released by the OS/browser would
  fall back to the room's original constraints instead of the active preset
  (minor finding).
- Neither path updated the room's audioCaptureDefaults, so a mid-call
  microphone device switch (which makes LiveKit create a brand new track)
  would also silently drop back to the room's original constraints (minor
  finding, inline comment).

Factored the shared apply-to-room-and-track logic into
applyVoiceProcessingPresetToRoom() and used it from setVoiceProcessingPreset,
the screenshare auto-switch, and its restore.
@rozagerardo

Copy link
Copy Markdown
Contributor Author

Addressed all 3 CodeRabbit findings in b99393a:

  1. (Major) Screenshare auto-switch didn't apply constraints — confirmed and fixed. applyPresenterVoiceBoostForScreenshare/restorePresenterVoiceAfterScreenshare updated preset state but never touched the live track, so the automatic screenshare voice-boost switch changed the UI only, not the actual mic behavior.
  2. (Minor) Unmute didn't pass preset constraints — fixed as suggested, so a track re-acquired on unmute doesn't fall back to defaults.
  3. (Minor) Room audioCaptureDefaults not updated on preset change — fixed, so a mid-call microphone device switch also preserves the active preset.

Factored the shared logic into applyVoiceProcessingPresetToRoom(), used from all three call sites (manual switch, auto-switch, restore).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/matrix/client/hooks/use-space-group-call.ts (1)

1843-1847: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Avoid falsely disabling presets when getCapabilities is unsupported.

If getCapabilities is not implemented by the browser (e.g., Firefox before version 116), capabilities will be undefined. Due to optional chaining, capabilities?.autoGainControl === undefined evaluates to true, which incorrectly marks the capability as unsupported and disables the UI control.

To prevent locking users out of features their browser might actually support, assume the capabilities are supported (i.e., not unsupported) when getCapabilities is missing.

🛠️ Proposed fix
           setUnsupportedVoiceProcessingConstraints({
-            autoGainControl: capabilities?.autoGainControl === undefined,
-            echoCancellation: capabilities?.echoCancellation === undefined,
-            noiseSuppression: capabilities?.noiseSuppression === undefined,
+            autoGainControl: capabilities ? capabilities.autoGainControl === undefined : false,
+            echoCancellation: capabilities ? capabilities.echoCancellation === undefined : false,
+            noiseSuppression: capabilities ? capabilities.noiseSuppression === undefined : false,
           });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/matrix/client/hooks/use-space-group-call.ts` around lines
1843 - 1847, Update the setUnsupportedVoiceProcessingConstraints call so each
capability is marked unsupported only when capabilities exists and its
corresponding property is undefined; when capabilities is undefined because
getCapabilities is unavailable, treat all capabilities as supported.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/core/src/matrix/client/hooks/use-space-group-call.ts`:
- Around line 1843-1847: Update the setUnsupportedVoiceProcessingConstraints
call so each capability is marked unsupported only when capabilities exists and
its corresponding property is undefined; when capabilities is undefined because
getCapabilities is unavailable, treat all capabilities as supported.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b46b5a5d-30c8-41ee-b640-1446c8bc56cc

📥 Commits

Reviewing files that changed from the base of the PR and between c3ad232 and b99393a.

📒 Files selected for processing (1)
  • packages/core/src/matrix/client/hooks/use-space-group-call.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant