mirror of
https://github.com/vector-im/element-call.git
synced 2026-03-19 06:20:25 +00:00
Update to delay ringtone logic.
This commit is contained in:
@@ -356,6 +356,16 @@ export const InCallView: FC<InCallViewProps> = ({
|
||||
const audioOutputSwitcher = useBehavior(vm.audioOutputSwitcher$);
|
||||
useSubscription(vm.autoLeave$, onLeave);
|
||||
|
||||
const ringDelay = (pickupPhaseAudio?.soundDuration["waiting"] ?? 1) * 2;
|
||||
|
||||
useEffect(() => {
|
||||
window.document.body.style.setProperty(
|
||||
"--call-ring-duration-s",
|
||||
`${ringDelay}s`,
|
||||
);
|
||||
window.document.body.style.setProperty("--call-ring-delay-s", `1s`);
|
||||
}, [pickupPhaseAudio?.soundDuration, ringDelay]);
|
||||
|
||||
// When we enter timeout or decline we will leave the call.
|
||||
useEffect((): void | (() => void) => {
|
||||
if (callPickupState === "timeout") {
|
||||
@@ -384,13 +394,16 @@ export const InCallView: FC<InCallViewProps> = ({
|
||||
// When waiting for pickup, loop a waiting sound
|
||||
useEffect((): void | (() => void) => {
|
||||
if (callPickupState !== "ringing" || !pickupPhaseAudio) return;
|
||||
const endSound = pickupPhaseAudio.playSoundLooping("waiting");
|
||||
const endSound = pickupPhaseAudio.playSoundLooping(
|
||||
"waiting",
|
||||
ringDelay / 2,
|
||||
);
|
||||
return () => {
|
||||
void endSound().catch((e) => {
|
||||
logger.error("Failed to stop ringing sound", e);
|
||||
});
|
||||
};
|
||||
}, [callPickupState, pickupPhaseAudio]);
|
||||
}, [callPickupState, pickupPhaseAudio, ringDelay]);
|
||||
|
||||
// Waiting UI overlay
|
||||
const waitingOverlay: JSX.Element | null = useMemo(() => {
|
||||
|
||||
@@ -25,10 +25,12 @@
|
||||
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 {
|
||||
.pulse::before .text {
|
||||
color: var(--cpd-color-text-on-solid-primary);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ async function playSound(
|
||||
buffer: AudioBuffer,
|
||||
volume: number,
|
||||
stereoPan: number,
|
||||
delayS = 0,
|
||||
abort?: AbortController,
|
||||
): Promise<void> {
|
||||
const gain = ctx.createGain();
|
||||
gain.gain.setValueAtTime(volume, 0);
|
||||
@@ -39,10 +41,13 @@ 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<void>((r) => src.addEventListener("ended", () => r()));
|
||||
src.connect(gain).connect(pan).connect(ctx.destination);
|
||||
controls.setPlaybackStarted();
|
||||
src.start();
|
||||
src.start(ctx.currentTime + delayS);
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -60,23 +65,35 @@ function playSoundLooping(
|
||||
buffer: AudioBuffer,
|
||||
volume: number,
|
||||
stereoPan: number,
|
||||
delayS?: number,
|
||||
): () => Promise<void> {
|
||||
const gain = ctx.createGain();
|
||||
gain.gain.setValueAtTime(volume, 0);
|
||||
const pan = ctx.createStereoPanner();
|
||||
pan.pan.setValueAtTime(stereoPan, 0);
|
||||
const src = ctx.createBufferSource();
|
||||
src.buffer = buffer;
|
||||
src.connect(gain).connect(pan).connect(ctx.destination);
|
||||
controls.setPlaybackStarted();
|
||||
src.loop = true;
|
||||
src.start();
|
||||
if (delayS === 0) {
|
||||
throw Error("Looping sounds must have a delay");
|
||||
}
|
||||
|
||||
// Our audio loop
|
||||
let lastSoundPromise: Promise<void>;
|
||||
let nextSoundPromise: Promise<void>;
|
||||
let ac: AbortController | undefined;
|
||||
void (async () => {
|
||||
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 () => {
|
||||
const p = new Promise<void>((r) =>
|
||||
src.addEventListener("ended", () => r()),
|
||||
);
|
||||
src.stop();
|
||||
return p;
|
||||
ac?.abort();
|
||||
// Wait for sounds to finish.
|
||||
await lastSoundPromise;
|
||||
await nextSoundPromise;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -91,9 +108,13 @@ interface Props<S extends string> {
|
||||
muted?: boolean;
|
||||
}
|
||||
|
||||
interface UseAudioContext<S> {
|
||||
interface UseAudioContext<S extends string> {
|
||||
playSound(soundName: S): Promise<void>;
|
||||
playSoundLooping(soundName: S): () => Promise<void>;
|
||||
playSoundLooping(soundName: S, delayS?: number): () => Promise<void>;
|
||||
/**
|
||||
* Map of sound name to duration in seconds.
|
||||
*/
|
||||
soundDuration: Record<string, number>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -181,7 +202,7 @@ export function useAudioContext<S extends string>(
|
||||
earpiecePan,
|
||||
);
|
||||
},
|
||||
playSoundLooping: (name): (() => Promise<void>) => {
|
||||
playSoundLooping: (name, delayS: number): (() => Promise<void>) => {
|
||||
if (!audioBuffers[name]) {
|
||||
throw Error(`Tried to play a sound that wasn't buffered (${name})`);
|
||||
}
|
||||
@@ -190,7 +211,14 @@ export function useAudioContext<S extends string>(
|
||||
audioBuffers[name],
|
||||
soundEffectVolume * earpieceVolume,
|
||||
earpiecePan,
|
||||
delayS,
|
||||
);
|
||||
},
|
||||
soundDuration: Object.fromEntries(
|
||||
Object.entries(audioBuffers).map(([k, v]) => [
|
||||
k,
|
||||
(v as AudioBuffer).duration,
|
||||
]),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user