/ proof-of-concept / takes
Sign in
proof-of-concept / takes
takes / src / components / MicMeter.tsx
38 lines · 1.2 KBBlameHistoryRaw
1import { useEffect, useRef } from 'react';
3// Live input level of a microphone stream.
4export function MicMeter({ stream }: { stream: MediaStream | null }) {
5 const barRef = useRef<HTMLDivElement>(null);
6 useEffect(() => {
7 if (!stream || stream.getAudioTracks().length === 0) return;
8 const ctx = new AudioContext();
9 const src = ctx.createMediaStreamSource(stream);
10 const analyser = ctx.createAnalyser();
11 analyser.fftSize = 1024;
12 src.connect(analyser);
13 const buf = new Float32Array(analyser.fftSize);
14 let raf = 0;
15 let level = 0;
16 const loop = () => {
17 analyser.getFloatTimeDomainData(buf);
18 let peak = 0;
19 for (const x of buf) peak = Math.max(peak, Math.abs(x));
20 level = Math.max(peak, level * 0.9);
21 if (barRef.current) {
22 barRef.current.style.width = `${Math.min(100, Math.sqrt(level) * 100)}%`;
23 barRef.current.classList.toggle('hot', level > 0.9);
24 }
25 raf = requestAnimationFrame(loop);
26 };
27 loop();
28 return () => {
29 cancelAnimationFrame(raf);
30 ctx.close();
31 };
32 }, [stream]);
33 return (
34 <div className="meter" title="Microphone level">
35 <div className="meter-bar" ref={barRef} />
36 </div>
37 );
↑↓ move↵openescclose