@lobehub/tts
Version:
A high-quality & reliable TTS React Hooks library
67 lines (65 loc) • 2.27 kB
JavaScript
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 };