UNPKG

@lobehub/tts

Version:

A high-quality & reliable TTS React Hooks library

67 lines (65 loc) 2.27 kB
import { throttle } from "es-toolkit/compat"; import { useCallback, useEffect, useRef, useState } from "react"; //#region src/react/hooks/useAudioVisualizer.ts const useAudioVisualizer = (audioRef, { count = 5 }) => { const barsSet = Array.from({ length: (count + 1) / 2 }).fill(0); const [bars, setBars] = useState([ 0, 0, 0, 0 ]); const audioContextRef = useRef(null); const analyserRef = useRef(null); const dataArrayRef = useRef(null); const animationFrameIdRef = useRef(null); const audioSourceRef = useRef(null); const [init, setInit] = useState(false); const renderFrame = throttle(() => { animationFrameIdRef.current = requestAnimationFrame(renderFrame); if (analyserRef.current && dataArrayRef.current) { analyserRef.current.getByteFrequencyData(dataArrayRef.current); const step = Math.floor(dataArrayRef.current.length / barsSet.length); setBars(barsSet.map((_, i) => { return dataArrayRef.current?.[i * step] || 0; })); } }, 50); const resetRenderFrame = useCallback(() => { if (animationFrameIdRef.current) cancelAnimationFrame(animationFrameIdRef.current); setBars(barsSet); }, []); useEffect(() => { if (!audioRef.current || !audioRef.current.currentSrc) return; try { audioContextRef.current = new AudioContext(); analyserRef.current = audioContextRef.current.createAnalyser(); analyserRef.current.fftSize = 256; const bufferLength = analyserRef.current.frequencyBinCount; dataArrayRef.current = new Uint8Array(bufferLength); audioSourceRef.current = audioContextRef.current.createMediaElementSource(audioRef.current); audioSourceRef.current.connect(analyserRef.current); analyserRef.current.connect(audioContextRef.current.destination); } catch (error) { console.error("Error useAudioVisualizer:", error); } setInit(true); return () => { audioSourceRef.current?.disconnect(); analyserRef.current?.disconnect(); audioContextRef.current?.close(); setInit(false); }; }, [audioRef?.current?.currentSrc]); useEffect(() => { if (!init) return; resetRenderFrame(); renderFrame(); return () => { resetRenderFrame(); }; }, [init]); return [...[...bars].slice(1, bars.length).reverse(), ...bars]; }; //#endregion export { useAudioVisualizer };