diff --git a/locales/en/app.json b/locales/en/app.json index d5c6293a..e131bb8f 100644 --- a/locales/en/app.json +++ b/locales/en/app.json @@ -70,6 +70,7 @@ "livekit_server_info": "LiveKit Server Info", "livekit_sfu": "LiveKit SFU: {{url}}", "matrix_id": "Matrix ID: {{id}}", + "mute_all_audio": "Mute all audio (participants, reactions, join sounds)", "show_connection_stats": "Show connection statistics", "show_non_member_tiles": "Show tiles for non-member media", "url_params": "URL parameters", diff --git a/src/room/CallEventAudioRenderer.tsx b/src/room/CallEventAudioRenderer.tsx index 6eeef4c4..a0d685ff 100644 --- a/src/room/CallEventAudioRenderer.tsx +++ b/src/room/CallEventAudioRenderer.tsx @@ -47,12 +47,15 @@ export const callEventAudioSounds = prefetchSounds({ export function CallEventAudioRenderer({ vm, + muted, }: { vm: CallViewModel; + muted?: boolean; }): ReactNode { const audioEngineCtx = useAudioContext({ sounds: callEventAudioSounds, latencyHint: "interactive", + muted, }); const audioEngineRef = useLatest(audioEngineCtx); diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 0d727485..960b4a05 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -62,8 +62,9 @@ import { } from "../utils/errors.ts"; import { GroupCallErrorBoundary } from "./GroupCallErrorBoundary.tsx"; import { - useExperimentalToDeviceTransportSetting, - useNewMembershipManagerSetting as useNewMembershipManagerSetting, + useNewMembershipManager as useNewMembershipManagerSetting, + useExperimentalToDeviceTransport as useExperimentalToDeviceTransportSetting, + muteAllAudio as muteAllAudioSetting, useSetting, } from "../settings/settings"; import { useTypedEventEmitter } from "../useEvents"; @@ -104,11 +105,13 @@ export const GroupCallView: FC = ({ null, ); + const [muteAllAudio] = useSetting(muteAllAudioSetting); const memberships = useMatrixRTCSessionMemberships(rtcSession); const leaveSoundContext = useLatest( useAudioContext({ sounds: callEventAudioSounds, latencyHint: "interactive", + muted: muteAllAudio, }), ); // This should use `useEffectEvent` (only available in experimental versions) diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index b434a1da..33f42f1b 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -96,7 +96,8 @@ import { ReactionsOverlay } from "./ReactionsOverlay"; import { CallEventAudioRenderer } from "./CallEventAudioRenderer"; import { debugTileLayout as debugTileLayoutSetting, - useExperimentalToDeviceTransportSetting, + useExperimentalToDeviceTransport as useExperimentalToDeviceTransportSetting, + muteAllAudio as muteAllAudioSetting, useSetting, } from "../settings/settings"; import { ReactionsReader } from "../reactions/ReactionsReader"; @@ -220,6 +221,8 @@ export const InCallView: FC = ({ room: livekitRoom, }); + const [muteAllAudio] = useSetting(muteAllAudioSetting); + const [toDeviceEncryptionSetting] = useSetting( useExperimentalToDeviceTransportSetting, ); @@ -693,10 +696,10 @@ export const InCallView: FC = ({ ) } - + {renderContent()} - - + + {footer} {layout.type !== "pip" && ( diff --git a/src/room/ReactionAudioRenderer.tsx b/src/room/ReactionAudioRenderer.tsx index c65f6094..2b95acb9 100644 --- a/src/room/ReactionAudioRenderer.tsx +++ b/src/room/ReactionAudioRenderer.tsx @@ -24,8 +24,10 @@ const soundMap = Object.fromEntries([ export function ReactionsAudioRenderer({ vm, + muted, }: { vm: CallViewModel; + muted?: boolean; }): ReactNode { const [shouldPlay] = useSetting(playReactionsSound); const [soundCache, setSoundCache] = useState = ({ client, livekitRoom }) => { useExperimentalToDeviceTransport, setUseExperimentalToDeviceTransport, ] = useSetting(useExperimentalToDeviceTransportSetting); + + const [muteAllAudio, setMuteAllAudio] = useSetting(muteAllAudioSetting); + const urlParams = useUrlParams(); const sfuUrl = useMemo((): URL | null => { @@ -175,6 +179,20 @@ export const DeveloperSettingsTab: FC = ({ client, livekitRoom }) => { )} /> + + ): void => { + setMuteAllAudio(event.target.checked); + }, + [setMuteAllAudio], + )} + /> + {livekitRoom ? ( <>

diff --git a/src/settings/settings.ts b/src/settings/settings.ts index 9f9f266c..75b98dfc 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -113,14 +113,16 @@ export const soundEffectVolumeSetting = new Setting( 0.5, ); -export const useNewMembershipManagerSetting = new Setting( +export const useNewMembershipManager = new Setting( "new-membership-manager", true, ); -export const useExperimentalToDeviceTransportSetting = new Setting( +export const useExperimentalToDeviceTransport = new Setting( "experimental-to-device-transport", true, ); +export const muteAllAudio = new Setting("mute-all-audio", false); + export const alwaysShowSelf = new Setting("always-show-self", true); diff --git a/src/useAudioContext.tsx b/src/useAudioContext.tsx index d96b9fdc..d6bc314b 100644 --- a/src/useAudioContext.tsx +++ b/src/useAudioContext.tsx @@ -47,6 +47,7 @@ interface Props { */ sounds: PrefetchedSounds | null; latencyHint: AudioContextLatencyCategory; + muted?: boolean; } interface UseAudioContext { @@ -112,7 +113,7 @@ export function useAudioContext( }, [audioContext, devices]); // Don't return a function until we're ready. - if (!audioContext || !audioBuffers) { + if (!audioContext || !audioBuffers || props.muted) { return null; } return {