fix(calls): harden ML denoise shim against static; fix lint/format
CI / Build & Quality Checks (push) Successful in 10m26s
Trigger Desktop Build / trigger (push) Successful in 17s

ML noise suppression produced loud static on real calls. RNNoise requires
mono 48kHz float input; feeding it stereo or wrong-rate data is the classic
cause of that static. Harden the shim:
- request mono (channelCount:1) + 48kHz capture
- run a 48kHz AudioContext and BAIL to the raw mic if the browser won't
  give a true 48kHz context (wrong-rate data -> static)
- force the worklet node to explicit mono in/out
- use the non-SIMD rnnoise.wasm (SIMD build artifacts on some GPUs)
- share one AudioContext across captures

Also fix the two CI-blocking eslint errors (unused vars in UrlPreviewCard
and useLocalMessageSearch) and apply repo-wide prettier formatting so
check:eslint and check:prettier pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-15 20:50:00 -04:00
parent 5deed79b42
commit 4a401cf816
13 changed files with 388 additions and 357 deletions
+35 -35
View File
@@ -173,19 +173,19 @@ Decorative CSS-only overlays that activate automatically on holidays and events.
### Themes
| Theme | Window | Effect |
|---|---|---|
| 🎆 New Year | Dec 31Jan 2 | Radial firework bursts in gold, red, cyan, purple; gold shimmer sweep |
| 🏮 Lunar New Year | Jan 22Feb 5 | Floating paper lanterns bobbing; silk texture; gold shimmer accent |
| 💖 Valentine's Day | Feb 1015 | ♥ hearts floating upward; soft pink ambient glow |
| 🍀 St. Patrick's Day | Mar 1518 | ☘ clovers drifting down; gold metallic shimmer top border |
| 🃏 April Fool's | Apr 1 | Glitch overlay: RGB channel separation, hue-rotate spikes, scanline sweep, "SIGNAL LOST" watermark |
| 🌱 Earth Day | Apr 2023 | 🌿🍃 leaf emoji drift; sage green ambient tint; vine accent on left edge |
| 🍂 Autumn | Sep 21Oct 31 | Warm orange/amber leaf shapes rotating and falling |
| 👾 Arcade Day | Sep 12 | CRT scanlines; blinking pixel corner decorations; "INSERT COIN" prompt |
| 🚀 Deep Space Week | Oct 410 | Warp-speed star streaks radiating from screen centre; nebula purple/blue ambient |
| 🎃 Halloween | Oct 15Nov 1 | Purple and orange glowing particles; SVG spider web in top-left corner; dark purple tint |
| ❄️ Christmas | Dec 10Jan 2 | White dot snowfall in multiple layers at varied speeds |
| Theme | Window | Effect |
| -------------------- | ------------- | -------------------------------------------------------------------------------------------------- |
| 🎆 New Year | Dec 31Jan 2 | Radial firework bursts in gold, red, cyan, purple; gold shimmer sweep |
| 🏮 Lunar New Year | Jan 22Feb 5 | Floating paper lanterns bobbing; silk texture; gold shimmer accent |
| 💖 Valentine's Day | Feb 1015 | ♥ hearts floating upward; soft pink ambient glow |
| 🍀 St. Patrick's Day | Mar 1518 | ☘ clovers drifting down; gold metallic shimmer top border |
| 🃏 April Fool's | Apr 1 | Glitch overlay: RGB channel separation, hue-rotate spikes, scanline sweep, "SIGNAL LOST" watermark |
| 🌱 Earth Day | Apr 2023 | 🌿🍃 leaf emoji drift; sage green ambient tint; vine accent on left edge |
| 🍂 Autumn | Sep 21Oct 31 | Warm orange/amber leaf shapes rotating and falling |
| 👾 Arcade Day | Sep 12 | CRT scanlines; blinking pixel corner decorations; "INSERT COIN" prompt |
| 🚀 Deep Space Week | Oct 410 | Warp-speed star streaks radiating from screen centre; nebula purple/blue ambient |
| 🎃 Halloween | Oct 15Nov 1 | Purple and orange glowing particles; SVG spider web in top-left corner; dark purple tint |
| ❄️ Christmas | Dec 10Jan 2 | White dot snowfall in multiple layers at varied speeds |
### Implementation
@@ -209,17 +209,17 @@ Animated APNG overlay frames that float around user avatars, inspired by Discord
99 hand-curated, original-IP decorations (no licensed character artwork) organized into 9 categories:
| Category | Count | Highlights |
|---|---|---|
| Gaming | 13 | Slither 'n Snack, Joystick, Space Invaders, Gaming Headsets |
| Cyber | 9 | Cybernetic, Glitch, Digital Sunrise, Futuristic UI (3 colors) |
| Space | 8 | Black Hole, Constellations, Solar Orbit, Aurora |
| Fantasy | 22 | Kitsune, Phoenix, Glowing Runes, D&D dice, Crystal Balls |
| Elements | 7 | Fire, Water, Air, Earth, Lightning, Ki Energy |
| Japanese | 6 | Kabuto, Oni Mask, Sakura Warrior, Straw Hat |
| Nature | 12 | **Lotus Flower**, Koi Pond, Sakura, Fall Leaves, Fireflies |
| Spooky | 13 | Candlelight, Witch Hat, Ghosts, Jack-o'-Lantern |
| Cozy | 11 | Cozy Cat, Fox Hat (3 colors), Cat Ears, Frog Hat |
| Category | Count | Highlights |
| -------- | ----- | ------------------------------------------------------------- |
| Gaming | 13 | Slither 'n Snack, Joystick, Space Invaders, Gaming Headsets |
| Cyber | 9 | Cybernetic, Glitch, Digital Sunrise, Futuristic UI (3 colors) |
| Space | 8 | Black Hole, Constellations, Solar Orbit, Aurora |
| Fantasy | 22 | Kitsune, Phoenix, Glowing Runes, D&D dice, Crystal Balls |
| Elements | 7 | Fire, Water, Air, Earth, Lightning, Ki Energy |
| Japanese | 6 | Kabuto, Oni Mask, Sakura Warrior, Straw Hat |
| Nature | 12 | **Lotus Flower**, Koi Pond, Sakura, Fall Leaves, Fireflies |
| Spooky | 13 | Candlelight, Witch Hat, Ghosts, Jack-o'-Lantern |
| Cozy | 11 | Cozy Cat, Fox Hat (3 colors), Cat Ears, Frog Hat |
All decoration files are 256×256 APNGs. They animate natively in all modern browsers via `<img>` elements.
@@ -239,12 +239,12 @@ Files are self-hosted on the Lotus Nextcloud instance. Direct access: `https://d
### Placement — Where Decorations Render
| Location | File |
|---|---|
| Message timeline | `src/app/features/room/message/Message.tsx` |
| Members drawer | `src/app/features/room/MembersDrawer.tsx` |
| Location | File |
| ----------------------- | -------------------------------------------------------------------- |
| Message timeline | `src/app/features/room/message/Message.tsx` |
| Members drawer | `src/app/features/room/MembersDrawer.tsx` |
| `@mention` autocomplete | `src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx` |
| Inbox / notifications | `src/app/pages/client/inbox/Notifications.tsx` |
| Inbox / notifications | `src/app/pages/client/inbox/Notifications.tsx` |
### Settings — Decoration Picker
@@ -409,13 +409,13 @@ Files: `src/app/utils/callSounds.ts`, `src/app/hooks/useCallJoinLeaveSounds.ts`
A three-way mic noise-suppression control in **Settings → General → Calls**:
| Tier | What it does |
|---|---|
| **Off** | No suppression (`noiseSuppression=false` to Element Call). |
| Tier | What it does |
| ------------------ | ----------------------------------------------------------------------------- |
| **Off** | No suppression (`noiseSuppression=false` to Element Call). |
| **Browser-native** | Element Call's built-in WebRTC suppressor (`noiseSuppression=true`). Default. |
| **ML (beta)** | On-device RNNoise — Krisp-style removal of fans, keyboards, dogs, etc. |
| **ML (beta)** | On-device RNNoise — Krisp-style removal of fans, keyboards, dogs, etc. |
**Why a shim, not a fork:** Element Call captures the mic *inside* its iframe and publishes to LiveKit; the host can't reach that track. LiveKit's Krisp filter is LiveKit-Cloud-only (we self-host the SFU), and EC's own RNNoise work (PR #3892) is unmerged. So the **ML tier** is delivered by injecting a same-origin pre-init script into the vendored EC `index.html` that monkeypatches `getUserMedia` and routes the captured mic through an RNNoise `AudioWorklet` (`@sapphi-red/web-noise-suppressor`) before LiveKit ever sees it — the same post-capture pipeline #3892 uses, executed from the realm we already control. Works on the self-hosted LiveKit SFU, survives EC version bumps, no EC fork/AGPL/rebase burden.
**Why a shim, not a fork:** Element Call captures the mic _inside_ its iframe and publishes to LiveKit; the host can't reach that track. LiveKit's Krisp filter is LiveKit-Cloud-only (we self-host the SFU), and EC's own RNNoise work (PR #3892) is unmerged. So the **ML tier** is delivered by injecting a same-origin pre-init script into the vendored EC `index.html` that monkeypatches `getUserMedia` and routes the captured mic through an RNNoise `AudioWorklet` (`@sapphi-red/web-noise-suppressor`) before LiveKit ever sees it — the same post-capture pipeline #3892 uses, executed from the realm we already control. Works on the self-hosted LiveKit SFU, survives EC version bumps, no EC fork/AGPL/rebase burden.
**How it's wired:**
@@ -1066,4 +1066,4 @@ The `encUrlPreview` setting defaults to `true` rather than `false`. A security a
| `src/app/hooks/useAvatarDecoration.ts` | Profile field fetch with module-level cache and in-flight deduplication |
| `src/app/components/avatar-decoration/AvatarDecoration.tsx` | APNG overlay wrapper rendered around avatars in timeline, members drawer, autocomplete |
| `src/app/features/settings/account/ProfileDecoration.tsx` | Settings decoration picker — scrollable grid, category headers, save button |
| `scripts/syncDecorations.mjs` | CDN HEAD-check sync script: removes catalog entries for deleted Nextcloud files |
| `scripts/syncDecorations.mjs` | CDN HEAD-check sync script: removes catalog entries for deleted Nextcloud files |