diff --git a/apps/dispatch/app/_components/Audio.tsx b/apps/dispatch/app/_components/Audio/Audio.tsx similarity index 97% rename from apps/dispatch/app/_components/Audio.tsx rename to apps/dispatch/app/_components/Audio/Audio.tsx index 684f1e41..df531c20 100644 --- a/apps/dispatch/app/_components/Audio.tsx +++ b/apps/dispatch/app/_components/Audio/Audio.tsx @@ -21,11 +21,9 @@ import { ROOMS } from "_data/livekitRooms"; import { useDispatchConnectionStore } from "_store/dispatch/connectionStore"; import { useSession } from "next-auth/react"; import { dispatchSocket } from "dispatch/socket"; +import { useSounds } from "_components/Audio/useSounds"; export const Audio = () => { - const connection = usePilotConnectionStore(); - const [showSource, setShowSource] = useState(false); - const { speakingParticipants, isTalking, @@ -40,6 +38,11 @@ export const Audio = () => { removeMessage, } = useAudioStore(); const [selectedRoom, setSelectedRoom] = useState("LST_01"); + useSounds({ + isReceiving: speakingParticipants.length > 0, + isTransmitting: isTalking, + unpausedTracks: speakingParticipants, + }); const { selectedStation, status: pilotState } = usePilotConnectionStore((state) => state); diff --git a/apps/dispatch/app/_components/Audio/useSounds.ts b/apps/dispatch/app/_components/Audio/useSounds.ts new file mode 100644 index 00000000..e1b1600b --- /dev/null +++ b/apps/dispatch/app/_components/Audio/useSounds.ts @@ -0,0 +1,90 @@ +"use client"; +import { useDebounce } from "_helpers/useDebounce"; +import { useEffect, useRef, useState } from "react"; + +export const useSounds = ({ + isReceiving, + isTransmitting, + unpausedTracks, +}: { + isReceiving: boolean; + isTransmitting: boolean; + unpausedTracks: unknown[]; +}) => { + // Sounds as refs + const connectionStart = useRef(null); + const connectionEnd = useRef(null); + const ownCallStarted = useRef(null); + const foreignCallStop = useRef(null); + const foreignCallBlocked = useRef(null); + const callToLong = useRef(null); + const adminCall = useRef(null); + + useEffect(() => { + if (!window) return; + connectionStart.current = new Audio("/sounds/connection_started_sepura.mp3"); + connectionEnd.current = new Audio("/sounds/connection_stoped_sepura.mp3"); + ownCallStarted.current = new Audio("/sounds/call_end_sepura.wav"); + foreignCallStop.current = new Audio("/sounds/call_end_sepura.wav"); + foreignCallBlocked.current = new Audio("/sounds/call_blocked_sepura.wav"); + callToLong.current = new Audio("/sounds/call_to_long.wav"); + adminCall.current = new Audio("/sounds/call_interrupted_by_admin.mp3"); + }, []); + + const [soundConnectionStarted, setSoundsConnectionStarted] = useState(false); + + useDebounce( + () => { + if (!isReceiving && !isTransmitting && soundConnectionStarted) { + setSoundsConnectionStarted(false); + connectionEnd.current!.currentTime = 0; + connectionEnd.current!.play(); + } + }, + 3000, + [unpausedTracks, isReceiving, isTransmitting], + ); + + useEffect(() => { + if ((isReceiving || isTransmitting) && !soundConnectionStarted) { + setSoundsConnectionStarted(true); + connectionStart.current!.currentTime = 0; + connectionStart.current!.play(); + ownCallStarted.current!.pause(); + } + }, [isReceiving, isTransmitting, soundConnectionStarted]); + + useEffect(() => { + if (isTransmitting && connectionStart.current!.paused) { + ownCallStarted.current!.volume = 0.2; + ownCallStarted.current!.currentTime = 0; + ownCallStarted.current!.play(); + } + }, [isTransmitting]); + + useEffect(() => { + if (!isReceiving) { + foreignCallStop.current!.volume = 0.2; + foreignCallStop.current!.currentTime = 0; + foreignCallStop.current!.play().catch(() => {}); + } + }, [isReceiving]); + + // Hotmic warning after 30 seconds + useEffect(() => { + if (isTransmitting) { + const timeout = setTimeout(() => { + if (isTransmitting) { + callToLong.current!.loop = true; + callToLong.current!.currentTime = 0; + callToLong.current!.volume = 1; + callToLong.current!.play(); + } + }, 25000); + return () => { + clearTimeout(timeout); + callToLong.current!.pause(); + }; + } + }, [isTransmitting]); +}; diff --git a/apps/dispatch/app/_components/left/SituationBoard.tsx b/apps/dispatch/app/_components/left/SituationBoard.tsx index e254efb2..d2a183e8 100644 --- a/apps/dispatch/app/_components/left/SituationBoard.tsx +++ b/apps/dispatch/app/_components/left/SituationBoard.tsx @@ -34,7 +34,7 @@ export const SituationBoard = () => {

- Stations + Stationen

diff --git a/apps/dispatch/app/_helpers/useDebounce.ts b/apps/dispatch/app/_helpers/useDebounce.ts new file mode 100644 index 00000000..f6d35a8b --- /dev/null +++ b/apps/dispatch/app/_helpers/useDebounce.ts @@ -0,0 +1,8 @@ +import { DependencyList, useEffect } from "react"; +import useTimeout from "./useTimeout"; + +export const useDebounce = (callback: () => void, delay: number, dependencies: DependencyList) => { + const { reset, clear } = useTimeout(callback, delay); + useEffect(reset, [...dependencies, reset]); + useEffect(() => clear, [clear]); +}; diff --git a/apps/dispatch/app/_helpers/useTimeout.ts b/apps/dispatch/app/_helpers/useTimeout.ts new file mode 100644 index 00000000..b71734d9 --- /dev/null +++ b/apps/dispatch/app/_helpers/useTimeout.ts @@ -0,0 +1,30 @@ +import { useCallback, useEffect, useRef } from "react"; + +export default function useTimeout(callback: () => void, delay: number) { + const callbackRef = useRef(callback); + const timeoutRef = useRef(null); + + useEffect(() => { + callbackRef.current = callback; + }, [callback]); + + const set = useCallback(() => { + timeoutRef.current = setTimeout(() => callbackRef.current(), delay); + }, [delay]); + + const clear = useCallback(() => { + if (timeoutRef.current) clearTimeout(timeoutRef.current); + }, []); + + useEffect(() => { + set(); + return clear; + }, [delay, set, clear]); + + const reset = useCallback(() => { + clear(); + set(); + }, [clear, set]); + + return { reset, clear, set }; +} diff --git a/apps/dispatch/app/dispatch/_components/navbar/Navbar.tsx b/apps/dispatch/app/dispatch/_components/navbar/Navbar.tsx index f919d210..f7e8ad49 100644 --- a/apps/dispatch/app/dispatch/_components/navbar/Navbar.tsx +++ b/apps/dispatch/app/dispatch/_components/navbar/Navbar.tsx @@ -2,7 +2,7 @@ import { Connection } from "./_components/Connection"; /* import { ThemeSwap } from "./_components/ThemeSwap"; */ -import { Audio } from "../../../_components/Audio"; +import { Audio } from "../../../_components/Audio/Audio"; /* import { useState } from "react"; */ import { ExitIcon, ExternalLinkIcon } from "@radix-ui/react-icons"; import Link from "next/link"; diff --git a/apps/dispatch/app/pilot/_components/navbar/Navbar.tsx b/apps/dispatch/app/pilot/_components/navbar/Navbar.tsx index c6e08d5c..a8376f12 100644 --- a/apps/dispatch/app/pilot/_components/navbar/Navbar.tsx +++ b/apps/dispatch/app/pilot/_components/navbar/Navbar.tsx @@ -2,7 +2,7 @@ import { Connection } from "./_components/Connection"; /* import { ThemeSwap } from "./ThemeSwap"; */ -import { Audio } from "../../../_components/Audio"; +import { Audio } from "../../../_components/Audio/Audio"; /* import { useState } from "react"; */ import { ExitIcon, ExternalLinkIcon } from "@radix-ui/react-icons"; import Link from "next/link"; diff --git a/apps/dispatch/public/sounds/call_blocked_sepura.wav b/apps/dispatch/public/sounds/call_blocked_sepura.wav new file mode 100644 index 00000000..663d0189 Binary files /dev/null and b/apps/dispatch/public/sounds/call_blocked_sepura.wav differ diff --git a/apps/dispatch/public/sounds/call_end_sepura.wav b/apps/dispatch/public/sounds/call_end_sepura.wav new file mode 100644 index 00000000..27acc92e Binary files /dev/null and b/apps/dispatch/public/sounds/call_end_sepura.wav differ diff --git a/apps/dispatch/public/sounds/call_interrupted_by_admin.mp3 b/apps/dispatch/public/sounds/call_interrupted_by_admin.mp3 new file mode 100644 index 00000000..34109754 Binary files /dev/null and b/apps/dispatch/public/sounds/call_interrupted_by_admin.mp3 differ diff --git a/apps/dispatch/public/sounds/call_to_long.wav b/apps/dispatch/public/sounds/call_to_long.wav new file mode 100644 index 00000000..b10357c7 Binary files /dev/null and b/apps/dispatch/public/sounds/call_to_long.wav differ diff --git a/apps/dispatch/public/sounds/connection_started_sepura.mp3 b/apps/dispatch/public/sounds/connection_started_sepura.mp3 new file mode 100644 index 00000000..16789886 Binary files /dev/null and b/apps/dispatch/public/sounds/connection_started_sepura.mp3 differ diff --git a/apps/dispatch/public/sounds/connection_stoped_sepura.mp3 b/apps/dispatch/public/sounds/connection_stoped_sepura.mp3 new file mode 100644 index 00000000..a31c9302 Binary files /dev/null and b/apps/dispatch/public/sounds/connection_stoped_sepura.mp3 differ