From 7be0c701a494fffb15044094bbd6dc09fa429747 Mon Sep 17 00:00:00 2001 From: PxlLoewe <72106766+PxlLoewe@users.noreply.github.com> Date: Wed, 16 Jul 2025 23:24:55 -0700 Subject: [PATCH] =?UTF-8?q?Funk-effekt=20und=20Mikrifon-Einstellungen=20hi?= =?UTF-8?q?nzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/_components/navbar/Settings.tsx | 9 ++-- apps/dispatch/app/_helpers/radioEffect.ts | 8 +-- apps/dispatch/app/_store/audioStore.ts | 50 ++++++++++++++++--- 3 files changed, 49 insertions(+), 18 deletions(-) diff --git a/apps/dispatch/app/_components/navbar/Settings.tsx b/apps/dispatch/app/_components/navbar/Settings.tsx index 65c53cea..72ff3fe9 100644 --- a/apps/dispatch/app/_components/navbar/Settings.tsx +++ b/apps/dispatch/app/_components/navbar/Settings.tsx @@ -11,6 +11,7 @@ import toast from "react-hot-toast"; export const SettingsBtn = () => { const session = useSession(); + const { data: user } = useQuery({ queryKey: ["user", session.data?.user.id], queryFn: () => getUserAPI(session.data!.user.id), @@ -38,7 +39,7 @@ export const SettingsBtn = () => { const [funkVolume, setFunkVol] = useState(0.8); const [dmeVolume, setDmeVol] = useState(0.8); - const setMic = useAudioStore((state) => state.setMic); + const { setMic } = useAudioStore((state) => state); useEffect(() => { if (user) { @@ -106,10 +107,6 @@ export const SettingsBtn = () => {

Eingabelautstärke

- {/* - - TODO: Livekit Kann aktuell keine Lautstärke manuell überschreiben, daher ist die MicVolumeBar deaktiviert -
{
{showIndication && ( - )} */} + )}

diff --git a/apps/dispatch/app/_helpers/radioEffect.ts b/apps/dispatch/app/_helpers/radioEffect.ts index a7e486b1..ab2a7f88 100644 --- a/apps/dispatch/app/_helpers/radioEffect.ts +++ b/apps/dispatch/app/_helpers/radioEffect.ts @@ -1,5 +1,3 @@ -import { AudioTrack, RemoteAudioTrack, RemoteTrack } from "livekit-client"; - // Helper function for distortion curve generation function createDistortionCurve(amount: number): Float32Array { const k = typeof amount === "number" ? amount : 50; @@ -14,12 +12,10 @@ function createDistortionCurve(amount: number): Float32Array { return curve; } -export const getRadioStream = (track: RemoteAudioTrack, volume: number): MediaStream | null => { +export const getRadioStream = (stream: MediaStream, volume: number): MediaStream | null => { try { const audioContext = new window.AudioContext(); - const sourceNode = audioContext.createMediaStreamSource( - new MediaStream([track.mediaStreamTrack]), - ); + const sourceNode = audioContext.createMediaStreamSource(stream); const destinationNode = audioContext.createMediaStreamDestination(); const gainNode = audioContext.createGain(); diff --git a/apps/dispatch/app/_store/audioStore.ts b/apps/dispatch/app/_store/audioStore.ts index 6bf09c61..fc4e5a6c 100644 --- a/apps/dispatch/app/_store/audioStore.ts +++ b/apps/dispatch/app/_store/audioStore.ts @@ -5,12 +5,21 @@ import { handleTrackSubscribed, handleTrackUnsubscribed, } from "_helpers/liveKitEventHandler"; -import { ConnectionQuality, Participant, Room, RoomEvent, RpcInvocationData } from "livekit-client"; +import { + ConnectionQuality, + LocalTrackPublication, + Participant, + Room, + RoomEvent, + RpcInvocationData, + Track, +} from "livekit-client"; import { pilotSocket } from "(app)/pilot/socket"; import { create } from "zustand"; import axios from "axios"; import { useDispatchConnectionStore } from "_store/dispatch/connectionStore"; import { changeDispatcherAPI } from "_querys/dispatcher"; +import { getRadioStream } from "_helpers/radioEffect"; let interval: NodeJS.Timeout; @@ -32,6 +41,7 @@ type TalkState = { addSpeakingParticipant: (participant: Participant) => void; removeSpeakingParticipant: (speakingParticipants: Participant) => void; room: Room | null; + localRadioTrack: LocalTrackPublication | undefined; }; const getToken = async (roomName: string) => { const response = await axios.get(`/api/livekit-token?roomName=${roomName}`); @@ -41,6 +51,7 @@ const getToken = async (roomName: string) => { export const useAudioStore = create((set, get) => ({ isTalking: false, + localRadioTrack: undefined, transmitBlocked: false, message: null, micDeviceId: null, @@ -75,9 +86,18 @@ export const useAudioStore = create((set, get) => ({ }, setMic: (micDeviceId, micVolume) => { set({ micDeviceId, micVolume }); + if (get().state === "connected") { + const { room, disconnect, connect } = get(); + const role = room?.localParticipant.attributes.role; + console.log(role); + if (room?.name || role) { + disconnect(); + connect(room?.name || "", role || "user"); + } + } }, toggleTalking: () => { - const { room, isTalking, micDeviceId, speakingParticipants, transmitBlocked } = get(); + const { room, isTalking, speakingParticipants, transmitBlocked } = get(); if (!room) return; if (speakingParticipants.length > 0 && !isTalking && !transmitBlocked) { @@ -94,10 +114,7 @@ export const useAudioStore = create((set, get) => ({ }); return; } - // Todo: use micVolume - room.localParticipant.setMicrophoneEnabled(!isTalking, { - deviceId: micDeviceId ?? undefined, - }); + room.localParticipant.setMicrophoneEnabled(!isTalking); set((state) => ({ isTalking: !state.isTalking, transmitBlocked: false })); }, @@ -131,6 +148,27 @@ export const useAudioStore = create((set, get) => ({ }); } + const inputStream = await navigator.mediaDevices.getUserMedia({ + audio: { + deviceId: get().micDeviceId ?? undefined, + noiseSuppression: true, + }, + }); + + // Funk-Effekt anwenden + const radioStream = getRadioStream(inputStream, get().micVolume); + if (!radioStream) throw new Error("Konnte Funkstream nicht erzeugen"); + + const [track] = radioStream.getAudioTracks(); + if (!track) throw new Error("Konnte Audio-Track nicht erzeugen"); + + const publishedTrack = await room.localParticipant.publishTrack(track, { + name: "radio-audio", + source: Track.Source.Microphone, + }); + await publishedTrack.mute(); + set({ localRadioTrack: publishedTrack }); + set({ state: "connected", room, message: null }); }) .on(RoomEvent.Disconnected, () => {