feat(calls): 3-tier mic noise suppression with on-device ML (P5-30)
Replace the boolean call noise-suppression setting with a 3-way control (Off / Browser-native / ML beta) in Settings -> General -> Calls. - Off: noiseSuppression=false to Element Call - Browser-native: EC's built-in WebRTC suppressor (prior default) - ML (beta): on-device RNNoise (@sapphi-red/web-noise-suppressor) Element Call captures the mic inside its iframe and publishes to LiveKit, so the host can't reach that track; LiveKit's Krisp filter is Cloud-only (we self-host the SFU) and EC's own RNNoise PR #3892 is unmerged. The ML tier instead injects a same-origin pre-init shim into the vendored EC index.html (build/lotus-denoise.js, wired by the lotusDenoise vite plugin) that patches getUserMedia and routes the captured mic through an RNNoise AudioWorklet before LiveKit sees it -- the same post-capture pipeline as #3892, with no EC fork/AGPL/rebase burden. Falls back to the raw mic if setup fails; keeps echoCancellation/AGC on the raw capture. - settings.ts: callNoiseSuppression -> 'off'|'browser'|'ml' + legacy boolean migration (true->browser, false->off) - CallEmbed/useCallEmbed: tier maps to noiseSuppression param and appends lotusDenoise=ml (native suppressor off in ML mode) - vite.config.js: copy RNNoise worklet/wasm + shim into the EC bundle and inject the shim <script> before EC's module entry - docs: LOTUS_FEATURES.md, LOTUS_TODO.md (P5-30 done) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,11 @@ export type DateFormat =
|
||||
| 'YYYY-MM-DD'
|
||||
| '';
|
||||
export type MessageSpacing = '0' | '100' | '200' | '300' | '400' | '500';
|
||||
// Call mic noise suppression tier:
|
||||
// - 'off' : no suppression
|
||||
// - 'browser' : WebRTC built-in suppression (Element Call noiseSuppression param)
|
||||
// - 'ml' : client-side RNNoise ML suppression (Lotus denoise shim)
|
||||
export type NoiseSuppressionMode = 'off' | 'browser' | 'ml';
|
||||
export type ChatBackground =
|
||||
| 'none'
|
||||
| 'blueprint'
|
||||
@@ -109,7 +114,7 @@ export interface Settings {
|
||||
perMessageProfiles: boolean;
|
||||
|
||||
cameraOnJoin: boolean;
|
||||
callNoiseSuppression: boolean;
|
||||
callNoiseSuppression: NoiseSuppressionMode;
|
||||
pttMode: boolean;
|
||||
pttKey: string;
|
||||
|
||||
@@ -199,7 +204,7 @@ const defaultSettings: Settings = {
|
||||
perMessageProfiles: false,
|
||||
|
||||
cameraOnJoin: false,
|
||||
callNoiseSuppression: true,
|
||||
callNoiseSuppression: 'browser',
|
||||
pttMode: false,
|
||||
pttKey: 'Space',
|
||||
|
||||
@@ -235,6 +240,14 @@ export const getSettings = (): Settings => {
|
||||
return {
|
||||
...defaultSettings,
|
||||
...saved,
|
||||
// Migrate legacy boolean callNoiseSuppression -> 3-way mode:
|
||||
// true => browser-native, false => off. New string values pass through.
|
||||
callNoiseSuppression:
|
||||
typeof saved.callNoiseSuppression === 'boolean'
|
||||
? saved.callNoiseSuppression
|
||||
? 'browser'
|
||||
: 'off'
|
||||
: (saved.callNoiseSuppression ?? defaultSettings.callNoiseSuppression),
|
||||
composerToolbarButtons: {
|
||||
...DEFAULT_COMPOSER_TOOLBAR,
|
||||
...(saved.composerToolbarButtons ?? {}),
|
||||
|
||||
Reference in New Issue
Block a user