From db21327c8146ebea7326e7df8ed29a30609c82a1 Mon Sep 17 00:00:00 2001 From: Saelmala Date: Mon, 9 Dec 2024 09:35:43 +0100 Subject: [PATCH 01/26] feat: control volume of line --- src/assets/icons/full_sound.svg | 1 + src/assets/icons/icon.tsx | 6 ++ src/assets/icons/no_sound.svg | 1 + .../production-line/production-line.tsx | 13 +++ .../production-line/use-control-volume.tsx | 54 +++++++++++ .../production-line/use-rtc-connection.ts | 9 +- src/components/slider/slider.tsx | 97 +++++++++++++++++++ 7 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 src/assets/icons/full_sound.svg create mode 100644 src/assets/icons/no_sound.svg create mode 100644 src/components/production-line/use-control-volume.tsx create mode 100644 src/components/slider/slider.tsx diff --git a/src/assets/icons/full_sound.svg b/src/assets/icons/full_sound.svg new file mode 100644 index 00000000..75c25155 --- /dev/null +++ b/src/assets/icons/full_sound.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/icon.tsx b/src/assets/icons/icon.tsx index 98af7638..0b2ea03a 100644 --- a/src/assets/icons/icon.tsx +++ b/src/assets/icons/icon.tsx @@ -9,6 +9,8 @@ import ConfirmSvg from "./done.svg?react"; import StepLeftSvg from "./chevron_left.svg?react"; import StepRightSvg from "./navigate_next.svg?react"; import Settings from "./settings.svg?react"; +import NoSound from "./no_sound.svg?react"; +import FullSound from "./full_sound.svg?react"; export const MicMuted = () => ; @@ -31,3 +33,7 @@ export const StepLeftIcon = () => ; export const StepRightIcon = () => ; export const SettingsIcon = () => ; + +export const NoSoundIcon = () => ; + +export const FullSoundIcon = () => ; diff --git a/src/assets/icons/no_sound.svg b/src/assets/icons/no_sound.svg new file mode 100644 index 00000000..ddf48824 --- /dev/null +++ b/src/assets/icons/no_sound.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/production-line/production-line.tsx b/src/components/production-line/production-line.tsx index d186ebe5..0d34f4a2 100644 --- a/src/components/production-line/production-line.tsx +++ b/src/components/production-line/production-line.tsx @@ -27,10 +27,12 @@ import { useLinePolling } from "./use-line-polling.ts"; import { useFetchProduction } from "../landing-page/use-fetch-production.ts"; import { useIsLoading } from "./use-is-loading.ts"; import { useCheckBadLineData } from "./use-check-bad-line-data.ts"; +import { useControlVolume } from "./use-control-volume.tsx"; import { NavigateToRootButton } from "../navigate-to-root-button/navigate-to-root-button.tsx"; import { useAudioCue } from "./use-audio-cue.ts"; import { DisplayWarning } from "../display-box.tsx"; import { SettingsModal, Hotkeys } from "./settings-modal.tsx"; +import { Slider } from "../slider/slider.tsx"; const TempDiv = styled.div` padding: 0 0 2rem 0; @@ -144,6 +146,10 @@ export const ProductionLine: FC = () => { inputId: joinProductionOptions?.audioinput ?? null, }); + const { setVolume } = useControlVolume({ + stream: inputAudioStream !== "no-device" ? inputAudioStream : null, + }); + const muteInput = useCallback( (mute: boolean) => { if (inputAudioStream && inputAudioStream !== "no-device") { @@ -312,6 +318,13 @@ export const ProductionLine: FC = () => { Controls + muteOutput()}> diff --git a/src/components/production-line/use-control-volume.tsx b/src/components/production-line/use-control-volume.tsx new file mode 100644 index 00000000..d47ad2a4 --- /dev/null +++ b/src/components/production-line/use-control-volume.tsx @@ -0,0 +1,54 @@ +import { useEffect, useRef, useState } from "react"; + +type TUseControlVolumeOptions = { + stream: MediaStream | null; +}; + +type TUseControlVolume = (options: TUseControlVolumeOptions) => { + setVolume: (value: number) => void; + audioContext: AudioContext | null; +}; + +export const useControlVolume: TUseControlVolume = ({ stream }) => { + const [audioContext, setAudioContext] = useState(null); + const gainNodeRef = useRef(null); + + useEffect(() => { + if (!stream) return; + + console.log("Initializing Audio Context"); + + const audioCtx = new AudioContext(); + setAudioContext(audioCtx); + + const source = audioCtx.createMediaStreamSource(stream); + + const gainNode = audioCtx.createGain(); + gainNode.gain.value = 0.5; + + source.connect(gainNode); + // Outputs the audio to the speakers + gainNode.connect(audioCtx.destination); + + gainNodeRef.current = gainNode; + + // eslint-disable-next-line consistent-return + return () => { + source.disconnect(); + gainNode.disconnect(); + audioCtx.close(); + gainNodeRef.current = null; + }; + }, [stream]); + + const setVolume = (value: number) => { + if (gainNodeRef.current) { + const clampedValue = Math.max(0, Math.min(1, value)); + gainNodeRef.current.gain.value = clampedValue; + + console.log("Setting Gain Node Volume:", clampedValue); + } + }; + + return { setVolume, audioContext }; +}; diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index 997aa055..119ea61c 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -14,6 +14,7 @@ import { useGlobalState } from "../../global-state/context-provider.tsx"; import { TGlobalStateAction } from "../../global-state/global-state-actions.ts"; import { TUseAudioInputValues } from "./use-audio-input.ts"; import { startRtcStatInterval } from "./rtc-stat-interval.ts"; +import { useControlVolume } from "./use-control-volume.tsx"; type TRtcConnectionOptions = { inputAudioStream: TUseAudioInputValues; @@ -235,6 +236,12 @@ export const useRtcConnection = ({ const [noStreamError, setNoStreamError] = useState(false); const audioElementsRef = useRef(audioElements); const navigate = useNavigate(); + const { setVolume, audioContext } = useControlVolume({ + stream: + inputAudioStream && typeof inputAudioStream !== "string" + ? inputAudioStream + : null, + }); // Use a ref to make sure we only clean up // audio elements once, and not every time @@ -398,5 +405,5 @@ export const useRtcConnection = ({ }; }, [rtcPeerConnection]); - return { connectionState, audioElements }; + return { connectionState, audioElements, setVolume, audioContext }; }; diff --git a/src/components/slider/slider.tsx b/src/components/slider/slider.tsx new file mode 100644 index 00000000..366868cc --- /dev/null +++ b/src/components/slider/slider.tsx @@ -0,0 +1,97 @@ +import styled from "@emotion/styled"; +import { FC, useState } from "react"; +import { NoSoundIcon, FullSoundIcon } from "../../assets/icons/icon"; + +const SliderWrapper = styled.div` + width: 100%; + margin: 2rem 0; + display: flex; + flex-direction: row; + align-items: center; + position: relative; +`; + +const IconWrapper = styled.div` + width: 5rem; + height: 5rem; + padding: 0.5rem; +`; + +const SliderTrack = styled.div` + width: 80%; + height: 0.4rem; + background-color: #e0e0e0; + border-radius: 0.2rem; + position: relative; +`; + +const SliderThumb = styled.div<{ position: number }>` + width: 1.5rem; + height: 1.5rem; + background-color: #59cbe8; + border-radius: 50%; + position: absolute; + top: -0.6rem; + left: ${({ position }) => `${position}%`}; + transform: translateX(-50%); + cursor: pointer; +`; + +type SliderProps = { + min: number; + max: number; + step?: number; + initialValue?: number; + setVolume: (value: number) => void; +}; + +export const Slider: FC = ({ + min, + max, + step = 1, + initialValue = min, + setVolume, +}) => { + const [value, setValue] = useState(initialValue); + + const handleInputChange = (e: React.ChangeEvent) => { + const newValue = Number(e.target.value); + setValue(newValue); + setVolume(newValue / max); + + console.log("Slider value: ", newValue); + console.log("Normalized value: ", newValue / max); + }; + + const thumbPosition = ((value - min) / (max - min)) * 100; + + return ( + + + + + + + + + + + + + ); +}; From 78047946e5f40507a8de029198da3f1a74b8205d Mon Sep 17 00:00:00 2001 From: Saelmala Date: Mon, 9 Dec 2024 11:28:26 +0100 Subject: [PATCH 02/26] fix: remove duplicate outputs --- .../production-line/production-line.tsx | 12 +- .../production-line/use-control-volume.tsx | 50 ++------ .../production-line/use-rtc-connection.ts | 111 ++++++++++++------ 3 files changed, 89 insertions(+), 84 deletions(-) diff --git a/src/components/production-line/production-line.tsx b/src/components/production-line/production-line.tsx index 0d34f4a2..7ae37216 100644 --- a/src/components/production-line/production-line.tsx +++ b/src/components/production-line/production-line.tsx @@ -146,10 +146,6 @@ export const ProductionLine: FC = () => { inputId: joinProductionOptions?.audioinput ?? null, }); - const { setVolume } = useControlVolume({ - stream: inputAudioStream !== "no-device" ? inputAudioStream : null, - }); - const muteInput = useCallback( (mute: boolean) => { if (inputAudioStream && inputAudioStream !== "no-device") { @@ -192,6 +188,10 @@ export const ProductionLine: FC = () => { sessionId, }); + const { setVolume } = useControlVolume({ + audioElements, + }); + useEffect(() => { if (connectionState === "connected") { playEnterSound(); @@ -199,9 +199,9 @@ export const ProductionLine: FC = () => { }, [connectionState, playEnterSound]); const muteOutput = useCallback(() => { - audioElements.forEach((singleElement: HTMLAudioElement) => { + audioElements.forEach(({ gainNode }) => { // eslint-disable-next-line no-param-reassign - singleElement.muted = !isOutputMuted; + gainNode.gain.value = isOutputMuted ? 1 : 0; }); setIsOutputMuted(!isOutputMuted); }, [audioElements, isOutputMuted]); diff --git a/src/components/production-line/use-control-volume.tsx b/src/components/production-line/use-control-volume.tsx index d47ad2a4..4706cf60 100644 --- a/src/components/production-line/use-control-volume.tsx +++ b/src/components/production-line/use-control-volume.tsx @@ -1,54 +1,26 @@ -import { useEffect, useRef, useState } from "react"; +type TAudioElement = { + audioCtx: AudioContext; + gainNode: GainNode; + source: MediaStreamAudioSourceNode; +}; type TUseControlVolumeOptions = { - stream: MediaStream | null; + audioElements: TAudioElement[]; }; type TUseControlVolume = (options: TUseControlVolumeOptions) => { setVolume: (value: number) => void; - audioContext: AudioContext | null; }; -export const useControlVolume: TUseControlVolume = ({ stream }) => { - const [audioContext, setAudioContext] = useState(null); - const gainNodeRef = useRef(null); - - useEffect(() => { - if (!stream) return; - - console.log("Initializing Audio Context"); - - const audioCtx = new AudioContext(); - setAudioContext(audioCtx); - - const source = audioCtx.createMediaStreamSource(stream); - - const gainNode = audioCtx.createGain(); - gainNode.gain.value = 0.5; - - source.connect(gainNode); - // Outputs the audio to the speakers - gainNode.connect(audioCtx.destination); - - gainNodeRef.current = gainNode; - - // eslint-disable-next-line consistent-return - return () => { - source.disconnect(); - gainNode.disconnect(); - audioCtx.close(); - gainNodeRef.current = null; - }; - }, [stream]); - +export const useControlVolume: TUseControlVolume = ({ audioElements }) => { const setVolume = (value: number) => { - if (gainNodeRef.current) { + audioElements.forEach(({ gainNode }) => { const clampedValue = Math.max(0, Math.min(1, value)); - gainNodeRef.current.gain.value = clampedValue; + gainNode.gain.setValueAtTime(clampedValue, gainNode.context.currentTime); console.log("Setting Gain Node Volume:", clampedValue); - } + }); }; - return { setVolume, audioContext }; + return { setVolume }; }; diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index 119ea61c..446497a1 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -29,7 +29,15 @@ type TEstablishConnection = { joinProductionOptions: TJoinProductionOptions; sessionId: string; dispatch: Dispatch; - setAudioElements: Dispatch>; + setAudioElements: Dispatch< + SetStateAction< + { + audioCtx: AudioContext; + gainNode: GainNode; + source: MediaStreamAudioSourceNode; + }[] + > + >; setNoStreamError: (input: boolean) => void; }; @@ -49,7 +57,7 @@ const attachInputAudioToPeerConnection = ({ const establishConnection = ({ rtcPeerConnection, sdpOffer, - joinProductionOptions, + // joinProductionOptions, sessionId, dispatch, setAudioElements, @@ -60,31 +68,45 @@ const establishConnection = ({ const selectedStream = streams[0]; if (selectedStream && selectedStream.getAudioTracks().length !== 0) { - const audioElement = new Audio(); - - audioElement.controls = false; - audioElement.autoplay = true; - audioElement.onerror = () => { - dispatch({ - type: "ERROR", - payload: new Error( - `Audio Error: ${audioElement.error?.code} - ${audioElement.error?.message}` - ), - }); - }; - - audioElement.srcObject = selectedStream; - - setAudioElements((prevArray) => [audioElement, ...prevArray]); - if (joinProductionOptions.audiooutput) { - audioElement.setSinkId(joinProductionOptions.audiooutput).catch((e) => { - dispatch({ - type: "ERROR", - payload: - e instanceof Error ? e : new Error("Error assigning audio sink."), - }); - }); - } + const audioCtx = new AudioContext(); + const gainNode = audioCtx.createGain(); + const source = audioCtx.createMediaStreamSource(selectedStream); + + source.connect(gainNode); + gainNode.connect(audioCtx.destination); + + gainNode.gain.value = 0.5; + + setAudioElements((prevArray) => [ + { audioCtx, gainNode, source }, + ...prevArray, + ]); + + // const audioElement = new Audio(); + + // audioElement.controls = false; + // audioElement.autoplay = true; + // audioElement.onerror = () => { + // dispatch({ + // type: "ERROR", + // payload: new Error( + // `Audio Error: ${audioElement.error?.code} - ${audioElement.error?.message}` + // ), + // }); + // }; + + // audioElement.srcObject = selectedStream; + + // setAudioElements((prevArray) => [audioElement, ...prevArray]); + // if (joinProductionOptions.audiooutput) { + // audioElement.setSinkId(joinProductionOptions.audiooutput).catch((e) => { + // dispatch({ + // type: "ERROR", + // payload: + // e instanceof Error ? e : new Error("Error assigning audio sink."), + // }); + // }); + // } } else if (selectedStream && selectedStream.getAudioTracks().length === 0) { setNoStreamError(true); dispatch({ @@ -232,15 +254,24 @@ export const useRtcConnection = ({ const [, dispatch] = useGlobalState(); const [connectionState, setConnectionState] = useState(null); - const [audioElements, setAudioElements] = useState([]); + const [audioElements, setAudioElements] = useState< + { + audioCtx: AudioContext; + gainNode: GainNode; + source: MediaStreamAudioSourceNode; + }[] + >([]); const [noStreamError, setNoStreamError] = useState(false); - const audioElementsRef = useRef(audioElements); + const audioElementsRef = useRef< + { + audioCtx: AudioContext; + gainNode: GainNode; + source: MediaStreamAudioSourceNode; + }[] + >(audioElements); const navigate = useNavigate(); - const { setVolume, audioContext } = useControlVolume({ - stream: - inputAudioStream && typeof inputAudioStream !== "string" - ? inputAudioStream - : null, + const { setVolume } = useControlVolume({ + audioElements, }); // Use a ref to make sure we only clean up @@ -251,10 +282,12 @@ export const useRtcConnection = ({ }, [audioElements]); const cleanUpAudio = useCallback(() => { - audioElementsRef.current.forEach((el) => { - el.pause(); - // eslint-disable-next-line no-param-reassign - el.srcObject = null; + audioElementsRef.current.forEach(({ audioCtx, source, gainNode }) => { + // el.pause(); + // el.srcObject = null; + source.disconnect(); + gainNode.disconnect(); + audioCtx.close(); }); }, [audioElementsRef]); @@ -405,5 +438,5 @@ export const useRtcConnection = ({ }; }, [rtcPeerConnection]); - return { connectionState, audioElements, setVolume, audioContext }; + return { connectionState, audioElements, setVolume }; }; From af31a28641833cb318ec0f98f304c38d7f8eab0a Mon Sep 17 00:00:00 2001 From: Saelmala Date: Mon, 9 Dec 2024 11:52:30 +0100 Subject: [PATCH 03/26] fix: testing --- .../production-line/use-rtc-connection.ts | 59 ++++++++++++------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index 446497a1..1c1dba74 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -35,6 +35,7 @@ type TEstablishConnection = { audioCtx: AudioContext; gainNode: GainNode; source: MediaStreamAudioSourceNode; + audioElement: HTMLAudioElement; }[] > >; @@ -57,7 +58,7 @@ const attachInputAudioToPeerConnection = ({ const establishConnection = ({ rtcPeerConnection, sdpOffer, - // joinProductionOptions, + joinProductionOptions, sessionId, dispatch, setAudioElements, @@ -71,16 +72,21 @@ const establishConnection = ({ const audioCtx = new AudioContext(); const gainNode = audioCtx.createGain(); const source = audioCtx.createMediaStreamSource(selectedStream); + const audioElement = new Audio(); source.connect(gainNode); gainNode.connect(audioCtx.destination); gainNode.gain.value = 0.5; - setAudioElements((prevArray) => [ - { audioCtx, gainNode, source }, - ...prevArray, - ]); + audioElement.srcObject = selectedStream; + audioElement.controls = false; + audioElement.autoplay = true; + + // setAudioElements((prevArray) => [ + // { audioCtx, gainNode, source }, + // ...prevArray, + // ]); // const audioElement = new Audio(); @@ -98,15 +104,20 @@ const establishConnection = ({ // audioElement.srcObject = selectedStream; // setAudioElements((prevArray) => [audioElement, ...prevArray]); - // if (joinProductionOptions.audiooutput) { - // audioElement.setSinkId(joinProductionOptions.audiooutput).catch((e) => { - // dispatch({ - // type: "ERROR", - // payload: - // e instanceof Error ? e : new Error("Error assigning audio sink."), - // }); - // }); - // } + if (joinProductionOptions.audiooutput) { + audioElement.setSinkId(joinProductionOptions.audiooutput).catch((e) => { + dispatch({ + type: "ERROR", + payload: + e instanceof Error ? e : new Error("Error assigning audio sink."), + }); + }); + } + + setAudioElements((prevArray) => [ + { audioCtx, gainNode, source, audioElement }, + ...prevArray, + ]); } else if (selectedStream && selectedStream.getAudioTracks().length === 0) { setNoStreamError(true); dispatch({ @@ -259,6 +270,7 @@ export const useRtcConnection = ({ audioCtx: AudioContext; gainNode: GainNode; source: MediaStreamAudioSourceNode; + audioElement: HTMLAudioElement; }[] >([]); const [noStreamError, setNoStreamError] = useState(false); @@ -267,6 +279,7 @@ export const useRtcConnection = ({ audioCtx: AudioContext; gainNode: GainNode; source: MediaStreamAudioSourceNode; + audioElement: HTMLAudioElement; }[] >(audioElements); const navigate = useNavigate(); @@ -282,13 +295,17 @@ export const useRtcConnection = ({ }, [audioElements]); const cleanUpAudio = useCallback(() => { - audioElementsRef.current.forEach(({ audioCtx, source, gainNode }) => { - // el.pause(); - // el.srcObject = null; - source.disconnect(); - gainNode.disconnect(); - audioCtx.close(); - }); + audioElementsRef.current.forEach( + ({ audioCtx, source, gainNode, audioElement }) => { + // el.pause(); + // el.srcObject = null; + // eslint-disable-next-line no-param-reassign + audioElement.srcObject = null; + source.disconnect(); + gainNode.disconnect(); + audioCtx.close(); + } + ); }, [audioElementsRef]); // Teardown From 7211ce6778f70009d59689a2a969726187631991 Mon Sep 17 00:00:00 2001 From: Saelmala Date: Mon, 9 Dec 2024 15:03:17 +0100 Subject: [PATCH 04/26] fix: testing --- .../production-line/production-line.tsx | 12 +-- .../production-line/use-control-volume.tsx | 50 +++++++--- .../production-line/use-rtc-connection.ts | 99 +++++-------------- 3 files changed, 71 insertions(+), 90 deletions(-) diff --git a/src/components/production-line/production-line.tsx b/src/components/production-line/production-line.tsx index 7ae37216..0d34f4a2 100644 --- a/src/components/production-line/production-line.tsx +++ b/src/components/production-line/production-line.tsx @@ -146,6 +146,10 @@ export const ProductionLine: FC = () => { inputId: joinProductionOptions?.audioinput ?? null, }); + const { setVolume } = useControlVolume({ + stream: inputAudioStream !== "no-device" ? inputAudioStream : null, + }); + const muteInput = useCallback( (mute: boolean) => { if (inputAudioStream && inputAudioStream !== "no-device") { @@ -188,10 +192,6 @@ export const ProductionLine: FC = () => { sessionId, }); - const { setVolume } = useControlVolume({ - audioElements, - }); - useEffect(() => { if (connectionState === "connected") { playEnterSound(); @@ -199,9 +199,9 @@ export const ProductionLine: FC = () => { }, [connectionState, playEnterSound]); const muteOutput = useCallback(() => { - audioElements.forEach(({ gainNode }) => { + audioElements.forEach((singleElement: HTMLAudioElement) => { // eslint-disable-next-line no-param-reassign - gainNode.gain.value = isOutputMuted ? 1 : 0; + singleElement.muted = !isOutputMuted; }); setIsOutputMuted(!isOutputMuted); }, [audioElements, isOutputMuted]); diff --git a/src/components/production-line/use-control-volume.tsx b/src/components/production-line/use-control-volume.tsx index 4706cf60..d47ad2a4 100644 --- a/src/components/production-line/use-control-volume.tsx +++ b/src/components/production-line/use-control-volume.tsx @@ -1,26 +1,54 @@ -type TAudioElement = { - audioCtx: AudioContext; - gainNode: GainNode; - source: MediaStreamAudioSourceNode; -}; +import { useEffect, useRef, useState } from "react"; type TUseControlVolumeOptions = { - audioElements: TAudioElement[]; + stream: MediaStream | null; }; type TUseControlVolume = (options: TUseControlVolumeOptions) => { setVolume: (value: number) => void; + audioContext: AudioContext | null; }; -export const useControlVolume: TUseControlVolume = ({ audioElements }) => { +export const useControlVolume: TUseControlVolume = ({ stream }) => { + const [audioContext, setAudioContext] = useState(null); + const gainNodeRef = useRef(null); + + useEffect(() => { + if (!stream) return; + + console.log("Initializing Audio Context"); + + const audioCtx = new AudioContext(); + setAudioContext(audioCtx); + + const source = audioCtx.createMediaStreamSource(stream); + + const gainNode = audioCtx.createGain(); + gainNode.gain.value = 0.5; + + source.connect(gainNode); + // Outputs the audio to the speakers + gainNode.connect(audioCtx.destination); + + gainNodeRef.current = gainNode; + + // eslint-disable-next-line consistent-return + return () => { + source.disconnect(); + gainNode.disconnect(); + audioCtx.close(); + gainNodeRef.current = null; + }; + }, [stream]); + const setVolume = (value: number) => { - audioElements.forEach(({ gainNode }) => { + if (gainNodeRef.current) { const clampedValue = Math.max(0, Math.min(1, value)); - gainNode.gain.setValueAtTime(clampedValue, gainNode.context.currentTime); + gainNodeRef.current.gain.value = clampedValue; console.log("Setting Gain Node Volume:", clampedValue); - }); + } }; - return { setVolume }; + return { setVolume, audioContext }; }; diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index 1c1dba74..6b785e93 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -14,7 +14,6 @@ import { useGlobalState } from "../../global-state/context-provider.tsx"; import { TGlobalStateAction } from "../../global-state/global-state-actions.ts"; import { TUseAudioInputValues } from "./use-audio-input.ts"; import { startRtcStatInterval } from "./rtc-stat-interval.ts"; -import { useControlVolume } from "./use-control-volume.tsx"; type TRtcConnectionOptions = { inputAudioStream: TUseAudioInputValues; @@ -29,16 +28,7 @@ type TEstablishConnection = { joinProductionOptions: TJoinProductionOptions; sessionId: string; dispatch: Dispatch; - setAudioElements: Dispatch< - SetStateAction< - { - audioCtx: AudioContext; - gainNode: GainNode; - source: MediaStreamAudioSourceNode; - audioElement: HTMLAudioElement; - }[] - > - >; + setAudioElements: Dispatch>; setNoStreamError: (input: boolean) => void; }; @@ -69,41 +59,32 @@ const establishConnection = ({ const selectedStream = streams[0]; if (selectedStream && selectedStream.getAudioTracks().length !== 0) { - const audioCtx = new AudioContext(); - const gainNode = audioCtx.createGain(); - const source = audioCtx.createMediaStreamSource(selectedStream); const audioElement = new Audio(); + // const audioCtx = new AudioContext(); + // const gainNode = audioCtx.createGain(); + // const source = audioCtx.createMediaStreamSource(selectedStream); - source.connect(gainNode); - gainNode.connect(audioCtx.destination); + // source.connect(gainNode); + // gainNode.connect(audioCtx.destination); - gainNode.gain.value = 0.5; + // gainNode.gain.value = 0.5; audioElement.srcObject = selectedStream; + audioElement.controls = false; audioElement.autoplay = true; + audioElement.onerror = () => { + dispatch({ + type: "ERROR", + payload: new Error( + `Audio Error: ${audioElement.error?.code} - ${audioElement.error?.message}` + ), + }); + }; - // setAudioElements((prevArray) => [ - // { audioCtx, gainNode, source }, - // ...prevArray, - // ]); - - // const audioElement = new Audio(); - - // audioElement.controls = false; - // audioElement.autoplay = true; - // audioElement.onerror = () => { - // dispatch({ - // type: "ERROR", - // payload: new Error( - // `Audio Error: ${audioElement.error?.code} - ${audioElement.error?.message}` - // ), - // }); - // }; - - // audioElement.srcObject = selectedStream; + audioElement.srcObject = selectedStream; - // setAudioElements((prevArray) => [audioElement, ...prevArray]); + setAudioElements((prevArray) => [audioElement, ...prevArray]); if (joinProductionOptions.audiooutput) { audioElement.setSinkId(joinProductionOptions.audiooutput).catch((e) => { dispatch({ @@ -113,11 +94,6 @@ const establishConnection = ({ }); }); } - - setAudioElements((prevArray) => [ - { audioCtx, gainNode, source, audioElement }, - ...prevArray, - ]); } else if (selectedStream && selectedStream.getAudioTracks().length === 0) { setNoStreamError(true); dispatch({ @@ -265,27 +241,10 @@ export const useRtcConnection = ({ const [, dispatch] = useGlobalState(); const [connectionState, setConnectionState] = useState(null); - const [audioElements, setAudioElements] = useState< - { - audioCtx: AudioContext; - gainNode: GainNode; - source: MediaStreamAudioSourceNode; - audioElement: HTMLAudioElement; - }[] - >([]); + const [audioElements, setAudioElements] = useState([]); const [noStreamError, setNoStreamError] = useState(false); - const audioElementsRef = useRef< - { - audioCtx: AudioContext; - gainNode: GainNode; - source: MediaStreamAudioSourceNode; - audioElement: HTMLAudioElement; - }[] - >(audioElements); + const audioElementsRef = useRef(audioElements); const navigate = useNavigate(); - const { setVolume } = useControlVolume({ - audioElements, - }); // Use a ref to make sure we only clean up // audio elements once, and not every time @@ -295,17 +254,11 @@ export const useRtcConnection = ({ }, [audioElements]); const cleanUpAudio = useCallback(() => { - audioElementsRef.current.forEach( - ({ audioCtx, source, gainNode, audioElement }) => { - // el.pause(); - // el.srcObject = null; - // eslint-disable-next-line no-param-reassign - audioElement.srcObject = null; - source.disconnect(); - gainNode.disconnect(); - audioCtx.close(); - } - ); + audioElementsRef.current.forEach((el) => { + el.pause(); + // eslint-disable-next-line no-param-reassign + el.srcObject = null; + }); }, [audioElementsRef]); // Teardown @@ -455,5 +408,5 @@ export const useRtcConnection = ({ }; }, [rtcPeerConnection]); - return { connectionState, audioElements, setVolume }; + return { connectionState, audioElements }; }; From 24c9cd7672e822681e97984afbb63517b7b715c4 Mon Sep 17 00:00:00 2001 From: Saelmala Date: Tue, 10 Dec 2024 15:49:50 +0100 Subject: [PATCH 05/26] fix: change to use element instead of context --- src/assets/icons/icon.tsx | 6 + src/assets/icons/minus.svg | 1 + src/assets/icons/no_sound.svg | 2 +- src/assets/icons/plus.svg | 1 + .../production-line/production-line.tsx | 35 ++-- .../production-line/settings-modal.tsx | 105 +++++++++--- .../production-line/use-control-volume.tsx | 54 ------ .../production-line/use-rtc-connection.ts | 11 +- src/components/slider/slider.tsx | 97 ----------- .../volume-slider/volume-slider.tsx | 159 ++++++++++++++++++ 10 files changed, 274 insertions(+), 197 deletions(-) create mode 100644 src/assets/icons/minus.svg create mode 100644 src/assets/icons/plus.svg delete mode 100644 src/components/production-line/use-control-volume.tsx delete mode 100644 src/components/slider/slider.tsx create mode 100644 src/components/volume-slider/volume-slider.tsx diff --git a/src/assets/icons/icon.tsx b/src/assets/icons/icon.tsx index 0b2ea03a..4476952f 100644 --- a/src/assets/icons/icon.tsx +++ b/src/assets/icons/icon.tsx @@ -11,6 +11,8 @@ import StepRightSvg from "./navigate_next.svg?react"; import Settings from "./settings.svg?react"; import NoSound from "./no_sound.svg?react"; import FullSound from "./full_sound.svg?react"; +import Minus from "./minus.svg?react"; +import Plus from "./plus.svg?react"; export const MicMuted = () => ; @@ -37,3 +39,7 @@ export const SettingsIcon = () => ; export const NoSoundIcon = () => ; export const FullSoundIcon = () => ; + +export const MinusIcon = () => ; + +export const PlusIcon = () => ; diff --git a/src/assets/icons/minus.svg b/src/assets/icons/minus.svg new file mode 100644 index 00000000..edeb6eb3 --- /dev/null +++ b/src/assets/icons/minus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/no_sound.svg b/src/assets/icons/no_sound.svg index ddf48824..50811799 100644 --- a/src/assets/icons/no_sound.svg +++ b/src/assets/icons/no_sound.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/icons/plus.svg b/src/assets/icons/plus.svg new file mode 100644 index 00000000..6c6665fc --- /dev/null +++ b/src/assets/icons/plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/production-line/production-line.tsx b/src/components/production-line/production-line.tsx index 0d34f4a2..55d52f4f 100644 --- a/src/components/production-line/production-line.tsx +++ b/src/components/production-line/production-line.tsx @@ -27,12 +27,11 @@ import { useLinePolling } from "./use-line-polling.ts"; import { useFetchProduction } from "../landing-page/use-fetch-production.ts"; import { useIsLoading } from "./use-is-loading.ts"; import { useCheckBadLineData } from "./use-check-bad-line-data.ts"; -import { useControlVolume } from "./use-control-volume.tsx"; import { NavigateToRootButton } from "../navigate-to-root-button/navigate-to-root-button.tsx"; import { useAudioCue } from "./use-audio-cue.ts"; import { DisplayWarning } from "../display-box.tsx"; import { SettingsModal, Hotkeys } from "./settings-modal.tsx"; -import { Slider } from "../slider/slider.tsx"; +import { VolumeSlider } from "../volume-slider/volume-slider.tsx"; const TempDiv = styled.div` padding: 0 0 2rem 0; @@ -135,21 +134,21 @@ export const ProductionLine: FC = () => { muteHotkey: "m", speakerHotkey: "n", pressToTalkHotkey: "t", + increaseVolumeHotkey: "u", + decreaseVolumeHotkey: "d", }); const [savedHotkeys, setSavedHotkeys] = useState({ muteHotkey: "m", speakerHotkey: "n", pressToTalkHotkey: "t", + increaseVolumeHotkey: "u", + decreaseVolumeHotkey: "d", }); const inputAudioStream = useAudioInput({ inputId: joinProductionOptions?.audioinput ?? null, }); - const { setVolume } = useControlVolume({ - stream: inputAudioStream !== "no-device" ? inputAudioStream : null, - }); - const muteInput = useCallback( (mute: boolean) => { if (inputAudioStream && inputAudioStream !== "no-device") { @@ -316,14 +315,11 @@ export const ProductionLine: FC = () => { }} > Controls - - muteOutput()}> @@ -332,7 +328,6 @@ export const ProductionLine: FC = () => { - {inputAudioStream && inputAudioStream !== "no-device" && ( { Push to Talk + + + {savedHotkeys.increaseVolumeHotkey.toUpperCase()}:{" "} + + Increase Volume + + + + {savedHotkeys.decreaseVolumeHotkey.toUpperCase()}:{" "} + + Decrease Volume + {isSettingsModalOpen && ( void; onSave: () => void; }; + export const SettingsModal = ({ hotkeys, lineName, @@ -87,39 +90,39 @@ export const SettingsModal = ({ muteHotkey: "", speakerHotkey: "", pressToTalkHotkey: "", + increaseVolumeHotkey: "", + decreaseVolumeHotkey: "", }); - const validateFields = (key: string, value: string) => { + const validateFields = (key: keyof Hotkeys, value: string) => { const currentValues = { ...hotkeys, [key]: value, }; - const duplicates = Object.entries(currentValues).reduce( - (acc, [field, val]) => { - if (val && value && val === value && field !== key) { - acc[key] = "This key is already in use."; + const newErrors = ( + Object.keys(currentValues) as Array + ).reduce( + (acc, field) => { + const isDuplicate = + Object.values(currentValues).filter( + (val) => val && val === currentValues[field] + ).length > 1; + + if (!currentValues[field] || currentValues[field] === "") { + acc[field] = "This field can not be empty."; + } else if (isDuplicate) { + acc[field] = "This key is already in use."; + } else { + acc[field] = ""; } + return acc; }, - {} as { [key: string]: string } + {} as { [K in keyof Hotkeys]: string } ); - setErrors((prevErrors) => ({ - ...prevErrors, - muteHotkey: - key === "muteHotkey" - ? duplicates.muteHotkey || "" - : prevErrors.muteHotkey, - speakerHotkey: - key === "speakerHotkey" - ? duplicates.speakerHotkey || "" - : prevErrors.speakerHotkey, - pressToTalkHotkey: - key === "pressToTalkHotkey" - ? duplicates.pressToTalkHotkey || "" - : prevErrors.pressToTalkHotkey, - })); + setErrors(newErrors); }; const handleInputChange = (key: keyof typeof hotkeys, value: string) => { @@ -134,9 +137,15 @@ export const SettingsModal = ({ const handleSave = () => { const hasErrors = Object.values(errors).some((error) => error !== ""); - if (!hasErrors) { - onSave(); + const hasEmptyFields = Object.values(hotkeys).some( + (value) => !value || value === "" + ); + + if (hasErrors || hasEmptyFields) { + return; } + + onSave(); }; const handleKeyDown = (e: React.KeyboardEvent, index: number) => { @@ -237,6 +246,56 @@ export const SettingsModal = ({ /> )} + + Increase volume: + setInputRef(3, el)} + type="text" + value={hotkeys.increaseVolumeHotkey} + onChange={(e) => + handleInputChange("increaseVolumeHotkey", e.target.value) + } + placeholder="Enter hotkey" + maxLength={1} + onKeyDown={(e) => handleKeyDown(e, 3)} + /> + {errors.increaseVolumeHotkey && ( + + )} + + + Decrease volume: + setInputRef(4, el)} + type="text" + value={hotkeys.decreaseVolumeHotkey} + onChange={(e) => + handleInputChange("decreaseVolumeHotkey", e.target.value) + } + placeholder="Enter hotkey" + maxLength={1} + onKeyDown={(e) => handleKeyDown(e, 4)} + /> + {errors.decreaseVolumeHotkey && ( + + )} + Cancel diff --git a/src/components/production-line/use-control-volume.tsx b/src/components/production-line/use-control-volume.tsx deleted file mode 100644 index d47ad2a4..00000000 --- a/src/components/production-line/use-control-volume.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { useEffect, useRef, useState } from "react"; - -type TUseControlVolumeOptions = { - stream: MediaStream | null; -}; - -type TUseControlVolume = (options: TUseControlVolumeOptions) => { - setVolume: (value: number) => void; - audioContext: AudioContext | null; -}; - -export const useControlVolume: TUseControlVolume = ({ stream }) => { - const [audioContext, setAudioContext] = useState(null); - const gainNodeRef = useRef(null); - - useEffect(() => { - if (!stream) return; - - console.log("Initializing Audio Context"); - - const audioCtx = new AudioContext(); - setAudioContext(audioCtx); - - const source = audioCtx.createMediaStreamSource(stream); - - const gainNode = audioCtx.createGain(); - gainNode.gain.value = 0.5; - - source.connect(gainNode); - // Outputs the audio to the speakers - gainNode.connect(audioCtx.destination); - - gainNodeRef.current = gainNode; - - // eslint-disable-next-line consistent-return - return () => { - source.disconnect(); - gainNode.disconnect(); - audioCtx.close(); - gainNodeRef.current = null; - }; - }, [stream]); - - const setVolume = (value: number) => { - if (gainNodeRef.current) { - const clampedValue = Math.max(0, Math.min(1, value)); - gainNodeRef.current.gain.value = clampedValue; - - console.log("Setting Gain Node Volume:", clampedValue); - } - }; - - return { setVolume, audioContext }; -}; diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index 6b785e93..2556a95b 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -60,19 +60,14 @@ const establishConnection = ({ if (selectedStream && selectedStream.getAudioTracks().length !== 0) { const audioElement = new Audio(); - // const audioCtx = new AudioContext(); - // const gainNode = audioCtx.createGain(); - // const source = audioCtx.createMediaStreamSource(selectedStream); - - // source.connect(gainNode); - // gainNode.connect(audioCtx.destination); - - // gainNode.gain.value = 0.5; audioElement.srcObject = selectedStream; audioElement.controls = false; audioElement.autoplay = true; + + audioElement.volume = 0.75; + audioElement.onerror = () => { dispatch({ type: "ERROR", diff --git a/src/components/slider/slider.tsx b/src/components/slider/slider.tsx deleted file mode 100644 index 366868cc..00000000 --- a/src/components/slider/slider.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import styled from "@emotion/styled"; -import { FC, useState } from "react"; -import { NoSoundIcon, FullSoundIcon } from "../../assets/icons/icon"; - -const SliderWrapper = styled.div` - width: 100%; - margin: 2rem 0; - display: flex; - flex-direction: row; - align-items: center; - position: relative; -`; - -const IconWrapper = styled.div` - width: 5rem; - height: 5rem; - padding: 0.5rem; -`; - -const SliderTrack = styled.div` - width: 80%; - height: 0.4rem; - background-color: #e0e0e0; - border-radius: 0.2rem; - position: relative; -`; - -const SliderThumb = styled.div<{ position: number }>` - width: 1.5rem; - height: 1.5rem; - background-color: #59cbe8; - border-radius: 50%; - position: absolute; - top: -0.6rem; - left: ${({ position }) => `${position}%`}; - transform: translateX(-50%); - cursor: pointer; -`; - -type SliderProps = { - min: number; - max: number; - step?: number; - initialValue?: number; - setVolume: (value: number) => void; -}; - -export const Slider: FC = ({ - min, - max, - step = 1, - initialValue = min, - setVolume, -}) => { - const [value, setValue] = useState(initialValue); - - const handleInputChange = (e: React.ChangeEvent) => { - const newValue = Number(e.target.value); - setValue(newValue); - setVolume(newValue / max); - - console.log("Slider value: ", newValue); - console.log("Normalized value: ", newValue / max); - }; - - const thumbPosition = ((value - min) / (max - min)) * 100; - - return ( - - - - - - - - - - - - - ); -}; diff --git a/src/components/volume-slider/volume-slider.tsx b/src/components/volume-slider/volume-slider.tsx new file mode 100644 index 00000000..54226c30 --- /dev/null +++ b/src/components/volume-slider/volume-slider.tsx @@ -0,0 +1,159 @@ +import styled from "@emotion/styled"; +import { FC, useState } from "react"; +import { useHotkeys } from "react-hotkeys-hook"; +import { + NoSoundIcon, + FullSoundIcon, + MinusIcon, + PlusIcon, +} from "../../assets/icons/icon"; +import { isMobile } from "../../bowser"; +import { PrimaryButton } from "../landing-page/form-elements"; + +const SliderWrapper = styled.div` + width: 100%; + margin: 2rem 0; + display: flex; + flex-direction: row; + position: relative; +`; + +const VolumeContainer = styled.div` + display: flex; + flex-direction: column; +`; + +const IconWrapper = styled.div` + width: 5rem; + height: 5rem; + padding: 0.5rem; +`; + +const SliderTrack = styled.div` + width: 80%; + height: 0.4rem; + background-color: #e0e0e0; + border-radius: 0.2rem; + position: relative; + margin-top: 2.5rem; +`; +// TODO: Remove margin-top and solve in a better way + +const SliderThumb = styled.div<{ position: number }>` + width: 1.5rem; + height: 1.5rem; + background-color: #59cbe8; + border-radius: 50%; + position: absolute; + top: -0.6rem; + left: ${({ position }) => `${position}%`}; + transform: translateX(-50%); + cursor: pointer; +`; + +const VolumeButton = styled(PrimaryButton)` + width: 7rem; + align-items: center; + height: 4.5rem; + padding: 1.5rem; + cursor: pointer; + margin-top: 1rem; +`; + +type TVolumeSliderProps = { + audioElements: HTMLAudioElement[]; + increaseVolumeKey?: string; + decreaseVolumeKey?: string; +}; + +export const VolumeSlider: FC = ({ + audioElements, + increaseVolumeKey, + decreaseVolumeKey, +}) => { + const [value, setValue] = useState(0.75); + + const handleInputChange = (e: React.ChangeEvent) => { + const newValue = parseFloat(e.target.value); + setValue(newValue); + + audioElements.forEach((audioElement) => { + // eslint-disable-next-line no-param-reassign + audioElement.volume = newValue; + }); + }; + + const thumbPosition = value * 100; + + useHotkeys(increaseVolumeKey || "u", () => { + const newValue = Math.min(value + 0.05, 1); + setValue(newValue); + + audioElements.forEach((audioElement) => { + // eslint-disable-next-line no-param-reassign + audioElement.volume = newValue; + }); + }); + + useHotkeys(decreaseVolumeKey || "d", () => { + const newValue = Math.max(value - 0.05, 0); + setValue(newValue); + + audioElements.forEach((audioElement) => { + // eslint-disable-next-line no-param-reassign + audioElement.volume = newValue; + }); + }); + + const handleVolumeButtonClick = (type: "increase" | "decrease") => { + const newValue = + type === "increase" + ? Math.min(value + 0.05, 1) + : Math.max(value - 0.05, 0); + setValue(newValue); + }; + + return ( + + + + + + {isMobile && ( + handleVolumeButtonClick("decrease")}> + + + )} + + + + + + + + + + {isMobile && ( + handleVolumeButtonClick("increase")}> + + + )} + + + ); +}; From b1a3cc1f1d18c8b32bcd98997060216e5c96f99c Mon Sep 17 00:00:00 2001 From: Saelmala Date: Wed, 11 Dec 2024 14:39:52 +0100 Subject: [PATCH 06/26] fix: ui colume buttons on mobile fix --- src/components/production-line/production-line.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/production-line/production-line.tsx b/src/components/production-line/production-line.tsx index 55d52f4f..45dcc61d 100644 --- a/src/components/production-line/production-line.tsx +++ b/src/components/production-line/production-line.tsx @@ -74,11 +74,11 @@ const FlexButtonWrapper = styled.div` width: 50%; padding: 0 1rem 2rem 1rem; - :first-of-type { + &.first { padding-left: 0; } - :last-of-type { + &.last { padding-right: 0; } `; @@ -321,7 +321,7 @@ export const ProductionLine: FC = () => { increaseVolumeKey={savedHotkeys.increaseVolumeHotkey} decreaseVolumeKey={savedHotkeys.decreaseVolumeHotkey} /> - + muteOutput()}> {isOutputMuted ? : } @@ -329,7 +329,7 @@ export const ProductionLine: FC = () => { {inputAudioStream && inputAudioStream !== "no-device" && ( - + muteInput(!isInputMuted)} From e4a0026025fa1b34ea8e0e230e956399723aacea Mon Sep 17 00:00:00 2001 From: Saelmala Date: Wed, 11 Dec 2024 15:44:36 +0100 Subject: [PATCH 07/26] test: ios mobile test --- package.json | 3 +- src/audioContexts.ts | 6 ++++ src/bowser.ts | 2 ++ .../production-line/use-rtc-connection.ts | 28 +++++++++++++++++- .../volume-slider/volume-slider.tsx | 15 ++++++++-- yarn.lock | 29 +++++++++++++++++++ 6 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 src/audioContexts.ts diff --git a/package.json b/package.json index a6e4646b..24a54cbd 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "react-dom": "^18.3.1", "react-hook-form": "^7.51.5", "react-hotkeys-hook": "^4.5.0", - "react-router-dom": "^6.26.2" + "react-router-dom": "^6.26.2", + "standardized-audio-context": "^25.3.77" }, "devDependencies": { "@commitlint/cli": "^19.3.0", diff --git a/src/audioContexts.ts b/src/audioContexts.ts new file mode 100644 index 00000000..9ca11e74 --- /dev/null +++ b/src/audioContexts.ts @@ -0,0 +1,6 @@ +import { AudioContext } from "standardized-audio-context"; + +export const audioContexts = new Map< + HTMLAudioElement, + { context: AudioContext; gainNode: GainNode } +>(); diff --git a/src/bowser.ts b/src/bowser.ts index 104c9a86..0af924d9 100644 --- a/src/bowser.ts +++ b/src/bowser.ts @@ -13,3 +13,5 @@ export const isValidBrowser = browser.satisfies({ safari: ">=16.4", samsung: ">=21", }); + +export const isIosSafari = isMobile && browser.getBrowserName() === "Safari"; diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index 2556a95b..a2268011 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -7,6 +7,7 @@ import { useState, } from "react"; import { useNavigate } from "react-router-dom"; +import { IAudioContext } from "standardized-audio-context"; import { noop } from "../../helpers"; import { API } from "../../api/api.ts"; import { TJoinProductionOptions } from "./types.ts"; @@ -14,6 +15,8 @@ import { useGlobalState } from "../../global-state/context-provider.tsx"; import { TGlobalStateAction } from "../../global-state/global-state-actions.ts"; import { TUseAudioInputValues } from "./use-audio-input.ts"; import { startRtcStatInterval } from "./rtc-stat-interval.ts"; +import { isIosSafari } from "../../bowser.ts"; +import { audioContexts } from "../../audioContexts.ts"; type TRtcConnectionOptions = { inputAudioStream: TUseAudioInputValues; @@ -66,7 +69,21 @@ const establishConnection = ({ audioElement.controls = false; audioElement.autoplay = true; - audioElement.volume = 0.75; + if (isIosSafari) { + const audioContext = new AudioContext(); + const gainNode = audioContext.createGain(); + const source = audioContext.createMediaStreamSource(selectedStream); + + source.connect(gainNode).connect(audioContext.destination); + gainNode.gain.value = 0.75; + + audioContexts.set(audioElement, { + context: audioContext as unknown as IAudioContext, + gainNode, + }); + } else { + audioElement.volume = 0.75; + } audioElement.onerror = () => { dispatch({ @@ -253,6 +270,15 @@ export const useRtcConnection = ({ el.pause(); // eslint-disable-next-line no-param-reassign el.srcObject = null; + + if (isIosSafari && audioContexts.has(el)) { + const audioContextData = audioContexts.get(el); + if (audioContextData) { + const { context } = audioContextData; + context.close(); + audioContexts.delete(el); + } + } }); }, [audioElementsRef]); diff --git a/src/components/volume-slider/volume-slider.tsx b/src/components/volume-slider/volume-slider.tsx index 54226c30..836767e0 100644 --- a/src/components/volume-slider/volume-slider.tsx +++ b/src/components/volume-slider/volume-slider.tsx @@ -7,8 +7,9 @@ import { MinusIcon, PlusIcon, } from "../../assets/icons/icon"; -import { isMobile } from "../../bowser"; +import { isMobile, isIosSafari } from "../../bowser"; import { PrimaryButton } from "../landing-page/form-elements"; +import { audioContexts } from "../../audioContexts"; const SliderWrapper = styled.div` width: 100%; @@ -78,8 +79,16 @@ export const VolumeSlider: FC = ({ setValue(newValue); audioElements.forEach((audioElement) => { - // eslint-disable-next-line no-param-reassign - audioElement.volume = newValue; + if (isIosSafari && audioContexts.has(audioElement)) { + const audioContextData = audioContexts.get(audioElement); + if (audioContextData) { + const { gainNode } = audioContextData; + gainNode.gain.value = newValue; + } + } else { + // eslint-disable-next-line no-param-reassign + audioElement.volume = newValue; + } }); }; diff --git a/yarn.lock b/yarn.lock index 1a1dfa52..e510ef5b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -192,6 +192,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.25.6", "@babel/runtime@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" + integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.22.15", "@babel/template@^7.24.0": version "7.24.0" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50" @@ -1385,6 +1392,14 @@ ast-types-flow@^0.0.8: resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6" integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ== +automation-events@^7.0.9: + version "7.1.4" + resolved "https://registry.yarnpkg.com/automation-events/-/automation-events-7.1.4.tgz#c1171c036e928b55e21505a228de2caa2a8fd5d5" + integrity sha512-KCkUSGBKlZz2DTnjzHLnPzlTayExlkeIrzIgq5OqZgwvZsmgSsyQOXv/L+r8P14yR69xfSnwL2FmmzSCzI9o/g== + dependencies: + "@babel/runtime" "^7.26.0" + tslib "^2.8.1" + available-typed-arrays@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" @@ -3859,6 +3874,15 @@ split2@^4.0.0: resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== +standardized-audio-context@^25.3.77: + version "25.3.77" + resolved "https://registry.yarnpkg.com/standardized-audio-context/-/standardized-audio-context-25.3.77.tgz#9502c40f3860f0877ce5c53a161f819f23c978f4" + integrity sha512-Ki9zNz6pKcC5Pi+QPjPyVsD9GwJIJWgryji0XL9cAJXMGyn+dPOf6Qik1AHei0+UNVcc4BOCa0hWLBzlwqsW/A== + dependencies: + "@babel/runtime" "^7.25.6" + automation-events "^7.0.9" + tslib "^2.7.0" + stop-iteration-iterator@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" @@ -4065,6 +4089,11 @@ tslib@^2.0.3, tslib@^2.6.2: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== +tslib@^2.7.0, tslib@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" From c508a0cce13064945e0645afa2c8f5f9a98048b2 Mon Sep 17 00:00:00 2001 From: Saelmala Date: Wed, 11 Dec 2024 16:13:01 +0100 Subject: [PATCH 08/26] test: ios mobile volume --- .../production-line/use-rtc-connection.ts | 37 +++++++++++-------- .../volume-slider/volume-slider.tsx | 26 ++++++++----- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index a2268011..bb594dbf 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -7,7 +7,6 @@ import { useState, } from "react"; import { useNavigate } from "react-router-dom"; -import { IAudioContext } from "standardized-audio-context"; import { noop } from "../../helpers"; import { API } from "../../api/api.ts"; import { TJoinProductionOptions } from "./types.ts"; @@ -48,6 +47,26 @@ const attachInputAudioToPeerConnection = ({ .getTracks() .forEach((track) => rtcPeerConnection.addTrack(track)); +const initializeAudioContextForElement = ( + audioElement: HTMLAudioElement, + selectedStream: MediaStream +) => { + if (isIosSafari && !audioContexts.has(audioElement)) { + // eslint-disable-next-line + const audioContext = new ((window as any).AudioContext || + // eslint-disable-next-line + (window as any).webkitAudioContext)(); + const gainNode = audioContext.createGain(); + const source = audioContext.createMediaStreamSource(selectedStream); + + source.connect(gainNode); + gainNode.connect(audioContext.destination); + gainNode.gain.value = 0.75; + + audioContexts.set(audioElement, { context: audioContext, gainNode }); + } +}; + const establishConnection = ({ rtcPeerConnection, sdpOffer, @@ -69,21 +88,9 @@ const establishConnection = ({ audioElement.controls = false; audioElement.autoplay = true; - if (isIosSafari) { - const audioContext = new AudioContext(); - const gainNode = audioContext.createGain(); - const source = audioContext.createMediaStreamSource(selectedStream); - - source.connect(gainNode).connect(audioContext.destination); - gainNode.gain.value = 0.75; + audioElement.volume = 0.75; - audioContexts.set(audioElement, { - context: audioContext as unknown as IAudioContext, - gainNode, - }); - } else { - audioElement.volume = 0.75; - } + initializeAudioContextForElement(audioElement, selectedStream); audioElement.onerror = () => { dispatch({ diff --git a/src/components/volume-slider/volume-slider.tsx b/src/components/volume-slider/volume-slider.tsx index 836767e0..033cea5c 100644 --- a/src/components/volume-slider/volume-slider.tsx +++ b/src/components/volume-slider/volume-slider.tsx @@ -80,11 +80,8 @@ export const VolumeSlider: FC = ({ audioElements.forEach((audioElement) => { if (isIosSafari && audioContexts.has(audioElement)) { - const audioContextData = audioContexts.get(audioElement); - if (audioContextData) { - const { gainNode } = audioContextData; - gainNode.gain.value = newValue; - } + const { gainNode } = audioContexts.get(audioElement)!; + gainNode.gain.value = newValue; } else { // eslint-disable-next-line no-param-reassign audioElement.volume = newValue; @@ -93,14 +90,18 @@ export const VolumeSlider: FC = ({ }; const thumbPosition = value * 100; - useHotkeys(increaseVolumeKey || "u", () => { const newValue = Math.min(value + 0.05, 1); setValue(newValue); audioElements.forEach((audioElement) => { - // eslint-disable-next-line no-param-reassign - audioElement.volume = newValue; + if (isIosSafari && audioContexts.has(audioElement)) { + const { gainNode } = audioContexts.get(audioElement)!; + gainNode.gain.value = newValue; + } else { + // eslint-disable-next-line no-param-reassign + audioElement.volume = newValue; + } }); }); @@ -109,8 +110,13 @@ export const VolumeSlider: FC = ({ setValue(newValue); audioElements.forEach((audioElement) => { - // eslint-disable-next-line no-param-reassign - audioElement.volume = newValue; + if (isIosSafari && audioContexts.has(audioElement)) { + const { gainNode } = audioContexts.get(audioElement)!; + gainNode.gain.value = newValue; + } else { + // eslint-disable-next-line no-param-reassign + audioElement.volume = newValue; + } }); }); From 0e3e672ff7e2d85ffa4bd2f9f6dda61b71eaad3f Mon Sep 17 00:00:00 2001 From: Saelmala Date: Wed, 11 Dec 2024 16:18:57 +0100 Subject: [PATCH 09/26] test: add log --- src/components/production-line/use-rtc-connection.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index bb594dbf..35ae5293 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -51,6 +51,7 @@ const initializeAudioContextForElement = ( audioElement: HTMLAudioElement, selectedStream: MediaStream ) => { + console.log("IS IOS SAFARI", isIosSafari); if (isIosSafari && !audioContexts.has(audioElement)) { // eslint-disable-next-line const audioContext = new ((window as any).AudioContext || From 9277b99df8d994c35e290e0dd4d9ef57eea1b3f0 Mon Sep 17 00:00:00 2001 From: Saelmala Date: Thu, 12 Dec 2024 09:39:44 +0100 Subject: [PATCH 10/26] test: iphone volume control --- src/components/production-line/use-rtc-connection.ts | 9 +++++++++ src/components/volume-slider/volume-slider.tsx | 1 + 2 files changed, 10 insertions(+) diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index 35ae5293..9dd095b5 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -65,6 +65,15 @@ const initializeAudioContextForElement = ( gainNode.gain.value = 0.75; audioContexts.set(audioElement, { context: audioContext, gainNode }); + console.log("AudioContext map:", audioContexts); + + if (audioContext.state === "suspended") { + audioContext.resume().then(() => { + console.log("AudioContext resumed on iOS Safari."); + }); + } + + console.log("Audio context state: ", audioContext.state); } }; diff --git a/src/components/volume-slider/volume-slider.tsx b/src/components/volume-slider/volume-slider.tsx index 033cea5c..3ecf33b6 100644 --- a/src/components/volume-slider/volume-slider.tsx +++ b/src/components/volume-slider/volume-slider.tsx @@ -112,6 +112,7 @@ export const VolumeSlider: FC = ({ audioElements.forEach((audioElement) => { if (isIosSafari && audioContexts.has(audioElement)) { const { gainNode } = audioContexts.get(audioElement)!; + console.log("Setting gain value to:", newValue); gainNode.gain.value = newValue; } else { // eslint-disable-next-line no-param-reassign From 5d21c18d666e8dacd504b1d9deb8d5806622668c Mon Sep 17 00:00:00 2001 From: Saelmala Date: Thu, 12 Dec 2024 09:53:50 +0100 Subject: [PATCH 11/26] test: iphone volume control --- src/components/production-line/use-rtc-connection.ts | 5 +++++ src/components/volume-slider/volume-slider.tsx | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index 9dd095b5..eb9aa1dd 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -52,7 +52,12 @@ const initializeAudioContextForElement = ( selectedStream: MediaStream ) => { console.log("IS IOS SAFARI", isIosSafari); + console.log( + "audio contexts before if statement", + !audioContexts.has(audioElement) + ); if (isIosSafari && !audioContexts.has(audioElement)) { + console.log("INSIDE IF STATEMENT SAFARI APP"); // eslint-disable-next-line const audioContext = new ((window as any).AudioContext || // eslint-disable-next-line diff --git a/src/components/volume-slider/volume-slider.tsx b/src/components/volume-slider/volume-slider.tsx index 3ecf33b6..ac7b4680 100644 --- a/src/components/volume-slider/volume-slider.tsx +++ b/src/components/volume-slider/volume-slider.tsx @@ -110,7 +110,13 @@ export const VolumeSlider: FC = ({ setValue(newValue); audioElements.forEach((audioElement) => { + console.log("IS IOS SAFARI VOL SLIDER: ", isIosSafari); + console.log( + "AUDIO CONTEXTS HAS AUDIO ELEMENT: ", + audioContexts.has(audioElement) + ); if (isIosSafari && audioContexts.has(audioElement)) { + console.log("IS INSIDE IOS SAFARI VOLUME SLIDER"); const { gainNode } = audioContexts.get(audioElement)!; console.log("Setting gain value to:", newValue); gainNode.gain.value = newValue; From 60202ffcfd44e084ff75f9c238a6f1706ec2f660 Mon Sep 17 00:00:00 2001 From: Saelmala Date: Thu, 12 Dec 2024 10:35:02 +0100 Subject: [PATCH 12/26] test: logs for iphone volume control --- src/components/volume-slider/volume-slider.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/volume-slider/volume-slider.tsx b/src/components/volume-slider/volume-slider.tsx index ac7b4680..8946d110 100644 --- a/src/components/volume-slider/volume-slider.tsx +++ b/src/components/volume-slider/volume-slider.tsx @@ -95,8 +95,15 @@ export const VolumeSlider: FC = ({ setValue(newValue); audioElements.forEach((audioElement) => { + console.log("IS IOS SAFARI VOL SLIDER: ", isIosSafari); + console.log( + "AUDIO CONTEXTS HAS AUDIO ELEMENT: ", + audioContexts.has(audioElement) + ); if (isIosSafari && audioContexts.has(audioElement)) { + console.log("IS INSIDE IOS SAFARI VOLUME SLIDER"); const { gainNode } = audioContexts.get(audioElement)!; + console.log("Setting gain value to:", newValue); gainNode.gain.value = newValue; } else { // eslint-disable-next-line no-param-reassign @@ -110,11 +117,6 @@ export const VolumeSlider: FC = ({ setValue(newValue); audioElements.forEach((audioElement) => { - console.log("IS IOS SAFARI VOL SLIDER: ", isIosSafari); - console.log( - "AUDIO CONTEXTS HAS AUDIO ELEMENT: ", - audioContexts.has(audioElement) - ); if (isIosSafari && audioContexts.has(audioElement)) { console.log("IS INSIDE IOS SAFARI VOLUME SLIDER"); const { gainNode } = audioContexts.get(audioElement)!; From 104086be6d3ad01c3dd9877f1d3b6e69dc16aebe Mon Sep 17 00:00:00 2001 From: Saelmala Date: Thu, 12 Dec 2024 10:43:11 +0100 Subject: [PATCH 13/26] test: logs iphone volume control --- src/components/volume-slider/volume-slider.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/volume-slider/volume-slider.tsx b/src/components/volume-slider/volume-slider.tsx index 8946d110..16944b02 100644 --- a/src/components/volume-slider/volume-slider.tsx +++ b/src/components/volume-slider/volume-slider.tsx @@ -79,10 +79,18 @@ export const VolumeSlider: FC = ({ setValue(newValue); audioElements.forEach((audioElement) => { + console.log("IS IOS SAFARI VOL SLIDER: ", isIosSafari); + console.log( + "AUDIO CONTEXTS HAS AUDIO ELEMENT: ", + audioContexts.has(audioElement) + ); if (isIosSafari && audioContexts.has(audioElement)) { + console.log("IS INSIDE IOS SAFARI VOLUME SLIDER"); const { gainNode } = audioContexts.get(audioElement)!; + console.log("Setting gain value to:", newValue); gainNode.gain.value = newValue; } else { + console.log("SEtting volume to: ", newValue); // eslint-disable-next-line no-param-reassign audioElement.volume = newValue; } @@ -90,20 +98,14 @@ export const VolumeSlider: FC = ({ }; const thumbPosition = value * 100; + useHotkeys(increaseVolumeKey || "u", () => { const newValue = Math.min(value + 0.05, 1); setValue(newValue); audioElements.forEach((audioElement) => { - console.log("IS IOS SAFARI VOL SLIDER: ", isIosSafari); - console.log( - "AUDIO CONTEXTS HAS AUDIO ELEMENT: ", - audioContexts.has(audioElement) - ); if (isIosSafari && audioContexts.has(audioElement)) { - console.log("IS INSIDE IOS SAFARI VOLUME SLIDER"); const { gainNode } = audioContexts.get(audioElement)!; - console.log("Setting gain value to:", newValue); gainNode.gain.value = newValue; } else { // eslint-disable-next-line no-param-reassign From 5bfb75f31a0e051bce716fc075159e115875fe02 Mon Sep 17 00:00:00 2001 From: Saelmala Date: Thu, 12 Dec 2024 11:10:46 +0100 Subject: [PATCH 14/26] test: iphone volume control --- .../production-line/use-rtc-connection.ts | 34 ++++++++++++-- .../volume-slider/volume-slider.tsx | 47 +++++++++++-------- 2 files changed, 57 insertions(+), 24 deletions(-) diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index eb9aa1dd..0d3ecde4 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -72,11 +72,35 @@ const initializeAudioContextForElement = ( audioContexts.set(audioElement, { context: audioContext, gainNode }); console.log("AudioContext map:", audioContexts); - if (audioContext.state === "suspended") { - audioContext.resume().then(() => { - console.log("AudioContext resumed on iOS Safari."); - }); - } + const ensureRunningState = () => { + if (audioContext.state === "suspended") { + console.log("AudioContext state is suspended, resuming..."); + audioContext + .resume() + .then(() => { + console.log( + "AudioContext resumed and running: ", + audioContext.state + ); + }) + .catch((error: Error) => { + console.error( + "Failed to resume audio context for volume change in slider component", + error + ); + }); + } + }; + + ensureRunningState(); + + document.addEventListener( + "click", + () => { + ensureRunningState(); + }, + { once: true } + ); console.log("Audio context state: ", audioContext.state); } diff --git a/src/components/volume-slider/volume-slider.tsx b/src/components/volume-slider/volume-slider.tsx index 16944b02..70be3f33 100644 --- a/src/components/volume-slider/volume-slider.tsx +++ b/src/components/volume-slider/volume-slider.tsx @@ -86,9 +86,29 @@ export const VolumeSlider: FC = ({ ); if (isIosSafari && audioContexts.has(audioElement)) { console.log("IS INSIDE IOS SAFARI VOLUME SLIDER"); - const { gainNode } = audioContexts.get(audioElement)!; - console.log("Setting gain value to:", newValue); - gainNode.gain.value = newValue; + + const { context, gainNode } = audioContexts.get(audioElement)!; + + if (context.state === "suspended") { + context + .resume() + .then(() => { + console.log( + "Audio context resumed for volume change in slider component yay" + ); + console.log("Setting gain value to:", newValue); + gainNode.gain.value = newValue; + }) + .catch((error: Error) => { + console.error( + "Failed to resume audio context for volume change in slider component", + error + ); + }); + } else { + console.log("Setting gain value to:", newValue); + gainNode.gain.value = newValue; + } } else { console.log("SEtting volume to: ", newValue); // eslint-disable-next-line no-param-reassign @@ -104,13 +124,8 @@ export const VolumeSlider: FC = ({ setValue(newValue); audioElements.forEach((audioElement) => { - if (isIosSafari && audioContexts.has(audioElement)) { - const { gainNode } = audioContexts.get(audioElement)!; - gainNode.gain.value = newValue; - } else { - // eslint-disable-next-line no-param-reassign - audioElement.volume = newValue; - } + // eslint-disable-next-line no-param-reassign + audioElement.volume = newValue; }); }); @@ -119,15 +134,8 @@ export const VolumeSlider: FC = ({ setValue(newValue); audioElements.forEach((audioElement) => { - if (isIosSafari && audioContexts.has(audioElement)) { - console.log("IS INSIDE IOS SAFARI VOLUME SLIDER"); - const { gainNode } = audioContexts.get(audioElement)!; - console.log("Setting gain value to:", newValue); - gainNode.gain.value = newValue; - } else { - // eslint-disable-next-line no-param-reassign - audioElement.volume = newValue; - } + // eslint-disable-next-line no-param-reassign + audioElement.volume = newValue; }); }); @@ -137,6 +145,7 @@ export const VolumeSlider: FC = ({ ? Math.min(value + 0.05, 1) : Math.max(value - 0.05, 0); setValue(newValue); + // TODO: Fix for iOS }; return ( From 5a9d06ef3b494aaacc4ac391336326b33e2f1325 Mon Sep 17 00:00:00 2001 From: Saelmala Date: Thu, 12 Dec 2024 11:15:08 +0100 Subject: [PATCH 15/26] test: volume control ios --- src/components/volume-slider/volume-slider.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/volume-slider/volume-slider.tsx b/src/components/volume-slider/volume-slider.tsx index 70be3f33..701f600c 100644 --- a/src/components/volume-slider/volume-slider.tsx +++ b/src/components/volume-slider/volume-slider.tsx @@ -88,6 +88,7 @@ export const VolumeSlider: FC = ({ console.log("IS INSIDE IOS SAFARI VOLUME SLIDER"); const { context, gainNode } = audioContexts.get(audioElement)!; + console.log("STATE OF AUDIO CONTEXT: ", context.state); if (context.state === "suspended") { context From b85365fca188bbc71a3aec1d5da6fe8857eaa183 Mon Sep 17 00:00:00 2001 From: Saelmala Date: Thu, 12 Dec 2024 11:47:49 +0100 Subject: [PATCH 16/26] test: logging gainNode.gain.value after assign --- src/components/volume-slider/volume-slider.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/volume-slider/volume-slider.tsx b/src/components/volume-slider/volume-slider.tsx index 701f600c..fc2a6dd5 100644 --- a/src/components/volume-slider/volume-slider.tsx +++ b/src/components/volume-slider/volume-slider.tsx @@ -99,6 +99,7 @@ export const VolumeSlider: FC = ({ ); console.log("Setting gain value to:", newValue); gainNode.gain.value = newValue; + console.log("ACTUAL Gain value set to:", gainNode.gain.value); }) .catch((error: Error) => { console.error( @@ -109,6 +110,7 @@ export const VolumeSlider: FC = ({ } else { console.log("Setting gain value to:", newValue); gainNode.gain.value = newValue; + console.log("ACTUAL Gain value set to:", gainNode.gain.value); } } else { console.log("SEtting volume to: ", newValue); From e5511eb07ba1f4352f92f7758c11ce46e85a6de0 Mon Sep 17 00:00:00 2001 From: Saelmala Date: Thu, 12 Dec 2024 11:56:46 +0100 Subject: [PATCH 17/26] test: ios issues --- .../production-line/use-rtc-connection.ts | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index 0d3ecde4..c91c19b8 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -122,35 +122,40 @@ const establishConnection = ({ if (selectedStream && selectedStream.getAudioTracks().length !== 0) { const audioElement = new Audio(); - audioElement.srcObject = selectedStream; + if (isIosSafari) { + initializeAudioContextForElement(audioElement, selectedStream); + } else { + audioElement.srcObject = selectedStream; - audioElement.controls = false; - audioElement.autoplay = true; + audioElement.controls = false; + audioElement.autoplay = true; - audioElement.volume = 0.75; + audioElement.volume = 0.75; - initializeAudioContextForElement(audioElement, selectedStream); - - audioElement.onerror = () => { - dispatch({ - type: "ERROR", - payload: new Error( - `Audio Error: ${audioElement.error?.code} - ${audioElement.error?.message}` - ), - }); - }; - - audioElement.srcObject = selectedStream; - - setAudioElements((prevArray) => [audioElement, ...prevArray]); - if (joinProductionOptions.audiooutput) { - audioElement.setSinkId(joinProductionOptions.audiooutput).catch((e) => { + audioElement.onerror = () => { dispatch({ type: "ERROR", - payload: - e instanceof Error ? e : new Error("Error assigning audio sink."), + payload: new Error( + `Audio Error: ${audioElement.error?.code} - ${audioElement.error?.message}` + ), }); - }); + }; + + audioElement.srcObject = selectedStream; + setAudioElements((prevArray) => [audioElement, ...prevArray]); + if (joinProductionOptions.audiooutput) { + audioElement + .setSinkId(joinProductionOptions.audiooutput) + .catch((e) => { + dispatch({ + type: "ERROR", + payload: + e instanceof Error + ? e + : new Error("Error assigning audio sink."), + }); + }); + } } } else if (selectedStream && selectedStream.getAudioTracks().length === 0) { setNoStreamError(true); From 9e9bfd043cc6519884553730a1525f6de25d84d6 Mon Sep 17 00:00:00 2001 From: Saelmala Date: Thu, 12 Dec 2024 12:09:40 +0100 Subject: [PATCH 18/26] test: ios problems --- .../production-line/use-rtc-connection.ts | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index c91c19b8..de649462 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -124,38 +124,37 @@ const establishConnection = ({ if (isIosSafari) { initializeAudioContextForElement(audioElement, selectedStream); - } else { - audioElement.srcObject = selectedStream; + } + + audioElement.srcObject = selectedStream; - audioElement.controls = false; - audioElement.autoplay = true; + audioElement.controls = false; + audioElement.autoplay = true; + if (!isIosSafari) { audioElement.volume = 0.75; + } - audioElement.onerror = () => { + audioElement.onerror = () => { + dispatch({ + type: "ERROR", + payload: new Error( + `Audio Error: ${audioElement.error?.code} - ${audioElement.error?.message}` + ), + }); + }; + + // audioElement.srcObject = selectedStream; + + setAudioElements((prevArray) => [audioElement, ...prevArray]); + if (joinProductionOptions.audiooutput) { + audioElement.setSinkId(joinProductionOptions.audiooutput).catch((e) => { dispatch({ type: "ERROR", - payload: new Error( - `Audio Error: ${audioElement.error?.code} - ${audioElement.error?.message}` - ), + payload: + e instanceof Error ? e : new Error("Error assigning audio sink."), }); - }; - - audioElement.srcObject = selectedStream; - setAudioElements((prevArray) => [audioElement, ...prevArray]); - if (joinProductionOptions.audiooutput) { - audioElement - .setSinkId(joinProductionOptions.audiooutput) - .catch((e) => { - dispatch({ - type: "ERROR", - payload: - e instanceof Error - ? e - : new Error("Error assigning audio sink."), - }); - }); - } + }); } } else if (selectedStream && selectedStream.getAudioTracks().length === 0) { setNoStreamError(true); From 6acd8b93265a922c92e54ed23affb6510b3b4cc1 Mon Sep 17 00:00:00 2001 From: Saelmala Date: Thu, 12 Dec 2024 12:18:42 +0100 Subject: [PATCH 19/26] test: iphone volume problems --- src/components/production-line/use-rtc-connection.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index de649462..d09ab0e6 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -67,7 +67,7 @@ const initializeAudioContextForElement = ( source.connect(gainNode); gainNode.connect(audioContext.destination); - gainNode.gain.value = 0.75; + gainNode.gain.value = 0; audioContexts.set(audioElement, { context: audioContext, gainNode }); console.log("AudioContext map:", audioContexts); From ff1f60c0fd929efda067b067dafe1ee9993609dd Mon Sep 17 00:00:00 2001 From: Saelmala Date: Thu, 12 Dec 2024 13:54:20 +0100 Subject: [PATCH 20/26] test: ios volume control --- .../production-line/use-rtc-connection.ts | 99 +++++++------------ .../volume-slider/volume-slider.tsx | 1 + 2 files changed, 37 insertions(+), 63 deletions(-) diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index d09ab0e6..7612f1d7 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -39,6 +39,38 @@ type TAttachAudioStream = { rtcPeerConnection: RTCPeerConnection; }; +const handleVolumeChange = (event: Event) => { + const slider = event.target as HTMLInputElement; + const newVolume = parseFloat(slider.value); + console.log("Volume slider changed to in initialize: ", newVolume); + + audioContexts.forEach(({ gainNode }) => { + // eslint-disable-next-line no-param-reassign + gainNode.gain.value = newVolume; + }); +}; + +const initializeAudioContextForElement = (audioElement: HTMLAudioElement) => { + const AudioContext = + // eslint-disable-next-line + window.AudioContext || (window as any).webkitAudioContext; + const audioCtx = new AudioContext(); + + const track = audioCtx.createMediaElementSource(audioElement); + + if (audioCtx.state === "suspended") { + audioCtx.resume(); + } + + const gainNode = audioCtx.createGain(); + const volumeControl = document.getElementById( + "volumeSlider" + ) as HTMLInputElement; + volumeControl.addEventListener("input", handleVolumeChange); + + track.connect(gainNode).connect(audioCtx.destination); +}; + const attachInputAudioToPeerConnection = ({ inputAudioStream, rtcPeerConnection, @@ -47,65 +79,6 @@ const attachInputAudioToPeerConnection = ({ .getTracks() .forEach((track) => rtcPeerConnection.addTrack(track)); -const initializeAudioContextForElement = ( - audioElement: HTMLAudioElement, - selectedStream: MediaStream -) => { - console.log("IS IOS SAFARI", isIosSafari); - console.log( - "audio contexts before if statement", - !audioContexts.has(audioElement) - ); - if (isIosSafari && !audioContexts.has(audioElement)) { - console.log("INSIDE IF STATEMENT SAFARI APP"); - // eslint-disable-next-line - const audioContext = new ((window as any).AudioContext || - // eslint-disable-next-line - (window as any).webkitAudioContext)(); - const gainNode = audioContext.createGain(); - const source = audioContext.createMediaStreamSource(selectedStream); - - source.connect(gainNode); - gainNode.connect(audioContext.destination); - gainNode.gain.value = 0; - - audioContexts.set(audioElement, { context: audioContext, gainNode }); - console.log("AudioContext map:", audioContexts); - - const ensureRunningState = () => { - if (audioContext.state === "suspended") { - console.log("AudioContext state is suspended, resuming..."); - audioContext - .resume() - .then(() => { - console.log( - "AudioContext resumed and running: ", - audioContext.state - ); - }) - .catch((error: Error) => { - console.error( - "Failed to resume audio context for volume change in slider component", - error - ); - }); - } - }; - - ensureRunningState(); - - document.addEventListener( - "click", - () => { - ensureRunningState(); - }, - { once: true } - ); - - console.log("Audio context state: ", audioContext.state); - } -}; - const establishConnection = ({ rtcPeerConnection, sdpOffer, @@ -122,15 +95,15 @@ const establishConnection = ({ if (selectedStream && selectedStream.getAudioTracks().length !== 0) { const audioElement = new Audio(); - if (isIosSafari) { - initializeAudioContextForElement(audioElement, selectedStream); - } - audioElement.srcObject = selectedStream; audioElement.controls = false; audioElement.autoplay = true; + if (isIosSafari) { + initializeAudioContextForElement(audioElement); + } + if (!isIosSafari) { audioElement.volume = 0.75; } diff --git a/src/components/volume-slider/volume-slider.tsx b/src/components/volume-slider/volume-slider.tsx index fc2a6dd5..b498a1bf 100644 --- a/src/components/volume-slider/volume-slider.tsx +++ b/src/components/volume-slider/volume-slider.tsx @@ -166,6 +166,7 @@ export const VolumeSlider: FC = ({ Date: Thu, 12 Dec 2024 14:42:19 +0100 Subject: [PATCH 21/26] test: iphone volume control --- .../production-line/use-rtc-connection.ts | 7 ++- .../volume-slider/volume-slider.tsx | 50 +++++++++---------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index 7612f1d7..c06bfa46 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -52,6 +52,7 @@ const handleVolumeChange = (event: Event) => { const initializeAudioContextForElement = (audioElement: HTMLAudioElement) => { const AudioContext = + // TODO Fixa detta så att det inte blir any, ändra om när det funkar // eslint-disable-next-line window.AudioContext || (window as any).webkitAudioContext; const audioCtx = new AudioContext(); @@ -68,6 +69,8 @@ const initializeAudioContextForElement = (audioElement: HTMLAudioElement) => { ) as HTMLInputElement; volumeControl.addEventListener("input", handleVolumeChange); + console.log("AUDIOCTX DESTINATION: ", audioCtx.destination); + track.connect(gainNode).connect(audioCtx.destination); }; @@ -104,10 +107,6 @@ const establishConnection = ({ initializeAudioContextForElement(audioElement); } - if (!isIosSafari) { - audioElement.volume = 0.75; - } - audioElement.onerror = () => { dispatch({ type: "ERROR", diff --git a/src/components/volume-slider/volume-slider.tsx b/src/components/volume-slider/volume-slider.tsx index b498a1bf..51f02375 100644 --- a/src/components/volume-slider/volume-slider.tsx +++ b/src/components/volume-slider/volume-slider.tsx @@ -87,31 +87,31 @@ export const VolumeSlider: FC = ({ if (isIosSafari && audioContexts.has(audioElement)) { console.log("IS INSIDE IOS SAFARI VOLUME SLIDER"); - const { context, gainNode } = audioContexts.get(audioElement)!; - console.log("STATE OF AUDIO CONTEXT: ", context.state); - - if (context.state === "suspended") { - context - .resume() - .then(() => { - console.log( - "Audio context resumed for volume change in slider component yay" - ); - console.log("Setting gain value to:", newValue); - gainNode.gain.value = newValue; - console.log("ACTUAL Gain value set to:", gainNode.gain.value); - }) - .catch((error: Error) => { - console.error( - "Failed to resume audio context for volume change in slider component", - error - ); - }); - } else { - console.log("Setting gain value to:", newValue); - gainNode.gain.value = newValue; - console.log("ACTUAL Gain value set to:", gainNode.gain.value); - } + // const { context, gainNode } = audioContexts.get(audioElement)!; + // console.log("STATE OF AUDIO CONTEXT: ", context.state); + + // if (context.state === "suspended") { + // context + // .resume() + // .then(() => { + // console.log( + // "Audio context resumed for volume change in slider component yay" + // ); + // console.log("Setting gain value to:", newValue); + // gainNode.gain.value = newValue; + // console.log("ACTUAL Gain value set to:", gainNode.gain.value); + // }) + // .catch((error: Error) => { + // console.error( + // "Failed to resume audio context for volume change in slider component", + // error + // ); + // }); + // } else { + // console.log("Setting gain value to:", newValue); + // gainNode.gain.value = newValue; + // console.log("ACTUAL Gain value set to:", gainNode.gain.value); + // } } else { console.log("SEtting volume to: ", newValue); // eslint-disable-next-line no-param-reassign From 1d35b3b319b76588d5b805dfbe916149c5bcd3da Mon Sep 17 00:00:00 2001 From: Saelmala Date: Fri, 13 Dec 2024 15:36:05 +0100 Subject: [PATCH 22/26] fix: remove non-working code --- src/audioContexts.ts | 6 --- .../production-line/use-rtc-connection.ts | 52 +------------------ .../volume-slider/volume-slider.tsx | 44 ++-------------- 3 files changed, 5 insertions(+), 97 deletions(-) delete mode 100644 src/audioContexts.ts diff --git a/src/audioContexts.ts b/src/audioContexts.ts deleted file mode 100644 index 9ca11e74..00000000 --- a/src/audioContexts.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { AudioContext } from "standardized-audio-context"; - -export const audioContexts = new Map< - HTMLAudioElement, - { context: AudioContext; gainNode: GainNode } ->(); diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index c06bfa46..014e7b03 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -14,8 +14,6 @@ import { useGlobalState } from "../../global-state/context-provider.tsx"; import { TGlobalStateAction } from "../../global-state/global-state-actions.ts"; import { TUseAudioInputValues } from "./use-audio-input.ts"; import { startRtcStatInterval } from "./rtc-stat-interval.ts"; -import { isIosSafari } from "../../bowser.ts"; -import { audioContexts } from "../../audioContexts.ts"; type TRtcConnectionOptions = { inputAudioStream: TUseAudioInputValues; @@ -39,41 +37,6 @@ type TAttachAudioStream = { rtcPeerConnection: RTCPeerConnection; }; -const handleVolumeChange = (event: Event) => { - const slider = event.target as HTMLInputElement; - const newVolume = parseFloat(slider.value); - console.log("Volume slider changed to in initialize: ", newVolume); - - audioContexts.forEach(({ gainNode }) => { - // eslint-disable-next-line no-param-reassign - gainNode.gain.value = newVolume; - }); -}; - -const initializeAudioContextForElement = (audioElement: HTMLAudioElement) => { - const AudioContext = - // TODO Fixa detta så att det inte blir any, ändra om när det funkar - // eslint-disable-next-line - window.AudioContext || (window as any).webkitAudioContext; - const audioCtx = new AudioContext(); - - const track = audioCtx.createMediaElementSource(audioElement); - - if (audioCtx.state === "suspended") { - audioCtx.resume(); - } - - const gainNode = audioCtx.createGain(); - const volumeControl = document.getElementById( - "volumeSlider" - ) as HTMLInputElement; - volumeControl.addEventListener("input", handleVolumeChange); - - console.log("AUDIOCTX DESTINATION: ", audioCtx.destination); - - track.connect(gainNode).connect(audioCtx.destination); -}; - const attachInputAudioToPeerConnection = ({ inputAudioStream, rtcPeerConnection, @@ -103,10 +66,6 @@ const establishConnection = ({ audioElement.controls = false; audioElement.autoplay = true; - if (isIosSafari) { - initializeAudioContextForElement(audioElement); - } - audioElement.onerror = () => { dispatch({ type: "ERROR", @@ -116,7 +75,7 @@ const establishConnection = ({ }); }; - // audioElement.srcObject = selectedStream; + audioElement.srcObject = selectedStream; setAudioElements((prevArray) => [audioElement, ...prevArray]); if (joinProductionOptions.audiooutput) { @@ -292,15 +251,6 @@ export const useRtcConnection = ({ el.pause(); // eslint-disable-next-line no-param-reassign el.srcObject = null; - - if (isIosSafari && audioContexts.has(el)) { - const audioContextData = audioContexts.get(el); - if (audioContextData) { - const { context } = audioContextData; - context.close(); - audioContexts.delete(el); - } - } }); }, [audioElementsRef]); diff --git a/src/components/volume-slider/volume-slider.tsx b/src/components/volume-slider/volume-slider.tsx index 51f02375..3c417713 100644 --- a/src/components/volume-slider/volume-slider.tsx +++ b/src/components/volume-slider/volume-slider.tsx @@ -7,9 +7,8 @@ import { MinusIcon, PlusIcon, } from "../../assets/icons/icon"; -import { isMobile, isIosSafari } from "../../bowser"; +import { isMobile } from "../../bowser"; import { PrimaryButton } from "../landing-page/form-elements"; -import { audioContexts } from "../../audioContexts"; const SliderWrapper = styled.div` width: 100%; @@ -79,44 +78,9 @@ export const VolumeSlider: FC = ({ setValue(newValue); audioElements.forEach((audioElement) => { - console.log("IS IOS SAFARI VOL SLIDER: ", isIosSafari); - console.log( - "AUDIO CONTEXTS HAS AUDIO ELEMENT: ", - audioContexts.has(audioElement) - ); - if (isIosSafari && audioContexts.has(audioElement)) { - console.log("IS INSIDE IOS SAFARI VOLUME SLIDER"); - - // const { context, gainNode } = audioContexts.get(audioElement)!; - // console.log("STATE OF AUDIO CONTEXT: ", context.state); - - // if (context.state === "suspended") { - // context - // .resume() - // .then(() => { - // console.log( - // "Audio context resumed for volume change in slider component yay" - // ); - // console.log("Setting gain value to:", newValue); - // gainNode.gain.value = newValue; - // console.log("ACTUAL Gain value set to:", gainNode.gain.value); - // }) - // .catch((error: Error) => { - // console.error( - // "Failed to resume audio context for volume change in slider component", - // error - // ); - // }); - // } else { - // console.log("Setting gain value to:", newValue); - // gainNode.gain.value = newValue; - // console.log("ACTUAL Gain value set to:", gainNode.gain.value); - // } - } else { - console.log("SEtting volume to: ", newValue); - // eslint-disable-next-line no-param-reassign - audioElement.volume = newValue; - } + console.log("Setting volume to: ", newValue); + // eslint-disable-next-line no-param-reassign + audioElement.volume = newValue; }); }; From b85198046859ac2b159a58b238f74780a11443f3 Mon Sep 17 00:00:00 2001 From: Saelmala Date: Mon, 16 Dec 2024 09:05:58 +0100 Subject: [PATCH 23/26] fix: small updates --- package.json | 3 +-- src/assets/icons/full_sound.svg | 4 ++- src/assets/icons/minus.svg | 4 ++- src/assets/icons/no_sound.svg | 4 ++- src/assets/icons/plus.svg | 4 ++- src/bowser.ts | 2 -- .../production-line/production-line.tsx | 10 +++---- .../production-line/use-rtc-connection.ts | 2 -- .../volume-slider/volume-slider.tsx | 26 ++++++++++++------- yarn.lock | 13 ---------- 10 files changed, 34 insertions(+), 38 deletions(-) diff --git a/package.json b/package.json index 6c059d31..4e9bae53 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,7 @@ "react-dom": "^18.3.1", "react-hook-form": "^7.51.5", "react-hotkeys-hook": "^4.5.0", - "react-router-dom": "^6.26.2", - "standardized-audio-context": "^25.3.77" + "react-router-dom": "^6.26.2" }, "devDependencies": { "@commitlint/cli": "^19.3.0", diff --git a/src/assets/icons/full_sound.svg b/src/assets/icons/full_sound.svg index 75c25155..147dc869 100644 --- a/src/assets/icons/full_sound.svg +++ b/src/assets/icons/full_sound.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/src/assets/icons/minus.svg b/src/assets/icons/minus.svg index edeb6eb3..8b013dde 100644 --- a/src/assets/icons/minus.svg +++ b/src/assets/icons/minus.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/src/assets/icons/no_sound.svg b/src/assets/icons/no_sound.svg index 50811799..17b8f3a5 100644 --- a/src/assets/icons/no_sound.svg +++ b/src/assets/icons/no_sound.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/src/assets/icons/plus.svg b/src/assets/icons/plus.svg index 6c6665fc..b343ee36 100644 --- a/src/assets/icons/plus.svg +++ b/src/assets/icons/plus.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/src/bowser.ts b/src/bowser.ts index 0af924d9..104c9a86 100644 --- a/src/bowser.ts +++ b/src/bowser.ts @@ -13,5 +13,3 @@ export const isValidBrowser = browser.satisfies({ safari: ">=16.4", samsung: ">=21", }); - -export const isIosSafari = isMobile && browser.getBrowserName() === "Safari"; diff --git a/src/components/production-line/production-line.tsx b/src/components/production-line/production-line.tsx index 034a1d84..0f7d3f94 100644 --- a/src/components/production-line/production-line.tsx +++ b/src/components/production-line/production-line.tsx @@ -338,12 +338,12 @@ export const ProductionLine = ({ }} > Controls + - muteOutput()}> diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index 6e2a2466..dbf7bc70 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -65,10 +65,8 @@ const establishConnection = ({ const audioElement = new Audio(); audioElement.srcObject = selectedStream; - audioElement.controls = false; audioElement.autoplay = true; - audioElement.onerror = () => { dispatch({ type: "ERROR", diff --git a/src/components/volume-slider/volume-slider.tsx b/src/components/volume-slider/volume-slider.tsx index 3c417713..23971b3c 100644 --- a/src/components/volume-slider/volume-slider.tsx +++ b/src/components/volume-slider/volume-slider.tsx @@ -15,12 +15,14 @@ const SliderWrapper = styled.div` margin: 2rem 0; display: flex; flex-direction: row; + align-items: center; position: relative; `; const VolumeContainer = styled.div` display: flex; flex-direction: column; + align-items: center; `; const IconWrapper = styled.div` @@ -35,9 +37,7 @@ const SliderTrack = styled.div` background-color: #e0e0e0; border-radius: 0.2rem; position: relative; - margin-top: 2.5rem; `; -// TODO: Remove margin-top and solve in a better way const SliderThumb = styled.div<{ position: number }>` width: 1.5rem; @@ -60,6 +60,12 @@ const VolumeButton = styled(PrimaryButton)` margin-top: 1rem; `; +const VolumeButtonContainer = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; +`; + type TVolumeSliderProps = { audioElements: HTMLAudioElement[]; increaseVolumeKey?: string; @@ -121,11 +127,6 @@ export const VolumeSlider: FC = ({ - {isMobile && ( - handleVolumeButtonClick("decrease")}> - - - )} @@ -151,12 +152,17 @@ export const VolumeSlider: FC = ({ - {isMobile && ( + + {isMobile && ( + + handleVolumeButtonClick("decrease")}> + + handleVolumeButtonClick("increase")}> - )} - + + )} ); }; diff --git a/yarn.lock b/yarn.lock index 5ee81bf9..0bd84179 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1270,14 +1270,6 @@ ast-types-flow@^0.0.8: resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6" integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ== -automation-events@^7.0.9: - version "7.1.4" - resolved "https://registry.yarnpkg.com/automation-events/-/automation-events-7.1.4.tgz#c1171c036e928b55e21505a228de2caa2a8fd5d5" - integrity sha512-KCkUSGBKlZz2DTnjzHLnPzlTayExlkeIrzIgq5OqZgwvZsmgSsyQOXv/L+r8P14yR69xfSnwL2FmmzSCzI9o/g== - dependencies: - "@babel/runtime" "^7.26.0" - tslib "^2.8.1" - available-typed-arrays@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" @@ -3746,11 +3738,6 @@ tslib@^2.0.3, tslib@^2.6.2: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== -tslib@^2.7.0, tslib@^2.8.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" - integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" From 4946fa7a7fa56f7b538b07d585880506ef27b799 Mon Sep 17 00:00:00 2001 From: Saelmala Date: Mon, 16 Dec 2024 09:26:55 +0100 Subject: [PATCH 24/26] fix: ui fixes --- src/assets/icons/minus.svg | 2 +- src/assets/icons/plus.svg | 2 +- .../production-line/exit-call-button.tsx | 2 + .../volume-slider/volume-slider.tsx | 78 +++++++++++-------- 4 files changed, 49 insertions(+), 35 deletions(-) diff --git a/src/assets/icons/minus.svg b/src/assets/icons/minus.svg index 8b013dde..eacef25a 100644 --- a/src/assets/icons/minus.svg +++ b/src/assets/icons/minus.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/assets/icons/plus.svg b/src/assets/icons/plus.svg index b343ee36..be10a07a 100644 --- a/src/assets/icons/plus.svg +++ b/src/assets/icons/plus.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/components/production-line/exit-call-button.tsx b/src/components/production-line/exit-call-button.tsx index 94b4370f..0c3c5480 100644 --- a/src/components/production-line/exit-call-button.tsx +++ b/src/components/production-line/exit-call-button.tsx @@ -6,6 +6,8 @@ const StyledBackBtn = styled(PrimaryButton)` padding: 0; margin: 0; width: 4rem; + background: #32383b; + border: 0.2rem solid #6d6d6d; `; export const ExitCallButton = ({ diff --git a/src/components/volume-slider/volume-slider.tsx b/src/components/volume-slider/volume-slider.tsx index 23971b3c..d7dbf44f 100644 --- a/src/components/volume-slider/volume-slider.tsx +++ b/src/components/volume-slider/volume-slider.tsx @@ -8,13 +8,13 @@ import { PlusIcon, } from "../../assets/icons/icon"; import { isMobile } from "../../bowser"; -import { PrimaryButton } from "../landing-page/form-elements"; +import { ActionButton } from "../landing-page/form-elements"; const SliderWrapper = styled.div` width: 100%; margin: 2rem 0; display: flex; - flex-direction: row; + flex-direction: column; align-items: center; position: relative; `; @@ -51,19 +51,29 @@ const SliderThumb = styled.div<{ position: number }>` cursor: pointer; `; -const VolumeButton = styled(PrimaryButton)` +const VolumeButton = styled(ActionButton)` + background-color: #32383b; width: 7rem; align-items: center; height: 4.5rem; padding: 1.5rem; cursor: pointer; margin-top: 1rem; + border: 0.2rem solid #6d6d6d; `; const VolumeButtonContainer = styled.div` display: flex; flex-direction: row; justify-content: space-between; + width: 100%; +`; + +const VolumeWrapper = styled.div` + display: flex; + flex-direction: row; + width: 100%; + align-items: center; `; type TVolumeSliderProps = { @@ -123,36 +133,38 @@ export const VolumeSlider: FC = ({ return ( - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + {isMobile && ( handleVolumeButtonClick("decrease")}> From 95058e63986bf0c7ba8c5b61694627a5aea1503e Mon Sep 17 00:00:00 2001 From: Saelmala Date: Mon, 16 Dec 2024 09:28:41 +0100 Subject: [PATCH 25/26] fix: remove added line --- src/components/production-line/use-rtc-connection.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index dbf7bc70..3ffed3df 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -64,7 +64,6 @@ const establishConnection = ({ if (selectedStream && selectedStream.getAudioTracks().length !== 0) { const audioElement = new Audio(); - audioElement.srcObject = selectedStream; audioElement.controls = false; audioElement.autoplay = true; audioElement.onerror = () => { From 8250ad065a5d83b9e8579c16843d6824d2387010 Mon Sep 17 00:00:00 2001 From: Saelmala Date: Mon, 16 Dec 2024 10:51:29 +0100 Subject: [PATCH 26/26] fixup! --- src/components/production-line/settings-modal.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/production-line/settings-modal.tsx b/src/components/production-line/settings-modal.tsx index df179a81..4357873f 100644 --- a/src/components/production-line/settings-modal.tsx +++ b/src/components/production-line/settings-modal.tsx @@ -109,7 +109,7 @@ export const SettingsModal = ({ (val) => val && val === currentValues[field] ).length > 1; - if (!currentValues[field] || currentValues[field] === "") { + if (!currentValues[field]) { acc[field] = "This field can not be empty."; } else if (isDuplicate) { acc[field] = "This key is already in use."; @@ -137,9 +137,7 @@ export const SettingsModal = ({ const handleSave = () => { const hasErrors = Object.values(errors).some((error) => error !== ""); - const hasEmptyFields = Object.values(hotkeys).some( - (value) => !value || value === "" - ); + const hasEmptyFields = Object.values(hotkeys).some((value) => !value); if (hasErrors || hasEmptyFields) { return;