refactor: remove browser NS toggle, default RNNoise on, rework voice settings

- 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)
This commit is contained in:
Jannis Braun
2026-02-23 00:49:42 +01:00
parent dc57856506
commit 5747267a6b
6 changed files with 41 additions and 78 deletions
File diff suppressed because one or more lines are too long
@@ -20,11 +20,11 @@ export function UserSettingsModal() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const echoCancellation = useVoiceStore((s) => s.echoCancellation); const echoCancellation = useVoiceStore((s) => s.echoCancellation);
const noiseSuppression = useVoiceStore((s) => s.noiseSuppression);
const autoGainControl = useVoiceStore((s) => s.autoGainControl); const autoGainControl = useVoiceStore((s) => s.autoGainControl);
const rnnoiseEnabled = useVoiceStore((s) => s.rnnoiseEnabled);
const setEchoCancellation = useVoiceStore((s) => s.setEchoCancellation); const setEchoCancellation = useVoiceStore((s) => s.setEchoCancellation);
const toggleNoiseSuppression = useVoiceStore((s) => s.toggleNoiseSuppression);
const setAutoGainControl = useVoiceStore((s) => s.setAutoGainControl); const setAutoGainControl = useVoiceStore((s) => s.setAutoGainControl);
const setRnnoiseEnabled = useVoiceStore((s) => s.setRnnoiseEnabled);
const isOpen = activeModal === 'userSettings'; const isOpen = activeModal === 'userSettings';
@@ -126,9 +126,19 @@ export function UserSettingsModal() {
<h3 className="text-xs font-bold text-discord-text-secondary uppercase mb-3"> <h3 className="text-xs font-bold text-discord-text-secondary uppercase mb-3">
Voice Processing Voice Processing
</h3> </h3>
<p className="text-xs text-discord-text-muted mb-3">
Echo Cancellation is automatically disabled while you screen share to prevent Chrome from ducking your mic volume. <div className="flex items-center justify-between py-2">
</p> <div>
<div className="text-sm text-discord-text-primary">AI Noise Suppression</div>
<div className="text-xs text-discord-text-muted">ML-based noise removal (RNNoise) filters keyboard, fans, and background noise</div>
</div>
<button
onClick={() => setRnnoiseEnabled(!rnnoiseEnabled)}
className={`relative w-10 h-5 rounded-full transition-colors ${rnnoiseEnabled ? 'bg-discord-green' : 'bg-discord-bg-tertiary'}`}
>
<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'}`} />
</button>
</div>
<div className="flex items-center justify-between py-2"> <div className="flex items-center justify-between py-2">
<div> <div>
@@ -143,19 +153,6 @@ export function UserSettingsModal() {
</button> </button>
</div> </div>
<div className="flex items-center justify-between py-2">
<div>
<div className="text-sm text-discord-text-primary">Noise Suppression</div>
<div className="text-xs text-discord-text-muted">Filters background noise from your mic</div>
</div>
<button
onClick={toggleNoiseSuppression}
className={`relative w-10 h-5 rounded-full transition-colors ${noiseSuppression ? 'bg-discord-green' : 'bg-discord-bg-tertiary'}`}
>
<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'}`} />
</button>
</div>
<div className="flex items-center justify-between py-2"> <div className="flex items-center justify-between py-2">
<div> <div>
<div className="text-sm text-discord-text-primary">Auto Gain Control</div> <div className="text-sm text-discord-text-primary">Auto Gain Control</div>
@@ -15,10 +15,8 @@ export function VoiceControls() {
const isScreenSharing = useVoiceStore((s) => s.isScreenSharing); const isScreenSharing = useVoiceStore((s) => s.isScreenSharing);
const toggleCamera = useVoiceStore((s) => s.toggleCamera); const toggleCamera = useVoiceStore((s) => s.toggleCamera);
const toggleScreenShare = useVoiceStore((s) => s.toggleScreenShare); const toggleScreenShare = useVoiceStore((s) => s.toggleScreenShare);
const noiseSuppression = useVoiceStore((s) => s.noiseSuppression);
const toggleNoiseSuppression = useVoiceStore((s) => s.toggleNoiseSuppression);
const rnnoiseEnabled = useVoiceStore((s) => s.rnnoiseEnabled); const rnnoiseEnabled = useVoiceStore((s) => s.rnnoiseEnabled);
const toggleRnnoise = useVoiceStore((s) => s.toggleRnnoise); const setRnnoiseEnabled = useVoiceStore((s) => s.setRnnoiseEnabled);
const connectionError = useVoiceStore((s) => s.connectionError); const connectionError = useVoiceStore((s) => s.connectionError);
const isLiveKitConnected = useVoiceStore((s) => s.isLiveKitConnected); const isLiveKitConnected = useVoiceStore((s) => s.isLiveKitConnected);
const channels = useServerStore((s) => s.channels); const channels = useServerStore((s) => s.channels);
@@ -56,9 +54,6 @@ export function VoiceControls() {
console.error('[VoiceControls] Failed to toggle screen share:', err); console.error('[VoiceControls] Failed to toggle screen share:', err);
} }
}; };
const handleNoiseSuppression = () => {
toggleNoiseSuppression();
};
const handleDisconnect = () => { const handleDisconnect = () => {
wsSend({ type: 'voice_leave' }); wsSend({ type: 'voice_leave' });
useVoiceStore.getState().leaveVoice(); useVoiceStore.getState().leaveVoice();
@@ -81,9 +76,7 @@ export function VoiceControls() {
? 'bg-[#111214] text-discord-green hover:bg-[#1a1b1e]' ? 'bg-[#111214] text-discord-green hover:bg-[#1a1b1e]'
: btnDefaultStyle}`, title: isScreenSharing ? 'Stop Sharing' : 'Share Screen', children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: [_jsx("path", { d: "M20 18C21.1 18 22 17.1 22 16V6C22 4.9 21.1 4 20 4H4C2.9 4 2 4.9 2 6V16C2 17.1 2.9 18 4 18H0V20H24V18H20ZM4 6H20V16H4V6Z" }), _jsx("path", { d: "M15 11L11 14V12H9V10H11V8L15 11Z" })] }) }), _jsx("button", { onClick: () => setShowVideoQuality(!showVideoQuality), className: `${btnBase} ${showVideoQuality : btnDefaultStyle}`, title: isScreenSharing ? 'Stop Sharing' : 'Share Screen', children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: [_jsx("path", { d: "M20 18C21.1 18 22 17.1 22 16V6C22 4.9 21.1 4 20 4H4C2.9 4 2 4.9 2 6V16C2 17.1 2.9 18 4 18H0V20H24V18H20ZM4 6H20V16H4V6Z" }), _jsx("path", { d: "M15 11L11 14V12H9V10H11V8L15 11Z" })] }) }), _jsx("button", { onClick: () => setShowVideoQuality(!showVideoQuality), className: `${btnBase} ${showVideoQuality
? 'bg-[#111214] text-discord-blurple hover:bg-[#1a1b1e]' ? 'bg-[#111214] text-discord-blurple hover:bg-[#1a1b1e]'
: btnDefaultStyle}`, title: "Video Quality", children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: [_jsx("path", { d: "M3 5v14h18V5H3zm16 12H5V7h14v10z" }), _jsx("path", { d: "M8 15l2.5-3.21L13 15l2-2.5L18 17H6z" })] }) }), _jsx("button", { onClick: handleNoiseSuppression, className: `${btnBase} ${noiseSuppression : btnDefaultStyle}`, title: "Video Quality", children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: [_jsx("path", { d: "M3 5v14h18V5H3zm16 12H5V7h14v10z" }), _jsx("path", { d: "M8 15l2.5-3.21L13 15l2-2.5L18 17H6z" })] }) }), _jsx("button", { onClick: () => setRnnoiseEnabled(!rnnoiseEnabled), className: `${btnBase} ${rnnoiseEnabled
? 'bg-[#111214] text-discord-green hover:bg-[#1a1b1e]'
: btnDefaultStyle}`, title: noiseSuppression ? 'Disable Browser Noise Suppression' : 'Enable Browser Noise Suppression', children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: [_jsx("path", { d: "M7 9v6h4l5 5V4l-5 5H7z" }), noiseSuppression ? (_jsxs(_Fragment, { children: [_jsx("path", { d: "M19 12c0-1.66-.68-3.16-1.76-4.24l-1.42 1.42C16.55 9.9 17 10.9 17 12c0 1.1-.45 2.1-1.18 2.82l1.42 1.42C18.32 15.16 19 13.66 19 12z" }), _jsx("path", { d: "M21 12c0-2.76-1.12-5.26-2.93-7.07l-1.42 1.42C18.2 7.9 19 9.85 19 12c0 2.15-.8 4.1-2.35 5.65l1.42 1.42C19.88 17.26 21 14.76 21 12z", opacity: "0.6" })] })) : (_jsx("line", { x1: "19", y1: "5", x2: "19", y2: "19", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", opacity: "0.4" }))] }) }), _jsx("button", { onClick: toggleRnnoise, className: `${btnBase} ${rnnoiseEnabled
? 'bg-[#111214] text-discord-green hover:bg-[#1a1b1e]' ? 'bg-[#111214] text-discord-green hover:bg-[#1a1b1e]'
: btnDefaultStyle}`, title: rnnoiseEnabled ? 'Disable AI Noise Suppression' : 'Enable AI Noise Suppression', children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: [_jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z", opacity: rnnoiseEnabled ? 0.15 : 0.08 }), _jsx("path", { d: "M12 1a2 2 0 012 2v1a2 2 0 01-4 0V3a2 2 0 012-2z" }), _jsx("path", { d: "M12 7c-1.66 0-3 1.34-3 3v2c0 1.66 1.34 3 3 3s3-1.34 3-3v-2c0-1.66-1.34-3-3-3z" }), _jsx("path", { d: "M17 11v1c0 2.76-2.24 5-5 5s-5-2.24-5-5v-1H5v1c0 3.53 2.61 6.43 6 6.92V21h2v-2.08c3.39-.49 6-3.39 6-6.92v-1h-2z" }), rnnoiseEnabled ? (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "18", cy: "5", r: "1.2", fill: "currentColor" }), _jsx("circle", { cx: "20", cy: "8", r: "0.9", fill: "currentColor", opacity: "0.7" }), _jsx("circle", { cx: "6", cy: "5", r: "1.2", fill: "currentColor" }), _jsx("circle", { cx: "4", cy: "8", r: "0.9", fill: "currentColor", opacity: "0.7" })] })) : (_jsx("line", { x1: "4", y1: "4", x2: "20", y2: "20", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", opacity: "0.4" }))] }) }), _jsx(VideoQualityPopover, { open: showVideoQuality, onClose: () => setShowVideoQuality(false) })] })] })); : btnDefaultStyle}`, title: rnnoiseEnabled ? 'Disable AI Noise Suppression' : 'Enable AI Noise Suppression', children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: [_jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z", opacity: rnnoiseEnabled ? 0.15 : 0.08 }), _jsx("path", { d: "M12 1a2 2 0 012 2v1a2 2 0 01-4 0V3a2 2 0 012-2z" }), _jsx("path", { d: "M12 7c-1.66 0-3 1.34-3 3v2c0 1.66 1.34 3 3 3s3-1.34 3-3v-2c0-1.66-1.34-3-3-3z" }), _jsx("path", { d: "M17 11v1c0 2.76-2.24 5-5 5s-5-2.24-5-5v-1H5v1c0 3.53 2.61 6.43 6 6.92V21h2v-2.08c3.39-.49 6-3.39 6-6.92v-1h-2z" }), rnnoiseEnabled ? (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "18", cy: "5", r: "1.2", fill: "currentColor" }), _jsx("circle", { cx: "20", cy: "8", r: "0.9", fill: "currentColor", opacity: "0.7" }), _jsx("circle", { cx: "6", cy: "5", r: "1.2", fill: "currentColor" }), _jsx("circle", { cx: "4", cy: "8", r: "0.9", fill: "currentColor", opacity: "0.7" })] })) : (_jsx("line", { x1: "4", y1: "4", x2: "20", y2: "20", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", opacity: "0.4" }))] }) }), _jsx(VideoQualityPopover, { open: showVideoQuality, onClose: () => setShowVideoQuality(false) })] })] }));
} }
@@ -16,10 +16,8 @@ export function VoiceControls() {
const isScreenSharing = useVoiceStore((s) => s.isScreenSharing); const isScreenSharing = useVoiceStore((s) => s.isScreenSharing);
const toggleCamera = useVoiceStore((s) => s.toggleCamera); const toggleCamera = useVoiceStore((s) => s.toggleCamera);
const toggleScreenShare = useVoiceStore((s) => s.toggleScreenShare); const toggleScreenShare = useVoiceStore((s) => s.toggleScreenShare);
const noiseSuppression = useVoiceStore((s) => s.noiseSuppression);
const toggleNoiseSuppression = useVoiceStore((s) => s.toggleNoiseSuppression);
const rnnoiseEnabled = useVoiceStore((s) => s.rnnoiseEnabled); const rnnoiseEnabled = useVoiceStore((s) => s.rnnoiseEnabled);
const toggleRnnoise = useVoiceStore((s) => s.toggleRnnoise); const setRnnoiseEnabled = useVoiceStore((s) => s.setRnnoiseEnabled);
const connectionError = useVoiceStore((s) => s.connectionError); const connectionError = useVoiceStore((s) => s.connectionError);
const isLiveKitConnected = useVoiceStore((s) => s.isLiveKitConnected); const isLiveKitConnected = useVoiceStore((s) => s.isLiveKitConnected);
const channels = useServerStore((s) => s.channels); const channels = useServerStore((s) => s.channels);
@@ -58,11 +56,6 @@ export function VoiceControls() {
} }
}; };
const handleNoiseSuppression = () => {
// Store toggle triggers syncMic → AudioManager re-acquires stream with correct constraints
toggleNoiseSuppression();
};
const handleDisconnect = () => { const handleDisconnect = () => {
wsSend({ type: 'voice_leave' }); wsSend({ type: 'voice_leave' });
useVoiceStore.getState().leaveVoice(); useVoiceStore.getState().leaveVoice();
@@ -174,32 +167,9 @@ export function VoiceControls() {
</svg> </svg>
</button> </button>
{/* Browser Noise Suppression */}
<button
onClick={handleNoiseSuppression}
className={`${btnBase} ${
noiseSuppression
? 'bg-[#111214] text-discord-green hover:bg-[#1a1b1e]'
: btnDefaultStyle
}`}
title={noiseSuppression ? 'Disable Browser Noise Suppression' : 'Enable Browser Noise Suppression'}
>
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M7 9v6h4l5 5V4l-5 5H7z" />
{noiseSuppression ? (
<>
<path d="M19 12c0-1.66-.68-3.16-1.76-4.24l-1.42 1.42C16.55 9.9 17 10.9 17 12c0 1.1-.45 2.1-1.18 2.82l1.42 1.42C18.32 15.16 19 13.66 19 12z" />
<path d="M21 12c0-2.76-1.12-5.26-2.93-7.07l-1.42 1.42C18.2 7.9 19 9.85 19 12c0 2.15-.8 4.1-2.35 5.65l1.42 1.42C19.88 17.26 21 14.76 21 12z" opacity="0.6" />
</>
) : (
<line x1="19" y1="5" x2="19" y2="19" stroke="currentColor" strokeWidth="2" strokeLinecap="round" opacity="0.4" />
)}
</svg>
</button>
{/* AI Noise Suppression (RNNoise) */} {/* AI Noise Suppression (RNNoise) */}
<button <button
onClick={toggleRnnoise} onClick={() => setRnnoiseEnabled(!rnnoiseEnabled)}
className={`${btnBase} ${ className={`${btnBase} ${
rnnoiseEnabled rnnoiseEnabled
? 'bg-[#111214] text-discord-green hover:bg-[#1a1b1e]' ? 'bg-[#111214] text-discord-green hover:bg-[#1a1b1e]'
+8 -7
View File
@@ -130,11 +130,10 @@ export const useVoiceStore = create()(persist((set, get) => ({
noiseSuppression: true, noiseSuppression: true,
echoCancellation: true, echoCancellation: true,
autoGainControl: false, autoGainControl: false,
rnnoiseEnabled: false, rnnoiseEnabled: true,
toggleNoiseSuppression: () => set((state) => ({ noiseSuppression: !state.noiseSuppression })),
setEchoCancellation: (enabled) => set({ echoCancellation: enabled }), setEchoCancellation: (enabled) => set({ echoCancellation: enabled }),
setAutoGainControl: (enabled) => set({ autoGainControl: enabled }), setAutoGainControl: (enabled) => set({ autoGainControl: enabled }),
toggleRnnoise: () => set((state) => ({ rnnoiseEnabled: !state.rnnoiseEnabled })), setRnnoiseEnabled: (enabled) => set({ rnnoiseEnabled: enabled }),
deafenedUserIds: new Set(), deafenedUserIds: new Set(),
setUserDeafened: (userId, deafened) => { setUserDeafened: (userId, deafened) => {
set((state) => { set((state) => {
@@ -206,7 +205,7 @@ export const useVoiceStore = create()(persist((set, get) => ({
}), }),
}), { }), {
name: 'opencord-voice-settings', name: 'opencord-voice-settings',
version: 3, version: 4,
migrate: (persistedState, version) => { migrate: (persistedState, version) => {
if (version === 0) { if (version === 0) {
persistedState.streamAttenuationEnabled = false; persistedState.streamAttenuationEnabled = false;
@@ -215,13 +214,16 @@ export const useVoiceStore = create()(persist((set, get) => ({
persistedState.echoCancellation = true; persistedState.echoCancellation = true;
persistedState.autoGainControl = false; persistedState.autoGainControl = false;
} }
if (version < 3) { if (version < 4) {
persistedState.rnnoiseEnabled = false; // v4: RNNoise on by default, browser NS is no longer user-configurable
persistedState.rnnoiseEnabled = true;
persistedState.noiseSuppression = true;
} }
return persistedState; return persistedState;
}, },
storage: createJSONStorage(() => localStorage), storage: createJSONStorage(() => localStorage),
// Only persist these keys. Maps and Sets are complex to serialize. // Only persist these keys. Maps and Sets are complex to serialize.
// noiseSuppression intentionally excluded — always true, managed by AudioManager.
partialize: (state) => ({ partialize: (state) => ({
currentVoiceChannelId: state.currentVoiceChannelId, currentVoiceChannelId: state.currentVoiceChannelId,
isMuted: state.isMuted, isMuted: state.isMuted,
@@ -231,7 +233,6 @@ export const useVoiceStore = create()(persist((set, get) => ({
inputDeviceId: state.inputDeviceId, inputDeviceId: state.inputDeviceId,
outputDeviceId: state.outputDeviceId, outputDeviceId: state.outputDeviceId,
videoQuality: state.videoQuality, videoQuality: state.videoQuality,
noiseSuppression: state.noiseSuppression,
echoCancellation: state.echoCancellation, echoCancellation: state.echoCancellation,
autoGainControl: state.autoGainControl, autoGainControl: state.autoGainControl,
rnnoiseEnabled: state.rnnoiseEnabled, rnnoiseEnabled: state.rnnoiseEnabled,
+11 -9
View File
@@ -65,10 +65,9 @@ interface VoiceState {
echoCancellation: boolean; echoCancellation: boolean;
autoGainControl: boolean; autoGainControl: boolean;
rnnoiseEnabled: boolean; rnnoiseEnabled: boolean;
toggleNoiseSuppression: () => void;
setEchoCancellation: (enabled: boolean) => void; setEchoCancellation: (enabled: boolean) => void;
setAutoGainControl: (enabled: boolean) => void; setAutoGainControl: (enabled: boolean) => void;
toggleRnnoise: () => void; setRnnoiseEnabled: (enabled: boolean) => void;
deafenedUserIds: Set<string>; deafenedUserIds: Set<string>;
setUserDeafened: (userId: string, deafened: boolean) => void; setUserDeafened: (userId: string, deafened: boolean) => void;
// WebSocket-based voice user status (visible without joining LiveKit) // WebSocket-based voice user status (visible without joining LiveKit)
@@ -226,11 +225,10 @@ export const useVoiceStore = create<VoiceState>()(
noiseSuppression: true, noiseSuppression: true,
echoCancellation: true, echoCancellation: true,
autoGainControl: false, autoGainControl: false,
rnnoiseEnabled: false, rnnoiseEnabled: true,
toggleNoiseSuppression: () => set((state) => ({ noiseSuppression: !state.noiseSuppression })),
setEchoCancellation: (enabled) => set({ echoCancellation: enabled }), setEchoCancellation: (enabled) => set({ echoCancellation: enabled }),
setAutoGainControl: (enabled) => set({ autoGainControl: enabled }), setAutoGainControl: (enabled) => set({ autoGainControl: enabled }),
toggleRnnoise: () => set((state) => ({ rnnoiseEnabled: !state.rnnoiseEnabled })), setRnnoiseEnabled: (enabled) => set({ rnnoiseEnabled: enabled }),
deafenedUserIds: new Set(), deafenedUserIds: new Set(),
setUserDeafened: (userId, deafened) => { setUserDeafened: (userId, deafened) => {
set((state) => { set((state) => {
@@ -305,7 +303,7 @@ export const useVoiceStore = create<VoiceState>()(
}), }),
{ {
name: 'opencord-voice-settings', name: 'opencord-voice-settings',
version: 3, version: 4,
migrate: (persistedState: any, version: number) => { migrate: (persistedState: any, version: number) => {
if (version === 0) { if (version === 0) {
persistedState.streamAttenuationEnabled = false; persistedState.streamAttenuationEnabled = false;
@@ -314,13 +312,18 @@ export const useVoiceStore = create<VoiceState>()(
persistedState.echoCancellation = true; persistedState.echoCancellation = true;
persistedState.autoGainControl = false; persistedState.autoGainControl = false;
} }
if (version < 3) { if (version < 4) {
persistedState.rnnoiseEnabled = false; // v4: RNNoise on by default, browser NS is no longer user-configurable
// (AudioManager uses it as automatic fallback when RNNoise is off)
persistedState.rnnoiseEnabled = true;
persistedState.noiseSuppression = true;
} }
return persistedState; return persistedState;
}, },
storage: createJSONStorage(() => localStorage), storage: createJSONStorage(() => localStorage),
// Only persist these keys. Maps and Sets are complex to serialize. // Only persist these keys. Maps and Sets are complex to serialize.
// noiseSuppression is intentionally excluded — always true internally,
// managed automatically by AudioManager based on RNNoise state.
partialize: (state) => ({ partialize: (state) => ({
currentVoiceChannelId: state.currentVoiceChannelId, currentVoiceChannelId: state.currentVoiceChannelId,
isMuted: state.isMuted, isMuted: state.isMuted,
@@ -330,7 +333,6 @@ export const useVoiceStore = create<VoiceState>()(
inputDeviceId: state.inputDeviceId, inputDeviceId: state.inputDeviceId,
outputDeviceId: state.outputDeviceId, outputDeviceId: state.outputDeviceId,
videoQuality: state.videoQuality, videoQuality: state.videoQuality,
noiseSuppression: state.noiseSuppression,
echoCancellation: state.echoCancellation, echoCancellation: state.echoCancellation,
autoGainControl: state.autoGainControl, autoGainControl: state.autoGainControl,
rnnoiseEnabled: state.rnnoiseEnabled, rnnoiseEnabled: state.rnnoiseEnabled,