mirror of
https://github.com/vector-im/element-call.git
synced 2026-08-29 21:15:19 +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$);
|
const audioOutputSwitcher = useBehavior(vm.audioOutputSwitcher$);
|
||||||
useSubscription(vm.autoLeave$, onLeave);
|
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.
|
// When we enter timeout or decline we will leave the call.
|
||||||
useEffect((): void | (() => void) => {
|
useEffect((): void | (() => void) => {
|
||||||
if (callPickupState === "timeout") {
|
if (callPickupState === "timeout") {
|
||||||
@@ -384,13 +394,16 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
// When waiting for pickup, loop a waiting sound
|
// When waiting for pickup, loop a waiting sound
|
||||||
useEffect((): void | (() => void) => {
|
useEffect((): void | (() => void) => {
|
||||||
if (callPickupState !== "ringing" || !pickupPhaseAudio) return;
|
if (callPickupState !== "ringing" || !pickupPhaseAudio) return;
|
||||||
const endSound = pickupPhaseAudio.playSoundLooping("waiting");
|
const endSound = pickupPhaseAudio.playSoundLooping(
|
||||||
|
"waiting",
|
||||||
|
ringDelay / 2,
|
||||||
|
);
|
||||||
return () => {
|
return () => {
|
||||||
void endSound().catch((e) => {
|
void endSound().catch((e) => {
|
||||||
logger.error("Failed to stop ringing sound", e);
|
logger.error("Failed to stop ringing sound", e);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}, [callPickupState, pickupPhaseAudio]);
|
}, [callPickupState, pickupPhaseAudio, ringDelay]);
|
||||||
|
|
||||||
// Waiting UI overlay
|
// Waiting UI overlay
|
||||||
const waitingOverlay: JSX.Element | null = useMemo(() => {
|
const waitingOverlay: JSX.Element | null = useMemo(() => {
|
||||||
|
|||||||
@@ -25,10 +25,12 @@
|
|||||||
inset: -12px;
|
inset: -12px;
|
||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
border: 12px solid rgba(255, 255, 255, 0.6);
|
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);
|
color: var(--cpd-color-text-on-solid-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,13 +38,24 @@
|
|||||||
0% {
|
0% {
|
||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
|
transform: scale(0);
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
70% {
|
35% {
|
||||||
transform: scale(1.15);
|
transform: scale(1.15);
|
||||||
opacity: 0.15;
|
opacity: 0.15;
|
||||||
}
|
}
|
||||||
100% {
|
50% {
|
||||||
transform: scale(1.2);
|
transform: scale(1.2);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
50.01% {
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
85% {
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ async function playSound(
|
|||||||
buffer: AudioBuffer,
|
buffer: AudioBuffer,
|
||||||
volume: number,
|
volume: number,
|
||||||
stereoPan: number,
|
stereoPan: number,
|
||||||
|
delayS = 0,
|
||||||
|
abort?: AbortController,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const gain = ctx.createGain();
|
const gain = ctx.createGain();
|
||||||
gain.gain.setValueAtTime(volume, 0);
|
gain.gain.setValueAtTime(volume, 0);
|
||||||
@@ -39,10 +41,13 @@ async function playSound(
|
|||||||
pan.pan.setValueAtTime(stereoPan, 0);
|
pan.pan.setValueAtTime(stereoPan, 0);
|
||||||
const src = ctx.createBufferSource();
|
const src = ctx.createBufferSource();
|
||||||
src.buffer = buffer;
|
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()));
|
const p = new Promise<void>((r) => src.addEventListener("ended", () => r()));
|
||||||
|
src.connect(gain).connect(pan).connect(ctx.destination);
|
||||||
controls.setPlaybackStarted();
|
controls.setPlaybackStarted();
|
||||||
src.start();
|
src.start(ctx.currentTime + delayS);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,23 +65,35 @@ function playSoundLooping(
|
|||||||
buffer: AudioBuffer,
|
buffer: AudioBuffer,
|
||||||
volume: number,
|
volume: number,
|
||||||
stereoPan: number,
|
stereoPan: number,
|
||||||
|
delayS?: number,
|
||||||
): () => Promise<void> {
|
): () => Promise<void> {
|
||||||
const gain = ctx.createGain();
|
if (delayS === 0) {
|
||||||
gain.gain.setValueAtTime(volume, 0);
|
throw Error("Looping sounds must have a delay");
|
||||||
const pan = ctx.createStereoPanner();
|
}
|
||||||
pan.pan.setValueAtTime(stereoPan, 0);
|
|
||||||
const src = ctx.createBufferSource();
|
// Our audio loop
|
||||||
src.buffer = buffer;
|
let lastSoundPromise: Promise<void>;
|
||||||
src.connect(gain).connect(pan).connect(ctx.destination);
|
let nextSoundPromise: Promise<void>;
|
||||||
controls.setPlaybackStarted();
|
let ac: AbortController | undefined;
|
||||||
src.loop = true;
|
void (async () => {
|
||||||
src.start();
|
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 () => {
|
return async () => {
|
||||||
const p = new Promise<void>((r) =>
|
ac?.abort();
|
||||||
src.addEventListener("ended", () => r()),
|
// Wait for sounds to finish.
|
||||||
);
|
await lastSoundPromise;
|
||||||
src.stop();
|
await nextSoundPromise;
|
||||||
return p;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,9 +108,13 @@ interface Props<S extends string> {
|
|||||||
muted?: boolean;
|
muted?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UseAudioContext<S> {
|
interface UseAudioContext<S extends string> {
|
||||||
playSound(soundName: S): Promise<void>;
|
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,
|
earpiecePan,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
playSoundLooping: (name): (() => Promise<void>) => {
|
playSoundLooping: (name, delayS: number): (() => Promise<void>) => {
|
||||||
if (!audioBuffers[name]) {
|
if (!audioBuffers[name]) {
|
||||||
throw Error(`Tried to play a sound that wasn't buffered (${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],
|
audioBuffers[name],
|
||||||
soundEffectVolume * earpieceVolume,
|
soundEffectVolume * earpieceVolume,
|
||||||
earpiecePan,
|
earpiecePan,
|
||||||
|
delayS,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
soundDuration: Object.fromEntries(
|
||||||
|
Object.entries(audioBuffers).map(([k, v]) => [
|
||||||
|
k,
|
||||||
|
(v as AudioBuffer).duration,
|
||||||
|
]),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user