From e201258af33107ac6accb8dd4baecd794f692bab Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 15 Sep 2025 15:41:15 +0100 Subject: [PATCH] Add sounds for ringing (#3490) * add wait for pickup overlay Signed-off-by: Timo K * refactor and leave logic Signed-off-by: Timo K * recursive play sound logic Signed-off-by: Timo K * review Signed-off-by: Timo K * text color Signed-off-by: Timo K * overlay styling and interval fixes Signed-off-by: Timo K * fix permissions and styling Signed-off-by: Timo K * fix always getting pickup sound Signed-off-by: Timo K * Add sound effects for declined,timeout and ringtone * better ringtone * Integrate sounds * Ensure leave sound does not play * Remove unused blocked sound * fix test * Improve tests * Loop ring sound inside Audio context for better perf. * lint * better ringtone * Update to delay ringtone logic. * lint + fix test * Tidy up ring sync and add comments. * lint * Refactor onLeave to take a sound so we don't need to repeat the sound * fix import --------- Signed-off-by: Timo K Co-authored-by: Timo K --- src/room/CallEventAudioRenderer.test.tsx | 16 +++++ src/room/CallEventAudioRenderer.tsx | 14 ++++ src/room/GroupCallView.test.tsx | 7 +- src/room/GroupCallView.tsx | 12 +++- src/room/InCallView.tsx | 70 +++++++++++--------- src/room/ReactionAudioRenderer.test.tsx | 2 + src/room/WaitingForJoin.module.css | 19 +++++- src/sound/blocked.mp3 | Bin 8403 -> 0 bytes src/sound/blocked.ogg | Bin 6863 -> 0 bytes src/sound/call_declined.mp3 | Bin 0 -> 8601 bytes src/sound/call_declined.ogg | Bin 0 -> 11597 bytes src/sound/call_timeout.mp3 | Bin 0 -> 8025 bytes src/sound/call_timeout.ogg | Bin 0 -> 11231 bytes src/sound/ringtone.mp3 | Bin 0 -> 7593 bytes src/sound/ringtone.ogg | Bin 0 -> 11425 bytes src/state/CallViewModel.ts | 34 ++++++---- src/useAudioContext.tsx | 80 ++++++++++++++++++++++- src/utils/test-viewmodel.ts | 7 +- 18 files changed, 207 insertions(+), 54 deletions(-) delete mode 100644 src/sound/blocked.mp3 delete mode 100644 src/sound/blocked.ogg create mode 100644 src/sound/call_declined.mp3 create mode 100644 src/sound/call_declined.ogg create mode 100644 src/sound/call_timeout.mp3 create mode 100644 src/sound/call_timeout.ogg create mode 100644 src/sound/ringtone.mp3 create mode 100644 src/sound/ringtone.ogg diff --git a/src/room/CallEventAudioRenderer.test.tsx b/src/room/CallEventAudioRenderer.test.tsx index 894d1d24..40b79da4 100644 --- a/src/room/CallEventAudioRenderer.test.tsx +++ b/src/room/CallEventAudioRenderer.test.tsx @@ -54,6 +54,8 @@ beforeEach(() => { playSound = vitest.fn(); (useAudioContext as MockedFunction).mockReturnValue({ playSound, + playSoundLooping: vitest.fn(), + soundDuration: {}, }); }); @@ -105,6 +107,20 @@ test("plays a sound when a user leaves", () => { expect(playSound).toBeCalledWith("left"); }); +test("does not play a sound before the call is successful", () => { + const { vm, rtcMemberships$ } = getBasicCallViewModelEnvironment( + [local, alice], + [localRtcMember], + { waitForCallPickup: true }, + ); + render(); + + act(() => { + rtcMemberships$.next([localRtcMember]); + }); + expect(playSound).not.toBeCalledWith("left"); +}); + test("plays no sound when the participant list is more than the maximum size", () => { const mockRtcMemberships: CallMembership[] = [localRtcMember]; for (let i = 0; i < MAX_PARTICIPANT_COUNT_FOR_SOUND; i++) { diff --git a/src/room/CallEventAudioRenderer.tsx b/src/room/CallEventAudioRenderer.tsx index 797501b6..23997c37 100644 --- a/src/room/CallEventAudioRenderer.tsx +++ b/src/room/CallEventAudioRenderer.tsx @@ -16,6 +16,10 @@ import handSoundOgg from "../sound/raise_hand.ogg"; import handSoundMp3 from "../sound/raise_hand.mp3"; import screenShareStartedOgg from "../sound/screen_share_started.ogg"; import screenShareStartedMp3 from "../sound/screen_share_started.mp3"; +import declineMp3 from "../sound/call_declined.mp3?url"; +import declineOgg from "../sound/call_declined.ogg?url"; +import timeoutMp3 from "../sound/call_timeout.mp3?url"; +import timeoutOgg from "../sound/call_timeout.ogg?url"; import { useAudioContext } from "../useAudioContext"; import { prefetchSounds } from "../soundUtils"; import { useLatest } from "../useLatest"; @@ -37,8 +41,18 @@ export const callEventAudioSounds = prefetchSounds({ mp3: screenShareStartedMp3, ogg: screenShareStartedOgg, }, + decline: { + mp3: declineMp3, + ogg: declineOgg, + }, + timeout: { + mp3: timeoutMp3, + ogg: timeoutOgg, + }, }); +export type CallEventSounds = keyof Awaited; + export function CallEventAudioRenderer({ vm, muted, diff --git a/src/room/GroupCallView.test.tsx b/src/room/GroupCallView.test.tsx index ff2b89d5..084c06ec 100644 --- a/src/room/GroupCallView.test.tsx +++ b/src/room/GroupCallView.test.tsx @@ -12,6 +12,7 @@ import { onTestFinished, test, vi, + vitest, } from "vitest"; import { render, waitFor, screen, act } from "@testing-library/react"; import { type MatrixClient, JoinRule, type RoomState } from "matrix-js-sdk"; @@ -97,13 +98,15 @@ beforeEach(() => { playSound = vi.fn(); (useAudioContext as MockedFunction).mockReturnValue({ playSound, + playSoundLooping: vi.fn(), + soundDuration: {}, }); // A trivial implementation of Active call to ensure we are testing GroupCallView exclusively here. (ActiveCall as MockedFunction).mockImplementation( ({ onLeave }) => { return (
- +
); }, @@ -209,6 +212,8 @@ test("GroupCallView plays a leave sound synchronously in widget mode", async () ); (useAudioContext as MockedFunction).mockReturnValue({ playSound, + playSoundLooping: vitest.fn(), + soundDuration: {}, }); const { getByText, rtcSession } = createGroupCallView( diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 18ec1a6a..dbc3ea18 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -53,7 +53,10 @@ import { InviteModal } from "./InviteModal"; import { HeaderStyle, type UrlParams, useUrlParams } from "../UrlParams"; import { E2eeType } from "../e2ee/e2eeType"; import { useAudioContext } from "../useAudioContext"; -import { callEventAudioSounds } from "./CallEventAudioRenderer"; +import { + callEventAudioSounds, + type CallEventSounds, +} from "./CallEventAudioRenderer"; import { useLatest } from "../useLatest"; import { usePageTitle } from "../usePageTitle"; import { @@ -317,8 +320,11 @@ export const GroupCallView: FC = ({ const navigate = useNavigate(); const onLeave = useCallback( - (cause: "user" | "error" = "user"): void => { - const audioPromise = leaveSoundContext.current?.playSound("left"); + ( + cause: "user" | "error" = "user", + playSound: CallEventSounds = "left", + ): void => { + const audioPromise = leaveSoundContext.current?.playSound(playSound); // In embedded/widget mode the iFrame will be killed right after the call ended prohibiting the posthog event from getting sent, // therefore we want the event to be sent instantly without getting queued/batched. const sendInstantly = !!widget; diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index c1244a2a..3d7044be 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -95,7 +95,10 @@ import { } from "../reactions/useReactionsSender"; import { ReactionsAudioRenderer } from "./ReactionAudioRenderer"; import { ReactionsOverlay } from "./ReactionsOverlay"; -import { CallEventAudioRenderer } from "./CallEventAudioRenderer"; +import { + CallEventAudioRenderer, + type CallEventSounds, +} from "./CallEventAudioRenderer"; import { debugTileLayout as debugTileLayoutSetting, useExperimentalToDeviceTransport as useExperimentalToDeviceTransportSetting, @@ -117,11 +120,8 @@ import { Avatar, Size as AvatarSize } from "../Avatar"; import waitingStyles from "./WaitingForJoin.module.css"; import { prefetchSounds } from "../soundUtils"; import { useAudioContext } from "../useAudioContext"; -// TODO: Dont use this!!! use the correct sound -import genericSoundOgg from "../sound/reactions/generic.ogg?url"; -import genericSoundMp3 from "../sound/reactions/generic.mp3?url"; -import leftCallSoundMp3 from "../sound/left_call.mp3"; -import leftCallSoundOgg from "../sound/left_call.ogg"; +import ringtoneMp3 from "../sound/ringtone.mp3?url"; +import ringtoneOgg from "../sound/ringtone.ogg?url"; const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); @@ -232,7 +232,7 @@ export interface InCallViewProps { livekitRoom: LivekitRoom; muteStates: MuteStates; /** Function to call when the user explicitly ends the call */ - onLeave: () => void; + onLeave: (cause: "user", soundFile?: CallEventSounds) => void; header: HeaderStyle; otelGroupCallMembership?: OTelGroupCallMembership; connState: ECConnectionState; @@ -281,14 +281,9 @@ export const InCallView: FC = ({ // Preload a waiting and decline sounds const pickupPhaseSoundCache = useInitial(async () => { return prefetchSounds({ - waiting: { mp3: genericSoundMp3, ogg: genericSoundOgg }, - decline: { mp3: leftCallSoundMp3, ogg: leftCallSoundOgg }, - // Do we want a timeout sound? + waiting: { mp3: ringtoneMp3, ogg: ringtoneOgg }, }); }); - // configure this to sth that fits to the pickup waiting sound. - // 1600 is in sync with the animation. - const PICKUP_SOUND_INTERVAL = 1600; const pickupPhaseAudio = useAudioContext({ sounds: pickupPhaseSoundCache, @@ -356,34 +351,47 @@ export const InCallView: FC = ({ const showFooter = useBehavior(vm.showFooter$); const earpieceMode = useBehavior(vm.earpieceMode$); const audioOutputSwitcher = useBehavior(vm.audioOutputSwitcher$); - useSubscription(vm.autoLeave$, onLeave); + useSubscription(vm.autoLeave$, () => onLeave("user")); + + // We need to set the proper timings on the animation based upon the sound length. + const ringDuration = pickupPhaseAudio?.soundDuration["waiting"] ?? 1; + useEffect((): (() => void) => { + // The CSS animation includes the delay, so we must double the length of the sound. + window.document.body.style.setProperty( + "--call-ring-duration-s", + `${ringDuration * 2}s`, + ); + window.document.body.style.setProperty( + "--call-ring-delay-s", + `${ringDuration}s`, + ); + // Remove properties when we unload. + return () => { + window.document.body.style.removeProperty("--call-ring-duration-s"); + window.document.body.style.removeProperty("--call-ring-delay-s"); + }; + }, [pickupPhaseAudio?.soundDuration, ringDuration]); // When we enter timeout or decline we will leave the call. useEffect((): void | (() => void) => { if (callPickupState === "timeout") { - onLeave(); + onLeave("user", "timeout"); } if (callPickupState === "decline") { - // Wait for the sound to finish before leaving - void pickupPhaseAudio - ?.playSound("decline") - .catch((e) => { - logger.error("Failed to play decline sound", e); - }) - .finally(() => { - onLeave(); - }); + onLeave("user", "decline"); } }, [callPickupState, onLeave, pickupPhaseAudio]); // When waiting for pickup, loop a waiting sound useEffect((): void | (() => void) => { - if (callPickupState !== "ringing") return; - const interval = window.setInterval(() => { - void pickupPhaseAudio?.playSound("waiting"); - }, PICKUP_SOUND_INTERVAL); - return (): void => window.clearInterval(interval); - }, [callPickupState, pickupPhaseAudio]); + if (callPickupState !== "ringing" || !pickupPhaseAudio) return; + const endSound = pickupPhaseAudio.playSoundLooping("waiting", ringDuration); + return () => { + void endSound().catch((e) => { + logger.error("Failed to stop ringing sound", e); + }); + }; + }, [callPickupState, pickupPhaseAudio, ringDuration]); // Waiting UI overlay const waitingOverlay: JSX.Element | null = useMemo(() => { @@ -823,7 +831,7 @@ export const InCallView: FC = ({ { playSound = vitest.fn(); (useAudioContext as MockedFunction).mockReturnValue({ playSound, + playSoundLooping: vitest.fn(), + soundDuration: {}, }); }); diff --git a/src/room/WaitingForJoin.module.css b/src/room/WaitingForJoin.module.css index 8f7ebeec..a598e482 100644 --- a/src/room/WaitingForJoin.module.css +++ b/src/room/WaitingForJoin.module.css @@ -25,7 +25,9 @@ inset: -12px; border-radius: 9999px; border: 12px solid rgba(255, 255, 255, 0.6); - animation: pulse 1.6s ease-out infinite; + animation: pulse var(--call-ring-duration-s) ease-out infinite; + animation-delay: 1s; + opacity: 0; } .text { @@ -36,13 +38,24 @@ 0% { transform: scale(0.95); opacity: 0.7; + transform: scale(0); + opacity: 1; } - 70% { + 35% { transform: scale(1.15); opacity: 0.15; } - 100% { + 50% { transform: scale(1.2); opacity: 0; } + 50.01% { + transform: scale(0); + } + 85% { + transform: scale(0); + } + 100% { + transform: scale(0); + } } diff --git a/src/sound/blocked.mp3 b/src/sound/blocked.mp3 deleted file mode 100644 index d37e5fc60e6ffd682b68e0d3f88f859d7025eb0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8403 zcmeHsXH-+$x9=vj0HGs>p3tO2LKl$GArz$-0qI>Jbg>Y6uhIqSMVfR0MS3-$6cG>= z=|!Yik&+kAdH*-w8RLGqAMU3+&M#wRuUWG8Tyw7Z+iQ>Ls7sOn7vM25Hr60qX#s#( z&pyaST3!qxBZiQW_{;kD7ye}V|C9dfp^mqU55Yp11|SCjSS6tR58V0(!v2Axf1>9f z`1B9p|AC802r)Ze#4L9q{0F?Gqkj)!m63U;mH$!yd;K>9|7PHSCIbXcZ~_1@E?ke) zc^MSA3C5I^1CkNqik(YXqM`!lDv%u@Uo3^ynUVIt-C}Y;zy1n{aA9(S*aJ7!*|nnv zwN=*uD&PSFVi&gJO@EM{f?RfE4Bz66@HL?5d*hyc6r|+zWkzk%FNi;4*_tLw;*AtV zU$OOsloGAlgrwfC;R`zhc$`DHMV%V8q&}_MtjCYg`?OLt(V!-a7yq20Dm z&0A_OB@8(UnT>@|Q;{r(#I&;VNNFP@RUNLJn8QQ6W!V|F*v%h$E?-M!yK+$;W+y?}hw- zf4z?;c(0)^jHkW_r1S&;y#x%sd|0sAgG-`=FgR_>Ptu_VMFZU4z+g<2q@XBzmBfTT z2|Y#`6wnLxD(sFRLe&xCepu%H;O=rbh=dNmv?;%T?q5_vU#2<#fFc%uwEP>kzl_J9 za;?+&qUhvF`tUjAeZ44z0Bz(l5fK1#e9!6_((B$;?h>vZi12W4JmB?8WvJAKXmUoA z$xv%vP34DSD``l{;NTcG5ZUGE4UR}PA#e$A+(uk6sfjrKr0*OtE^mx=fUTOH3OWr` ziO$pUDjP~#MDS3}3H0^5x!ONuR&N{n%@4eANsBvT4-pK9!XiN{y!3HtE2N`Z&X=6V z0v-b^BT*v11*En`ueyMWNW~)jmgK<=qqB(SYGHn({^Oc>zxh8(r3EDMHFmG4rpX{e zls?TG2ECUh1--slW(WmbzV#J;Z>eG{#j8)3Zc3f0?bx9vq{H3&{j;Du^v?C)c*9uV z7ta-BomWzVXEKQbWSzB-kTI|fii0f`M9jTnFa2v7a5P8o#GvOs)mxgbr;p@fIONr` zE@d{vrsK`&cD?&J+aE6?+ig!<+BgFj{Ap}=k})|%N}QT8v&%V3@!f~Pn3=c%lQa=q zk__~UpQ`;M>uS!#1WRQe^j$OU5z3(dbOv-xt@_<^A zkpsi2HCEpnwC&vb#_sR7BpK$SxI;Dd2`~CqasN+JuCTYy8^*Fzmfnwazj zljN#Bu^0T;6`<;+{1V1mF0S`(Kkb=nY`;w2SMvKgpa^S~t{e+BK4iP?N|_)ci5y3E`D}IW%ZVYa3A8#NQeViW?FlKJCdtgPtyV+!E{5Z(|el{f%^8jrca)nq?JZp2LFgLZ=#9Jak^zf%W2W_6Lte}#Q#SFIG=Ef!h1++~=1 zN;zpuzWpbDRbsk0IEX0FR!rggbDIanT3P!T_{jayiiGD#ZI(arQ$_|xJyQ#kyYjT^ zC0k^tlDyc})K*RFeYK`LOc6Xnu|?aR?`LuxTc)bBMB3pjsn-XqJ}>CCD}}N?O4jS~ z`#mVjk)l6y40>H8M4u?V5Q0E#o90ldygQiZ%<9XGrJ*Y|1gq8YbyR7GXSl`Q|Ys zV~!4tmx)Vy^-Yr_7|=Q5Gh3ncOxf}o(b&%Qxk6$%@pcp_}`2h(QTzzbm zu3x<`Cj^3QJd^7z>A6@yY}ER@Kq1Uhp;47_{D@$h?l2O<4twbWCtA}Y3uI!EbF+sZ z#?r+E3Z^`akKE8ECZojda7e}p;8chiK}pMW8yE`+c1Ms*Ezz>#p#Z?NkN~v~l;@_F zd_$Az{sy^FAdwkyBVUx|b|SYge})(Q8pk@I`Gd{*HIE!jLQ^H-hJCS`of#bL>SZ{C zep1Ohnb%@O$$G08(M!)@t<7xvr|oznSj>;J9m8Z-+g&tZx1tH4%b9JScr01}vLD43 zC9%ir{v3ny6hB`I@)}@^YI@YRooKM8(i-$V7NGX9b_g$c#BjY{|6S!5Q=XRSMUVKl{ z2-hj5{`=4_bot{<bxow0G0KBFmCHr|655LD!7^fDg)( zEj{1rHWps?{GQvVo-epAy4@^{76_!=dD^OQcGE)(e{URoS1P%qz<__u9a(Bv<|TnN zxuUSlK)B<7^S1_a3EP^Oo! zo8GXs$C{FK+z4Dts&2GqdTS*D&ueB5Nd*6_^Dx`J(lTB@*>&V!6hgX;U4{}ZMawf8 ztX0^1HJCL%cSw3N6JksLkpjyVWP9EjidiOSfK3+5qGeVF#B`@P;?!50zy-Kcb7>Iz zM;kpC7dc-Nv6)HvC$zBWzpCZ+BdRpwR=1P zB?dHwxtOts3MMJW&$wc^?ua0$$L!<0y`eE1tMuq8^btkpeaIQ_se@m}WME56%GD7| zPto<4jwwBaJ#qf>;OPs=*N|T^cLx*?USDjJy@jf;p#vha4vIT*08u0a#jC4fCYC*x z+cw*8z0%=RhIUV2E`p%g6OaeWnjR*5%=BL5y-hUV<3*WCD;Y5sn9J>$Y56REozmw> z7{}0$jvYgJ?X?U!9^st#COZQAJklKXlx|n1D-|GV_R^E1M_+|xTbd@m5qUS{IZiKr zc=mi%{t?@+Ie+c_RsHT;HwiHfi{n4x@$vI2cZ&{&2qXM1{*Mf-y~Cb+^fMsANo3%v zl8&1kwq#><6Hre zg|bQ%B}$ld6Pheo6%wGWpL6)r!v&WTG}?n_?W?;{;qVCV?1Gnjd>ySh*OFT<8>-Py z=pc{2!&YK=^UI!So~S>uHvcL$wqUiE0Ap`Wnj)(HIcI5X6Tv!jHO%UjV8Ty*kR0YX z;EQFIl;&2K`PIIjgPEks;&(sBlGO|aCNZu@K);PpMIh@Mu-bKSk`4{G={T2t;^8=+ zIlY~mM1vpMnCjQ=`t_ip-S^!$020O(m>u<0%~BdO+@8fFXjUXNu|HM>G_M1uhhSCX z!2XZPwL#3h@_h%H`sA#|2Umzm?unE_lS(ox{1CK3CJb37cLefpI=-SzSa;}8rmHlU zmSFXA)AjU!bX8QY^aYu@TlTj{H_ko}`?T#VYToHCy*4c9X4l)v^bioKq2PR)XRGm-$OgPK@nDONnUc=OB>-?5-YT5eMfrpYZ-MuMk!$B}q8&TDn2Vfct zS;pn7GWanC;kjFf>FLR|P!{{53}Rvp5Y7GmU%BvTYg3hSEcK%cQsBl?mPEe&>H`B6 zX$x8nF9X78@#`0)5LdB2LLi0D`73!C3T_)0%AC4GvA&69Temp-%r;wMCUP-OKRcL? zz2Ur^HNxnPW->M-@z0i26-TRssDPdHW8SOL(%;3!o3neX(Z=mZSFXcz)Qs)>goUox z%MS6qEew}sA=Y$$x*H8`5zEVKQf6@TTEv_7j^aB^_;uC6Whk_@fk}(uUlr$ zRy|-j33jIOo4YOCK4F>Jmd(T9RWAeewaz*G;3+k|@TAs1v&Hz%WXl{}lBjXiJqM{n z&Bp8AKm{qUUNF}S?`nIOKIDwB*p-j9ljYAhwau82wrUrjF?|yKbimTTXwosJhuX8? zVlUt~5hI6$&#%;d&U57pMY1(|9P`2PyChWNzWpRbb%Ve-5n_+P@Cb> zRJOQwB?-FLF>YfXaFRF3VS|kZIxnJbL641#)4W0oz)a%vW3G_c$IwFLoN=5G8fz3o zq=eLPemF0eH^pf6x_(}zu#Sp)ME=-8)7+&eaM6eTi;H<}Kl5!eA3C_dna>~J^?gqq z2KM~27mBK@NUV{0O)YiBaM0avQ>g*vd$bk=v(ofE7iOf3yzI~GoTm2eat!sxdX>kfvcF& zR>+!4%FMwR<-bm4d4 zKG&QKcQ5wXZUM|mqT!=j*@sEnnPBd4+c1t8RBBX6S2Hzci+vMAte%Y+Ef;evjuPmX zyTLgI*Hs*aUYQgUr>WYCEY;Ro8xPsB;c8QBC^hHW<^%CjXr(!xp^hZSYnEI>?&hMS zmIP96l-Q(nA0b1l%RTNhr?Fvf)#E+iZQxnnHZg{DOV;gjapAj9@8R(;*P_U1=<0({ zWCBVpKmC5*<8i+hUsX@*t3u!TJXVI3AJXeIrV*oxa{_t?AxR`moa&%Q(KEsz5J;jC z8GaSW^aJ`C)!PhI)#}*$cgXt=Q2jq>w3w&C23k`Wq`(7!4-#j|ZZ88_g6^dwF94nu zaRZiTSkM*jOJ{wEJzow{y0 z>MwAkjpcW5y|Rf+=z|IaGg2$|PBYmOK__^4&~bcTbU=e&VZ2_wvEZt(c{Oga#^lO7 z(Mc-GpX97f(#?aR{R3WY9W4m?j>Z@M=F&~bmL9vTtoOWR?NGgisN?a2N2PO2O}0wj zJ}p$vTr#gNeHUDnmqWDo$1R_&ZarOnk!T@)dwEfIsLYkF);f&vD)7`@VcVUINkQeI zVFm6cL37$&xJGfCmFKt2?9{f$2oY)6DbWKop+Tw-p`I0o!>GwX0M7rG*q@~!-vD$~|?xNYo+cszNsP<@_|wtJQ#E{uMVkq^w=u&za?Sm>jY{hDZkni>*P%D6vnDcF>%Bt^jH}oLS-ykEHh!8z zGum5(bLWtgW2EnSpDrNJ`aAsngZe0O)F%GHZ2nF9<`X`qjrDi0Ib;nh@HsZSubo3p$Vz_rXs7t?@bP?8&f40uX03atB?*ou}D>{ z-E>u=U8ewpqHy*Y83;>BlY!Ab<$#RbDQV<_#gu@q#g@AB(v39R-|2k9pP=o^t@aSH z_H)Uf3d0p{PEwkDz*JT@DWv#XE?QGJ`L)p~=dxmFLTib(KgUJcw(t`!e(Tu zt+8y!N0ouaWT`_2C<(LQ(hOP63C09oZWCg-q?XA+p(mQ4>hN{Y?}|XOrjzA^=JN;b zF(RMu@;|(6QL7PJ>p$C2I!%6VE|4|uZ073pdpG2Q6mDZF9D}Ae{86B2&0~84DO42x z;yL~RfK*<7E<^eP-+Es3c=FpmG1=j;#w8abnSC4U(>JXrmy9w9GwH316&5|y5zZ@~ zv%#N+-Tb6{tUzCogRVz=zjA5QSIZJbTfHR|7mp9}Yw8`hr?TjV0X$$|D#{8L=nylC zc(SD+Q(e$Jm%q`4qx>0d_>l$a;5KY1K}N)rwDDQWomWTI>(S(9$dTfGRRd@Ap4=+e zrrDx?^VZ-_a#y3R^Ims-N~!C;a+g{8)*oH3pW^YypFe%Pzsol8dFG?FSnm;?xcTuQ zUtfnB{(NmM(;xrXlM6qvg$64K_w(>vRVX^y6wm_!-z+;Z`X0*K~s1Rc+Yt$7FEf`F#rks*O zTVR0L=t4C@IJuLDZZqt6LOFXI?$!Nkz$l&LkxmE*9Qf;YyG4rW* z=j;B0Da~_37r8yFthZ|4xmoM8ZU4ktT2=!xrCC|=>B52+?a!`X?3x_%zAx*k_tV%Y zd%e@N%c|YeZn+VcQK+@14JgCG;W; z3sB6Ck<{ggy`Jea3)}gaY>dxW_cE7G$mWpDWQeCokxSdplc$vDf&VuR}D-5SK>iy?#9HnJ8JAz|0sxT00ssjei;HShW?#Y;BvIy~2u?S^83vtDgi0cG zOQ(onL{7Xs(P=(p)lHXg%+?HM>X{OUNUL|^x?<@7J4lMe6%Q4P4(IAW`+_!xpepwE%r)1 zwcJh)^v0nrFwZz@p@x#DF@@bC31Ba8OUt4)%zUY0Q2lAHE1FgPsOyUmvyJQ(ea1@e zkZD7M3ik*3l=q4Kq@R=Q3|DqW0 z-}80CYJ}e{&b7~{ZQSlQ8yeVrYP-X@^^t1xb^DJmHmafZ&SmQXUfsz(_@AwO{?hZN zb2tjd+?^XIP!ntfASSJrOxyaGqDsZ z2~Ai8MXW1`f&wDof^9*mHdL&;^5zD&y?=i1^V#>;`@Hj+JNHaIcfRM$nKRdW!v;Tq z2H(i@kh3kqc6EI`hgyi*x+x)&mxxrLohpzQ0O@B@^7lEEH!|{1LPjDj{5?+(Xwjzs zNh%66GtGt_Jl1VpZ)0y}X=`R_VS$WT1Rw5qQJbQ<2`gY@6w+viG!o)?ku&2^N;7W~ zW3@jD&;TGN;!Ip5iF5fFJi|wCNS|?M<8wWRht_U=E02&iQevKgxP*}%Vw};L#{tNI zJ@gbTUqDwi;FDGa=4UQh0l92@SjR)^FpNJkXZsk~N>^RLAF{C1$b4q^QJ?Q@ABcf< zqXhmO#<`-*3VYa@XyfLSscb*0&%Z`77VN%mac(YO(Z)D;_fw13dzrHs*+rS|HXrf) z4fK?`Gct&Dx4cYcdMl3aLWf_VY<~{RZ=;vt`6l+Ni}(a;v@Xa4pyZ^n;H0rS24AZV zS^>b&e6csS=-)h`f3rq^mg?*vB>+%B74~xzh~4%-b;~CP<&)%Tc(07oRx3|d^5Jd( z`1oo#Z^5d_$3+96T4E=V1PUZEf|2a~s{9`+&>$TEMJR^4+RxEd=+wdH^wq}?{4Iru zxO(#85JSve-g>EWwuOS8vRn`FGt#vL7T44N0687U)I6zHY5%x*s3b_Uh*@L5pxEu~ zw)`W4^Y$afiFa-e7hB$e+{xk~jUw1@r1*MCkopX_q-I-ZkqJ__<<8jJVyza)Ew0>_ zSG4aeChGybIAUP;4Sv8S+#;VY+08VOmYvMCYNPzrF3%jESNx>8T*LA@X7$p_>ny)`o8G{U zFYQN)GVA<WrEE6geY{t6E?ajTGxaKbL_FaniTdKFt3SX>qH5a8^-aE4-mzN)F%%Va0fo@eEhc*rXpXWHUJL!M&x`$a{iFrexf@qrqu+{l#v}N z7&P8LWS38L&nNNnH*6D?6&8&Z9y5{2y8sN*1tCFcia?qoC`;Ktnw=k0C@PZ{ExlFx zys>KHKc!i611AEIM08Ccy0VDQEXc^wP1ius)&JR&=$t@lyGxx}W0%ef2xvgkjMMn0 z001;@p243eFEdRV3$KhqwTbB`bAKapoQ)l&xos|NB=AP?hbYT8nymP3rZ8(Ury zrNMeGsc|#eaJJ!=WftyiIDU`N7qeYTj1th>O~sw$#g)BmMiy3lILbtqrOATI%L14p z)RWEnv|0*Ga%2F=qt5PhoO6ac=UbWRsX*~opmr#HEB~7p?Jkw}))A&}lR!bQ9kpIN zX?-hGIuxi~3XZ;NIf9(HH@P8cv3_R+dz-O%i z7GG-!vUR{_H*KO?zT#~KH<6NOX6aQbDxXX5F5y{t{)Gh*Hqg5X0^klCI;q))e>LU9 zh7umeIb94JV(@?NA=smXY9PQkA6|UL+B4hGNvl-tuTg*Jpl8Nyh6lLC35`F$$xgsU zf%G9e5sV4;4+YKzAW%dS0I)Ww$QU~Me@Fims135wV6!n4c%z2$1WF{{tOX*aPRI08 z6kHALY>a%}8MnfSr;c;Jm{+t!Ky>eR^v3dvMz$T}U3=?@Ec;|k z5ysh<>Up@7Co{3|ep&)N8?9J&({uP-4N|cQ^%7u2bJvqr? z1VJgUNG6k2&2sX_!a2kGk~2p3p5)17<&9<-Ph`5}upnjR3uP0xtY{oKQ&@lqg$$W! ztkv-0^ucx!7a%1{8$?}GAI8eLB?og&a?W(|XU(CVFj$ICHwNGet`zHc%VM#C7=v#i z^0AD?B*|Ttj38#Q$hv$s(}Z+N$ciO%Ff6iEsLn7U9by4+KSKdNSw1nXOhA|e(k)z& zL)*(xjE%ZuzeI~AupB8q$g~`WVURmn51!^DVxc_bVKI?9d$L3x_Xt@`7%6!%$t3}g{tTMwE(2q5U&vC0yW(%=uMOOw$>-MeA^}w|Z3E08 zm)SKUA3aLv6da4pm*Djk~p6`TZ@sKFph z*s4$iN$lX98b-F|2vK}5IIcPI4&B(`P;VsVSfVC_Om6m%@+I?GET(CFU=H)dseBf* zvL`UtQY!8Z&Z%sP%ylQ%-3!jKoNE;4DLs2Xw!8)xwb8`14i}Z%tQrv=XIk1HJG({I zD1>6#6TH!)1@|x{M^ayuW4ZEUKZ|178w$YpnF_#Z_de?i;!)lG%JcBG+5qB4z(6B` zHUq6f%*wTgR-wsIJ;8=`a?U`Qi&&5R{=;IzIysle8vn*&nB=3#2;_=HmSoCXF-&8L z82jzUBozx0j>Ukhg^H5*BZ*;zi`1!}fXZW%v*0j^5H6W4(PpZV_}VNoS%PJ%8o)+a z$7ZUX;A=zUM6ibwFv(E6`gSll#?hDPaptZ&r8aNjj8}<|znY)ZK{+pCjol1zZ}0x%oAh94LTw6wa~*G0#~ZB9zrk}VolM^)&r-z@fzhL1af`OqC5xVi2D>xw?C@RiGREP&xRFvPb6;tNP z#{=Y!jnorvCMugRv$dkGT(-iVYH724*-EONjiaLr&B}U(wUx~(8%J9zmA+z`gUvD< z2TMmQhiNS37l!GD=vilAs;hL_zwc>haR1^}BNyuS&AwHb z_^5Jt!;7%~<-*yh(16)_m#f@X@0!4J-3-^OXUMd)j2?=Y@m#Um&6LHj72nq~r`|>X zK9(`@+OeGNz2x)ph1qD2umuIj8iUO)E}`+ISJY`6>nvh?&W=}3HR4SpoAB<74}KMI z{cY3pc#A6ddUw46IEJJ#27mdiqrDw%Ar_+ND+ZceW1rR?lzd(^d3X8>FScgSjYR&- zH0G;-H={eR4^3w`PO!r?3uv5zA-&I_kmZ)a1*PsXTleyw_wV1=I~RGK?jB3Rd%mBV z=&LShifD3M_hBk%dHcLz^)I`2n&m1a4MYPicP|Y|fdySRJwHg~cY*%2$t7k{){8%Beg;+qR`W zRCoa8E=8*><@7SE={P?9<2cR)ihpNoo@_l`9&iIQ^+pnYJ|etn+ZJC`Qiv(xRedmR zR~eSRo*>5F_jI{;7^8G`_X50W##a~b*c;O>-gg?g9p|_mDF&~s!&|pf*y}2Vm##UG zp%O~+(0w<-+xZQ&a^L2(>uXM>Sjxnr5AS%fllD8|nYjAccxD1XguH-48qGFZ@6Kfi z)!AA(fp3kgtJ6CvhE(mEkc1fC`|mcCe+t^TF|5CL;{KD63I+DF)Ru5gmF?C=B{$g_ zJ#K4{^)kw}f)A8;QSQWExKT0O7x&z_O6l_PSoW!v(z!cX$D7Pls@gI(8bNtNgBSJ2 zs~gW^d|T0eDz2_Sm-Z$lCBJj<8Ksqz_NcpX*v>t+r#-esJe~^kEBDt`&uX})%pTz# zZ5nqxW&KLYjmTb0QV?6MiNTZNHlAFw=VrC?+wij3v4o(sppLeiA4naC%HDjY->zyn z*!6Y5J;?{eh0P{BZp71eO^BS=yX_`$GhD(f1-!eS-Ld{s@8AQKPq%z5^GdGbjC*&C zy`%N>KGQ(xujnek%9;utR-oe8PlSpWm*Q=*E@fFDmv@|0cR20GT*yN7yx{@S=_}8^mNgvKU_cYiPE?Akg$CA@+wawo z*dt8BD6d;9EiljdG-q$HW4k5A%xc;xME*sTYdpyiy}Q&{;Ydj@UPqCR zQC#yH8uGlWhH9mA;m@Im-!%-p*m?oJV4+eLU-%(so_Tmv%7MZi)6+X59N(J!HW*>_ zusau$*dv3ytgkXIHEH?9@)u8HW?v&{z*J!q99m&vT^)Wgb!r^$uO|PgH_zG%%*)a2 zpapIm_&lsQQb=$!P*V4w-I7#vDR!W@VR88CdHGYTw!T@lm9@M+e9z+T4mLrLK{Tfh zbBxo)_}IJ9n0t%%^YD|=wShhl>j&yd=HbLwA6kxDZSR*p`{4b^{c_)d{?8FNQ0xIT z3slu-i7}HZF$^JVtwH+L&{s?O_{+iZr;5tG283)K>V3nnt*xmdDRaC}D-{~RB78E&N094kTOkl zwvXzFJ{!Gw?T4eueT;njPi1D4XT`BD*hO}gQ{!!dcdL_@8xnCFWz}TfxbNE?eZwZ> zGS2U0Uf*d~k7w$p%MR^hJ%T{yS%G9V7Ny;hVe}v%@nU~<+faCXon&w7Y*%_mtkShh z+lQ37tG5^QUi#8B()1`YI&2`XwXxoyrs`NL0i#Hk(2V@=1@C;{Bl&t_DZBbwcgKb$ zjwhz2X>Z<5KHu_*GnzKO;wT7>Sl}K3pR{yipA041hSeqX0c4)OVY zg8T7RUzgc^?c4ih{=P4ZbXC|P!+Vq=Qy?7bA}hjOEE|I7KE z4~-<-HOUXBubz7A?btI-dq(z%h~L!tuIIh-PO#f}wK~lP>?>^Z?ctvukpCa8j<~#Q z!4K=s_2(g8UcLb)BOeT=Jw_U!^FKdpN0=i|T%EIIU(!Y^NZKsbx>z6Iv83e>xkrK$ z;4VDlBI25wnVFbFcR#Zg4r|RlUSJ+!W`>Rn5g&L}A7hl3bI{6KM2Ll%nDy?fe@D23b%S%7!;8-&W&1g@?f_EXjcY(XDMW1Ws@`q&rL*}o-5ATb^``>I+HE~e zPj;dVx}FMS-c1e-O(v(VOYNbh-GW8n@ofE_U7YWRf1d^iIZxu$^oJIEK5#F)y1sr& z^kJZ{L8>=ZgZtfnoRWCS^gzw~eg57q>nQC!LlyuoE1P-dotQ@JHkfoJbhtT}A2l6gFwREW0zB`!IEB5JM+Y=~iO3 zX6=sDo!di0d+xU#y+?Xiw7}VW|LTy&#J=yhzPNv6)z`k7*nPm^dyg;dhT9%0&#iCk zb+7D+KRlMcgu61xuVL|CE1`4k3iJ`lxi5|2o0jz5OK}US-;E6Zw#*t%E|CdL^wkADikw0 Kp}h%ND*pxJyaK%d diff --git a/src/sound/call_declined.mp3 b/src/sound/call_declined.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0d0c23359961c17878cf8571c6ec0ce8ec09200d GIT binary patch literal 8601 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 literal 0 HcmV?d00001 diff --git a/src/sound/call_declined.ogg b/src/sound/call_declined.ogg new file mode 100644 index 0000000000000000000000000000000000000000..458fcd2641094230f64371d0d35eef3e1ef902f4 GIT binary patch literal 11597 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 aBYMPfHd&v~JDo}E;p<=7qDAQ7LjMI2{gN>N literal 0 HcmV?d00001 diff --git a/src/sound/ringtone.mp3 b/src/sound/ringtone.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..d4c73adb2031bce41f824dc7870332f73a40ce78 GIT binary patch literal 7593 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 d&oR=vln&-nlV literal 0 HcmV?d00001 diff --git a/src/state/CallViewModel.ts b/src/state/CallViewModel.ts index 3802d6dd..ca797aa8 100644 --- a/src/state/CallViewModel.ts +++ b/src/state/CallViewModel.ts @@ -49,6 +49,7 @@ import { race, scan, skip, + skipWhile, startWith, switchAll, switchMap, @@ -853,17 +854,6 @@ export class CallViewModel extends ViewModel { throttleTime(THROTTLE_SOUND_EFFECT_MS), ); - public readonly leaveSoundEffect$ = this.userMedia$.pipe( - pairwise(), - filter( - ([prev, current]) => - current.length <= MAX_PARTICIPANT_COUNT_FOR_SOUND && - current.length < prev.length, - ), - map(() => {}), - throttleTime(THROTTLE_SOUND_EFFECT_MS), - ); - /** * The number of participants currently in the call. * @@ -963,7 +953,9 @@ export class CallViewModel extends ViewModel { * - "success": Someone else joined. The call is in a normal state. No audiovisual feedback. * - null: EC is configured to never show any waiting for answer state. */ - public readonly callPickupState$ = this.options.waitForCallPickup + public readonly callPickupState$: Behavior< + "unknown" | "ringing" | "timeout" | "decline" | "success" | null + > = this.options.waitForCallPickup ? this.scope.behavior< "unknown" | "ringing" | "timeout" | "decline" | "success" >( @@ -983,6 +975,24 @@ export class CallViewModel extends ViewModel { ) : constant(null); + public readonly leaveSoundEffect$ = combineLatest([ + this.callPickupState$, + this.userMedia$, + ]).pipe( + // Until the call is successful, do not play a leave sound. + // If callPickupState$ is null, then we always play the sound as it will not conflict with a decline sound. + skipWhile(([c]) => c !== null && c !== "success"), + map(([, userMedia]) => userMedia), + pairwise(), + filter( + ([prev, current]) => + current.length <= MAX_PARTICIPANT_COUNT_FOR_SOUND && + current.length < prev.length, + ), + map(() => {}), + throttleTime(THROTTLE_SOUND_EFFECT_MS), + ); + /** * List of MediaItems that we want to display, that are of type ScreenShare */ diff --git a/src/useAudioContext.tsx b/src/useAudioContext.tsx index 1b00e82b..59334dda 100644 --- a/src/useAudioContext.tsx +++ b/src/useAudioContext.tsx @@ -32,6 +32,8 @@ async function playSound( buffer: AudioBuffer, volume: number, stereoPan: number, + delayS = 0, + abort?: AbortController, ): Promise { const gain = ctx.createGain(); gain.gain.setValueAtTime(volume, 0); @@ -39,13 +41,62 @@ async function playSound( pan.pan.setValueAtTime(stereoPan, 0); const src = ctx.createBufferSource(); src.buffer = buffer; - src.connect(gain).connect(pan).connect(ctx.destination); + abort?.signal.addEventListener("abort", () => { + src.disconnect(); + }); const p = new Promise((r) => src.addEventListener("ended", () => r())); + src.connect(gain).connect(pan).connect(ctx.destination); controls.setPlaybackStarted(); - src.start(); + src.start(ctx.currentTime + delayS); return p; } +/** + * Play a sound though a given AudioContext, looping until stopped. Will take + * care of connecting the correct buffer and gating + * through gain. + * @param volume The volume to play at. + * @param ctx The context to play through. + * @param buffer The buffer to play. + * @returns A function used to end the sound. This function will return a promise when the sound has stopped. + */ +function playSoundLooping( + ctx: AudioContext, + buffer: AudioBuffer, + volume: number, + stereoPan: number, + delayS?: number, +): () => Promise { + if (delayS === 0) { + throw Error("Looping sounds must have a delay"); + } + + // Our audio loop + let lastSoundPromise: Promise; + let nextSoundPromise: Promise; + let ac: AbortController | undefined; + void (async (): Promise => { + ac = new AbortController(); + // Play a sound immediately + lastSoundPromise = Promise.resolve(); + do { + // Queue up the next sound. + nextSoundPromise = playSound(ctx, buffer, volume, stereoPan, delayS, ac); + // Await the previous sound. + await lastSoundPromise; + // Swap the promises over, and loop round to play the next sound. + lastSoundPromise = nextSoundPromise; + } while (!ac.signal.aborted); + })(); + + return async () => { + ac?.abort(); + // Wait for sounds to finish. + await lastSoundPromise; + await nextSoundPromise; + }; +} + interface Props { /** * The sounds to play. If no sounds should be played then @@ -57,8 +108,13 @@ interface Props { muted?: boolean; } -interface UseAudioContext { +interface UseAudioContext { playSound(soundName: S): Promise; + playSoundLooping(soundName: S, delayS?: number): () => Promise; + /** + * Map of sound name to duration in seconds. + */ + soundDuration: Record; } /** @@ -146,5 +202,23 @@ export function useAudioContext( earpiecePan, ); }, + playSoundLooping: (name, delayS: number): (() => Promise) => { + if (!audioBuffers[name]) { + throw Error(`Tried to play a sound that wasn't buffered (${name})`); + } + return playSoundLooping( + audioContext, + audioBuffers[name], + soundEffectVolume * earpieceVolume, + earpiecePan, + delayS, + ); + }, + soundDuration: Object.fromEntries( + Object.entries(audioBuffers).map(([k, v]) => [ + k, + (v as AudioBuffer).duration, + ]), + ), }; } diff --git a/src/utils/test-viewmodel.ts b/src/utils/test-viewmodel.ts index e5558ae2..09044e3f 100644 --- a/src/utils/test-viewmodel.ts +++ b/src/utils/test-viewmodel.ts @@ -22,7 +22,10 @@ import { } from "matrix-js-sdk"; import { E2eeType } from "../e2ee/e2eeType"; -import { CallViewModel } from "../state/CallViewModel"; +import { + CallViewModel, + type CallViewModelOptions, +} from "../state/CallViewModel"; import { mockLivekitRoom, mockMatrixRoom, @@ -122,6 +125,7 @@ export function getBasicRTCSession( export function getBasicCallViewModelEnvironment( members: RoomMember[], initialRtcMemberships: CallMembership[] = [localRtcMember, aliceRtcMember], + callViewModelOptions: Partial = {}, ): { vm: CallViewModel; rtcMemberships$: BehaviorSubject; @@ -148,6 +152,7 @@ export function getBasicCallViewModelEnvironment( mockMediaDevices({}), { encryptionSystem: { kind: E2eeType.PER_PARTICIPANT }, + ...callViewModelOptions, }, of(ConnectionState.Connected), handRaisedSubject$,