Funk-effekt und Mikrifon-Einstellungen hinzugefügt

This commit is contained in:
PxlLoewe
2025-07-16 23:24:55 -07:00
parent 66c32530a7
commit 7be0c701a4
3 changed files with 49 additions and 18 deletions

View File

@@ -11,6 +11,7 @@ import toast from "react-hot-toast";
export const SettingsBtn = () => { export const SettingsBtn = () => {
const session = useSession(); const session = useSession();
const { data: user } = useQuery({ const { data: user } = useQuery({
queryKey: ["user", session.data?.user.id], queryKey: ["user", session.data?.user.id],
queryFn: () => getUserAPI(session.data!.user.id), queryFn: () => getUserAPI(session.data!.user.id),
@@ -38,7 +39,7 @@ export const SettingsBtn = () => {
const [funkVolume, setFunkVol] = useState<number>(0.8); const [funkVolume, setFunkVol] = useState<number>(0.8);
const [dmeVolume, setDmeVol] = useState<number>(0.8); const [dmeVolume, setDmeVol] = useState<number>(0.8);
const setMic = useAudioStore((state) => state.setMic); const { setMic } = useAudioStore((state) => state);
useEffect(() => { useEffect(() => {
if (user) { if (user) {
@@ -106,10 +107,6 @@ export const SettingsBtn = () => {
<p className="flex items-center gap-2 text-base mb-2 justify-start w-full"> <p className="flex items-center gap-2 text-base mb-2 justify-start w-full">
<Volume2 size={20} /> Eingabelautstärke <Volume2 size={20} /> Eingabelautstärke
</p> </p>
{/*
TODO: Livekit Kann aktuell keine Lautstärke manuell überschreiben, daher ist die MicVolumeBar deaktiviert
<div className="w-full"> <div className="w-full">
<input <input
type="range" type="range"
@@ -134,7 +131,7 @@ export const SettingsBtn = () => {
</div> </div>
{showIndication && ( {showIndication && (
<MicVolumeBar deviceId={selectedDevice ? selectedDevice : ""} volumeInput={micVol} /> <MicVolumeBar deviceId={selectedDevice ? selectedDevice : ""} volumeInput={micVol} />
)} */} )}
<div className="divider w-full" /> <div className="divider w-full" />
</div> </div>
<p className="flex items-center gap-2 text-base mb-2"> <p className="flex items-center gap-2 text-base mb-2">

View File

@@ -1,5 +1,3 @@
import { AudioTrack, RemoteAudioTrack, RemoteTrack } from "livekit-client";
// Helper function for distortion curve generation // Helper function for distortion curve generation
function createDistortionCurve(amount: number): Float32Array { function createDistortionCurve(amount: number): Float32Array {
const k = typeof amount === "number" ? amount : 50; const k = typeof amount === "number" ? amount : 50;
@@ -14,12 +12,10 @@ function createDistortionCurve(amount: number): Float32Array {
return curve; return curve;
} }
export const getRadioStream = (track: RemoteAudioTrack, volume: number): MediaStream | null => { export const getRadioStream = (stream: MediaStream, volume: number): MediaStream | null => {
try { try {
const audioContext = new window.AudioContext(); const audioContext = new window.AudioContext();
const sourceNode = audioContext.createMediaStreamSource( const sourceNode = audioContext.createMediaStreamSource(stream);
new MediaStream([track.mediaStreamTrack]),
);
const destinationNode = audioContext.createMediaStreamDestination(); const destinationNode = audioContext.createMediaStreamDestination();
const gainNode = audioContext.createGain(); const gainNode = audioContext.createGain();

View File

@@ -5,12 +5,21 @@ import {
handleTrackSubscribed, handleTrackSubscribed,
handleTrackUnsubscribed, handleTrackUnsubscribed,
} from "_helpers/liveKitEventHandler"; } 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 { pilotSocket } from "(app)/pilot/socket";
import { create } from "zustand"; import { create } from "zustand";
import axios from "axios"; import axios from "axios";
import { useDispatchConnectionStore } from "_store/dispatch/connectionStore"; import { useDispatchConnectionStore } from "_store/dispatch/connectionStore";
import { changeDispatcherAPI } from "_querys/dispatcher"; import { changeDispatcherAPI } from "_querys/dispatcher";
import { getRadioStream } from "_helpers/radioEffect";
let interval: NodeJS.Timeout; let interval: NodeJS.Timeout;
@@ -32,6 +41,7 @@ type TalkState = {
addSpeakingParticipant: (participant: Participant) => void; addSpeakingParticipant: (participant: Participant) => void;
removeSpeakingParticipant: (speakingParticipants: Participant) => void; removeSpeakingParticipant: (speakingParticipants: Participant) => void;
room: Room | null; room: Room | null;
localRadioTrack: LocalTrackPublication | undefined;
}; };
const getToken = async (roomName: string) => { const getToken = async (roomName: string) => {
const response = await axios.get(`/api/livekit-token?roomName=${roomName}`); const response = await axios.get(`/api/livekit-token?roomName=${roomName}`);
@@ -41,6 +51,7 @@ const getToken = async (roomName: string) => {
export const useAudioStore = create<TalkState>((set, get) => ({ export const useAudioStore = create<TalkState>((set, get) => ({
isTalking: false, isTalking: false,
localRadioTrack: undefined,
transmitBlocked: false, transmitBlocked: false,
message: null, message: null,
micDeviceId: null, micDeviceId: null,
@@ -75,9 +86,18 @@ export const useAudioStore = create<TalkState>((set, get) => ({
}, },
setMic: (micDeviceId, micVolume) => { setMic: (micDeviceId, micVolume) => {
set({ 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: () => { toggleTalking: () => {
const { room, isTalking, micDeviceId, speakingParticipants, transmitBlocked } = get(); const { room, isTalking, speakingParticipants, transmitBlocked } = get();
if (!room) return; if (!room) return;
if (speakingParticipants.length > 0 && !isTalking && !transmitBlocked) { if (speakingParticipants.length > 0 && !isTalking && !transmitBlocked) {
@@ -94,10 +114,7 @@ export const useAudioStore = create<TalkState>((set, get) => ({
}); });
return; return;
} }
// Todo: use micVolume room.localParticipant.setMicrophoneEnabled(!isTalking);
room.localParticipant.setMicrophoneEnabled(!isTalking, {
deviceId: micDeviceId ?? undefined,
});
set((state) => ({ isTalking: !state.isTalking, transmitBlocked: false })); set((state) => ({ isTalking: !state.isTalking, transmitBlocked: false }));
}, },
@@ -131,6 +148,27 @@ export const useAudioStore = create<TalkState>((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 }); set({ state: "connected", room, message: null });
}) })
.on(RoomEvent.Disconnected, () => { .on(RoomEvent.Disconnected, () => {