Integrate raise hand sound into call event renderer.

This commit is contained in:
Half-Shot
2024-12-03 14:40:43 +00:00
parent 719e6e7977
commit 0c331a05ad
3 changed files with 32 additions and 46 deletions

View File

@@ -51,7 +51,14 @@ afterEach(() => {
window.HTMLMediaElement.prototype.play = originalPlayFn;
});
test("plays a sound when entering a call", () => {
/**
* We don't want to play a sound when loading the call state
* because typically this occurs in two stages. We first join
* the call as a local participant and *then* the remote
* participants join from our perspective. We don't want to make
* a noise every time.
*/
test("does NOT play a sound when entering a call", () => {
const audioIsPlaying: string[] = mockMediaPlay();
const members = new Map([alice, bob].map((p) => [p.userId, p]));
const remoteParticipants = of([aliceParticipant]);
@@ -75,10 +82,7 @@ test("plays a sound when entering a call", () => {
);
render(<CallEventAudioRenderer vm={vm} />);
expect(audioIsPlaying).toEqual([
// Joining the call
enterSound,
]);
expect(audioIsPlaying).toHaveLength(0);
});
test("plays no sound when muted", () => {
@@ -141,8 +145,6 @@ test("plays a sound when a user joins", () => {
});
// Play a sound when joining a call.
expect(audioIsPlaying).toEqual([
// Joining the call
enterSound,
// Bob leaves
enterSound,
]);
@@ -178,14 +180,12 @@ test("plays a sound when a user leaves", () => {
liveKitRoom.removeParticipant(aliceParticipant);
});
expect(audioIsPlaying).toEqual([
// Joining the call
enterSound,
// Alice leaves
leaveSound,
]);
});
test("plays no sound when the participant list", () => {
test("plays no sound when the participant list is more than the maximum size", () => {
const audioIsPlaying: string[] = mockMediaPlay();
const members = new Map([alice].map((p) => [p.userId, p]));
const remoteParticipants = new Map<string, RemoteParticipant>([

View File

@@ -5,14 +5,17 @@ SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { ReactNode, useEffect } from "react";
import { ReactNode, useDeferredValue, useEffect, useMemo } from "react";
import { debounce, filter, interval, throttle } from "rxjs";
import { CallViewModel } from "../state/CallViewModel";
import joinCallSoundMp3 from "../sound/join_call.mp3";
import joinCallSoundOgg from "../sound/join_call.ogg";
import leftCallSoundMp3 from "../sound/left_call.mp3";
import leftCallSoundOgg from "../sound/left_call.ogg";
import handSoundOgg from "../sound/raise_hand.ogg?url";
import handSoundMp3 from "../sound/raise_hand.mp3?url";
import { prefetchSounds, useAudioContext } from "../useAudioContext";
import { useReactions } from "../useReactions";
// Do not play any sounds if the participant count has exceeded this
// number.
@@ -29,6 +32,10 @@ const Sounds = prefetchSounds({
mp3: leftCallSoundMp3,
ogg: leftCallSoundOgg,
},
raiseHand: {
mp3: handSoundMp3,
ogg: handSoundOgg,
},
});
export function CallEventAudioRenderer({
@@ -41,6 +48,19 @@ export function CallEventAudioRenderer({
latencyHint: "interactive",
});
const { raisedHands } = useReactions();
const raisedHandCount = useMemo(
() => Object.keys(raisedHands).length,
[raisedHands],
);
const previousRaisedHandCount = useDeferredValue(raisedHandCount);
useEffect(() => {
if (audioEngineCtx && previousRaisedHandCount < raisedHandCount) {
audioEngineCtx.playSound("raiseHand");
}
}, [audioEngineCtx, previousRaisedHandCount, raisedHandCount]);
useEffect(() => {
if (!audioEngineCtx) {
return;

View File

@@ -19,7 +19,6 @@ import {
TouchEvent,
forwardRef,
useCallback,
useDeferredValue,
useEffect,
useMemo,
useRef,
@@ -81,11 +80,8 @@ import { makeSpotlightLandscapeLayout } from "../grid/SpotlightLandscapeLayout";
import { makeSpotlightPortraitLayout } from "../grid/SpotlightPortraitLayout";
import { GridTileViewModel, TileViewModel } from "../state/TileViewModel";
import { ReactionsProvider, useReactions } from "../useReactions";
import handSoundOgg from "../sound/raise_hand.ogg?url";
import handSoundMp3 from "../sound/raise_hand.mp3?url";
import { ReactionsAudioRenderer } from "./ReactionAudioRenderer";
import { useSwitchCamera } from "./useSwitchCamera";
import { soundEffectVolumeSetting, useSetting } from "../settings/settings";
import { ReactionsOverlay } from "./ReactionsOverlay";
import { CallEventAudioRenderer } from "./CallEventAudioRenderer";
@@ -183,14 +179,7 @@ export const InCallView: FC<InCallViewProps> = ({
connState,
onShareClick,
}) => {
const [soundEffectVolume] = useSetting(soundEffectVolumeSetting);
const { supportsReactions, raisedHands, sendReaction, toggleRaisedHand } =
useReactions();
const raisedHandCount = useMemo(
() => Object.keys(raisedHands).length,
[raisedHands],
);
const previousRaisedHandCount = useDeferredValue(raisedHandCount);
const { supportsReactions, sendReaction, toggleRaisedHand } = useReactions();
useWakeLock();
@@ -340,25 +329,6 @@ export const InCallView: FC<InCallViewProps> = ({
[vm],
);
// Play a sound when the raised hand count increases.
const handRaisePlayer = useRef<HTMLAudioElement>(null);
useEffect(() => {
if (!handRaisePlayer.current) {
return;
}
if (previousRaisedHandCount < raisedHandCount) {
handRaisePlayer.current.volume = soundEffectVolume;
handRaisePlayer.current.play().catch((ex) => {
logger.warn("Failed to play raise hand sound", ex);
});
}
}, [
raisedHandCount,
handRaisePlayer,
previousRaisedHandCount,
soundEffectVolume,
]);
useEffect(() => {
widget?.api.transport
.send(
@@ -672,10 +642,6 @@ export const InCallView: FC<InCallViewProps> = ({
<RoomAudioRenderer />
{renderContent()}
<CallEventAudioRenderer vm={vm} />
<audio ref={handRaisePlayer} preload="auto" hidden>
<source src={handSoundOgg} type="audio/ogg; codecs=vorbis" />
<source src={handSoundMp3} type="audio/mpeg" />
</audio>
<ReactionsAudioRenderer />
<ReactionsOverlay />
{footer}