76 lines
2.1 KiB
TypeScript
76 lines
2.1 KiB
TypeScript
"use client";
|
|
import { cn } from "@repo/shared-components";
|
|
import { useEffect, useState } from "react";
|
|
|
|
type MicrophoneLevelProps = {
|
|
deviceId: string;
|
|
volumeInput: number; // Verstärkung der Lautstärke
|
|
};
|
|
|
|
export default function MicrophoneLevel({ deviceId, volumeInput }: MicrophoneLevelProps) {
|
|
const [volumeLevel, setVolumeLevel] = useState(0);
|
|
|
|
useEffect(() => {
|
|
let audioContext: AudioContext | null = null;
|
|
let analyser: AnalyserNode | null = null;
|
|
let source: MediaStreamAudioSourceNode | null = null;
|
|
let rafId: number;
|
|
|
|
async function start() {
|
|
audioContext = new AudioContext();
|
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
audio: { deviceId: deviceId ? { exact: deviceId } : undefined },
|
|
});
|
|
source = audioContext.createMediaStreamSource(stream);
|
|
analyser = audioContext.createAnalyser();
|
|
analyser.fftSize = 256;
|
|
source.connect(analyser);
|
|
|
|
const dataArray = new Uint8Array(analyser.frequencyBinCount);
|
|
|
|
const updateVolume = () => {
|
|
if (!analyser) return;
|
|
analyser.getByteFrequencyData(dataArray);
|
|
const avg = dataArray.reduce((a, b) => a + b, 0) / dataArray.length;
|
|
setVolumeLevel(avg * volumeInput);
|
|
rafId = requestAnimationFrame(updateVolume);
|
|
};
|
|
|
|
updateVolume();
|
|
}
|
|
|
|
start();
|
|
|
|
return () => {
|
|
cancelAnimationFrame(rafId);
|
|
audioContext?.close();
|
|
};
|
|
}, [deviceId, volumeInput]);
|
|
|
|
const barWidth = Math.min((volumeLevel / 140) * 100, 100);
|
|
|
|
return (
|
|
<div className="w-full">
|
|
<div className="relative w-full bg-base-300 h-5 rounded">
|
|
<div
|
|
className={cn("bg-primary h-full rounded", barWidth == 100 && "bg-red-400")}
|
|
style={{
|
|
width: `${barWidth}%`,
|
|
transition: "width 0.2s",
|
|
}}
|
|
/>
|
|
<div
|
|
className="absolute top-0 left-[60%] w-[30%] h-full bg-green-500 opacity-40 rounded"
|
|
style={{
|
|
transform: "translateX(-50%)",
|
|
}}
|
|
/>
|
|
</div>
|
|
<p className="text-gray-500 text-sm">
|
|
Lautstärke sollte beim Sprechen in dem Grünen bereich bleiben. Beachte das scharfe Laute
|
|
(z.B. "S" oder "Z") die Anzeige verfälschen können.
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|