From 5747267a6b9a7bd6e2dee373de7db74fd0c36f83 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Mon, 23 Feb 2026 00:49:42 +0100 Subject: [PATCH] refactor: remove browser NS toggle, default RNNoise on, rework voice settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RNNoise (AI Noise Suppression) now enabled by default for all users - Remove redundant browser Noise Suppression toggle from VoiceControls and UserSettings — AudioManager handles it automatically as fallback - Add AI Noise Suppression toggle to UserSettings panel - Rename toggleRnnoise → setRnnoiseEnabled for clearer API - Store migration v3→v4: enable RNNoise for existing users - Keep Echo Cancellation and Auto Gain Control (orthogonal features) --- .../web/src/components/modals/UserSettings.js | 6 ++-- .../src/components/modals/UserSettings.tsx | 33 ++++++++---------- .../web/src/components/voice/VoiceControls.js | 11 ++---- .../src/components/voice/VoiceControls.tsx | 34 ++----------------- packages/web/src/stores/voiceStore.js | 15 ++++---- packages/web/src/stores/voiceStore.ts | 20 ++++++----- 6 files changed, 41 insertions(+), 78 deletions(-) diff --git a/packages/web/src/components/modals/UserSettings.js b/packages/web/src/components/modals/UserSettings.js index f14f2d8b..7983cde9 100644 --- a/packages/web/src/components/modals/UserSettings.js +++ b/packages/web/src/components/modals/UserSettings.js @@ -18,11 +18,11 @@ export function UserSettingsModal() { const [success, setSuccess] = useState(''); const [isLoading, setIsLoading] = useState(false); const echoCancellation = useVoiceStore((s) => s.echoCancellation); - const noiseSuppression = useVoiceStore((s) => s.noiseSuppression); const autoGainControl = useVoiceStore((s) => s.autoGainControl); + const rnnoiseEnabled = useVoiceStore((s) => s.rnnoiseEnabled); const setEchoCancellation = useVoiceStore((s) => s.setEchoCancellation); - const toggleNoiseSuppression = useVoiceStore((s) => s.toggleNoiseSuppression); const setAutoGainControl = useVoiceStore((s) => s.setAutoGainControl); + const setRnnoiseEnabled = useVoiceStore((s) => s.setRnnoiseEnabled); const isOpen = activeModal === 'userSettings'; const handleSave = async () => { setError(''); @@ -50,5 +50,5 @@ export function UserSettingsModal() { }; if (!user) return null; - return (_jsx(Modal, { isOpen: isOpen, onClose: closeModal, title: "User Settings", maxWidth: "max-w-lg", children: _jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex items-center gap-4 p-4 bg-discord-bg-secondary rounded-lg", children: [_jsx(Avatar, { src: user.avatar, name: user.displayName ?? user.username, size: 64, status: user.status }), _jsxs("div", { children: [_jsx("div", { className: "font-bold text-lg", children: user.displayName ?? user.username }), _jsxs("div", { className: "text-discord-text-muted text-sm", children: ["@", user.username] }), user.customStatus && (_jsx("div", { className: "text-discord-text-secondary text-sm mt-1", children: user.customStatus }))] })] }), error && (_jsx("div", { className: "p-2 bg-discord-red/10 border border-discord-red/30 rounded text-discord-text-danger text-sm", children: error })), success && (_jsx("div", { className: "p-2 bg-discord-green/10 border border-discord-green/30 rounded text-discord-text-positive text-sm", children: success })), _jsxs("div", { children: [_jsx("label", { className: "block text-xs font-bold text-discord-text-secondary uppercase mb-2", children: "Status" }), _jsxs("select", { value: status, onChange: (e) => setStatus(e.target.value), className: "w-full px-3 py-2 bg-discord-bg-tertiary rounded text-discord-text-primary outline-none focus:ring-2 focus:ring-discord-blurple appearance-none", children: [_jsx("option", { value: "online", children: "Online" }), _jsx("option", { value: "idle", children: "Idle" }), _jsx("option", { value: "dnd", children: "Do Not Disturb" })] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-xs font-bold text-discord-text-secondary uppercase mb-2", children: "Display Name" }), _jsx("input", { type: "text", value: displayName, onChange: (e) => setDisplayName(e.target.value), className: "w-full px-3 py-2 bg-discord-bg-tertiary rounded text-discord-text-primary outline-none focus:ring-2 focus:ring-discord-blurple" })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-xs font-bold text-discord-text-secondary uppercase mb-2", children: "Custom Status" }), _jsx("input", { type: "text", value: customStatus, onChange: (e) => setCustomStatus(e.target.value), className: "w-full px-3 py-2 bg-discord-bg-tertiary rounded text-discord-text-primary outline-none focus:ring-2 focus:ring-discord-blurple", placeholder: "What are you up to?" })] }), _jsxs("div", { className: "border-t border-white/[0.06] pt-4", children: [_jsx("h3", { className: "text-xs font-bold text-discord-text-secondary uppercase mb-3", children: "Voice Processing" }), _jsx("p", { className: "text-xs text-discord-text-muted mb-3", children: "Disable Auto Gain Control when streaming to prevent your browser from ducking your microphone." }), _jsxs("div", { className: "flex items-center justify-between py-2", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm text-discord-text-primary", children: "Echo Cancellation" }), _jsx("div", { className: "text-xs text-discord-text-muted", children: "Removes echo when using speakers" })] }), _jsx("button", { onClick: () => setEchoCancellation(!echoCancellation), className: `relative w-10 h-5 rounded-full transition-colors ${echoCancellation ? 'bg-discord-green' : 'bg-discord-bg-tertiary'}`, children: _jsx("div", { className: `absolute top-0.5 left-0.5 w-4 h-4 rounded-full bg-white transition-transform ${echoCancellation ? 'translate-x-5' : 'translate-x-0'}` }) })] }), _jsxs("div", { className: "flex items-center justify-between py-2", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm text-discord-text-primary", children: "Noise Suppression" }), _jsx("div", { className: "text-xs text-discord-text-muted", children: "Filters background noise from your mic" })] }), _jsx("button", { onClick: toggleNoiseSuppression, className: `relative w-10 h-5 rounded-full transition-colors ${noiseSuppression ? 'bg-discord-green' : 'bg-discord-bg-tertiary'}`, children: _jsx("div", { className: `absolute top-0.5 left-0.5 w-4 h-4 rounded-full bg-white transition-transform ${noiseSuppression ? 'translate-x-5' : 'translate-x-0'}` }) })] }), _jsxs("div", { className: "flex items-center justify-between py-2", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm text-discord-text-primary", children: "Auto Gain Control" }), _jsx("div", { className: "text-xs text-discord-text-muted", children: "Auto-adjusts mic volume \u2014 can cause voice ducking during streams" })] }), _jsx("button", { onClick: () => setAutoGainControl(!autoGainControl), className: `relative w-10 h-5 rounded-full transition-colors ${autoGainControl ? 'bg-discord-green' : 'bg-discord-bg-tertiary'}`, children: _jsx("div", { className: `absolute top-0.5 left-0.5 w-4 h-4 rounded-full bg-white transition-transform ${autoGainControl ? 'translate-x-5' : 'translate-x-0'}` }) })] })] }), _jsxs("div", { className: "flex items-center justify-between pt-2", children: [_jsx("button", { onClick: handleLogout, className: "px-4 py-2 text-sm text-discord-red hover:bg-discord-red/10 rounded transition-colors", children: "Log Out" }), _jsx("button", { onClick: handleSave, disabled: isLoading, className: "px-4 py-2 bg-discord-blurple hover:bg-discord-blurple-hover text-white text-sm font-medium rounded transition-colors disabled:opacity-50", children: isLoading ? 'Saving...' : 'Save Changes' })] })] }) })); + return (_jsx(Modal, { isOpen: isOpen, onClose: closeModal, title: "User Settings", maxWidth: "max-w-lg", children: _jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex items-center gap-4 p-4 bg-discord-bg-secondary rounded-lg", children: [_jsx(Avatar, { src: user.avatar, name: user.displayName ?? user.username, size: 64, status: user.status }), _jsxs("div", { children: [_jsx("div", { className: "font-bold text-lg", children: user.displayName ?? user.username }), _jsxs("div", { className: "text-discord-text-muted text-sm", children: ["@", user.username] }), user.customStatus && (_jsx("div", { className: "text-discord-text-secondary text-sm mt-1", children: user.customStatus }))] })] }), error && (_jsx("div", { className: "p-2 bg-discord-red/10 border border-discord-red/30 rounded text-discord-text-danger text-sm", children: error })), success && (_jsx("div", { className: "p-2 bg-discord-green/10 border border-discord-green/30 rounded text-discord-text-positive text-sm", children: success })), _jsxs("div", { children: [_jsx("label", { className: "block text-xs font-bold text-discord-text-secondary uppercase mb-2", children: "Status" }), _jsxs("select", { value: status, onChange: (e) => setStatus(e.target.value), className: "w-full px-3 py-2 bg-discord-bg-tertiary rounded text-discord-text-primary outline-none focus:ring-2 focus:ring-discord-blurple appearance-none", children: [_jsx("option", { value: "online", children: "Online" }), _jsx("option", { value: "idle", children: "Idle" }), _jsx("option", { value: "dnd", children: "Do Not Disturb" })] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-xs font-bold text-discord-text-secondary uppercase mb-2", children: "Display Name" }), _jsx("input", { type: "text", value: displayName, onChange: (e) => setDisplayName(e.target.value), className: "w-full px-3 py-2 bg-discord-bg-tertiary rounded text-discord-text-primary outline-none focus:ring-2 focus:ring-discord-blurple" })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-xs font-bold text-discord-text-secondary uppercase mb-2", children: "Custom Status" }), _jsx("input", { type: "text", value: customStatus, onChange: (e) => setCustomStatus(e.target.value), className: "w-full px-3 py-2 bg-discord-bg-tertiary rounded text-discord-text-primary outline-none focus:ring-2 focus:ring-discord-blurple", placeholder: "What are you up to?" })] }), _jsxs("div", { className: "border-t border-white/[0.06] pt-4", children: [_jsx("h3", { className: "text-xs font-bold text-discord-text-secondary uppercase mb-3", children: "Voice Processing" }), _jsxs("div", { className: "flex items-center justify-between py-2", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm text-discord-text-primary", children: "AI Noise Suppression" }), _jsx("div", { className: "text-xs text-discord-text-muted", children: "ML-based noise removal (RNNoise) \u2014 filters keyboard, fans, and background noise" })] }), _jsx("button", { onClick: () => setRnnoiseEnabled(!rnnoiseEnabled), className: `relative w-10 h-5 rounded-full transition-colors ${rnnoiseEnabled ? 'bg-discord-green' : 'bg-discord-bg-tertiary'}`, children: _jsx("div", { className: `absolute top-0.5 left-0.5 w-4 h-4 rounded-full bg-white transition-transform ${rnnoiseEnabled ? 'translate-x-5' : 'translate-x-0'}` }) })] }), _jsxs("div", { className: "flex items-center justify-between py-2", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm text-discord-text-primary", children: "Echo Cancellation" }), _jsx("div", { className: "text-xs text-discord-text-muted", children: "Removes echo when using speakers (auto-disabled during screen share)" })] }), _jsx("button", { onClick: () => setEchoCancellation(!echoCancellation), className: `relative w-10 h-5 rounded-full transition-colors ${echoCancellation ? 'bg-discord-green' : 'bg-discord-bg-tertiary'}`, children: _jsx("div", { className: `absolute top-0.5 left-0.5 w-4 h-4 rounded-full bg-white transition-transform ${echoCancellation ? 'translate-x-5' : 'translate-x-0'}` }) })] }), _jsxs("div", { className: "flex items-center justify-between py-2", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm text-discord-text-primary", children: "Auto Gain Control" }), _jsx("div", { className: "text-xs text-discord-text-muted", children: "Auto-adjusts mic volume \u2014 can cause voice ducking during streams" })] }), _jsx("button", { onClick: () => setAutoGainControl(!autoGainControl), className: `relative w-10 h-5 rounded-full transition-colors ${autoGainControl ? 'bg-discord-green' : 'bg-discord-bg-tertiary'}`, children: _jsx("div", { className: `absolute top-0.5 left-0.5 w-4 h-4 rounded-full bg-white transition-transform ${autoGainControl ? 'translate-x-5' : 'translate-x-0'}` }) })] })] }), _jsxs("div", { className: "flex items-center justify-between pt-2", children: [_jsx("button", { onClick: handleLogout, className: "px-4 py-2 text-sm text-discord-red hover:bg-discord-red/10 rounded transition-colors", children: "Log Out" }), _jsx("button", { onClick: handleSave, disabled: isLoading, className: "px-4 py-2 bg-discord-blurple hover:bg-discord-blurple-hover text-white text-sm font-medium rounded transition-colors disabled:opacity-50", children: isLoading ? 'Saving...' : 'Save Changes' })] })] }) })); } diff --git a/packages/web/src/components/modals/UserSettings.tsx b/packages/web/src/components/modals/UserSettings.tsx index c2f41cc2..3f356a54 100644 --- a/packages/web/src/components/modals/UserSettings.tsx +++ b/packages/web/src/components/modals/UserSettings.tsx @@ -20,11 +20,11 @@ export function UserSettingsModal() { const [isLoading, setIsLoading] = useState(false); const echoCancellation = useVoiceStore((s) => s.echoCancellation); - const noiseSuppression = useVoiceStore((s) => s.noiseSuppression); const autoGainControl = useVoiceStore((s) => s.autoGainControl); + const rnnoiseEnabled = useVoiceStore((s) => s.rnnoiseEnabled); const setEchoCancellation = useVoiceStore((s) => s.setEchoCancellation); - const toggleNoiseSuppression = useVoiceStore((s) => s.toggleNoiseSuppression); const setAutoGainControl = useVoiceStore((s) => s.setAutoGainControl); + const setRnnoiseEnabled = useVoiceStore((s) => s.setRnnoiseEnabled); const isOpen = activeModal === 'userSettings'; @@ -126,9 +126,19 @@ export function UserSettingsModal() {
- Echo Cancellation is automatically disabled while you screen share to prevent Chrome from ducking your mic volume. -
+ +