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:
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]'
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user