import { useState, useRef, useEffect } from 'react'; export interface AudioVisualizerHook { audioData: Uint8Array | null; startVisualization: (stream: MediaStream) => void; stopVisualization: () => void; isVisualizing: boolean; } export function useAudioVisualizer(): AudioVisualizerHook { const [audioData, setAudioData] = useState(null); const [isVisualizing, setIsVisualizing] = useState(false); const audioContextRef = useRef(null); const analyserRef = useRef(null); const dataArrayRef = useRef(null); const animationFrameRef = useRef(null); const sourceRef = useRef(null); useEffect(() => { return () => { stopVisualization(); }; }, []); const startVisualization = (stream: MediaStream) => { try { // Create audio context const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)(); audioContextRef.current = audioContext; // Create analyser node const analyser = audioContext.createAnalyser(); analyser.fftSize = 256; analyser.smoothingTimeConstant = 0.8; analyserRef.current = analyser; // Create data array const bufferLength = analyser.frequencyBinCount; const dataArray = new Uint8Array(bufferLength); dataArrayRef.current = dataArray; // Connect stream to analyser const source = audioContext.createMediaStreamSource(stream); source.connect(analyser); sourceRef.current = source; // Start visualization loop setIsVisualizing(true); const visualize = () => { if (!analyserRef.current || !dataArrayRef.current) return; // Create a new Uint8Array to avoid type issues with ArrayBufferLike const dataArray = new Uint8Array(dataArrayRef.current.length); analyserRef.current.getByteFrequencyData(dataArray); setAudioData(new Uint8Array(dataArray)); animationFrameRef.current = requestAnimationFrame(visualize); }; visualize(); } catch (error) { console.error('Error starting audio visualization:', error); } }; const stopVisualization = () => { if (animationFrameRef.current) { cancelAnimationFrame(animationFrameRef.current); animationFrameRef.current = null; } if (sourceRef.current) { sourceRef.current.disconnect(); sourceRef.current = null; } if (audioContextRef.current) { audioContextRef.current.close().catch(console.error); audioContextRef.current = null; } analyserRef.current = null; dataArrayRef.current = null; setAudioData(null); setIsVisualizing(false); }; return { audioData, startVisualization, stopVisualization, isVisualizing, }; }