import { useRef, useEffect, useState } from 'react'; interface UseAudioAnalyzerReturn { audioData: Uint8Array; analyser: AnalyserNode | null; connectToStream: (stream: MediaStream) => void; disconnect: () => void; } export const useAudioAnalyzer = (fftSize: number = 256): UseAudioAnalyzerReturn => { const [audioData, setAudioData] = useState(new Uint8Array(fftSize / 2)); const [analyser, setAnalyser] = useState(null); const audioContextRef = useRef(null); const sourceRef = useRef(null); const animationFrameRef = useRef(null); const connectToStream = (stream: MediaStream) => { try { // Clean up existing connections disconnect(); // Create new audio context and analyser const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)(); const analyserNode = audioContext.createAnalyser(); analyserNode.fftSize = fftSize; analyserNode.smoothingTimeConstant = 0.8; // Connect stream to analyser const source = audioContext.createMediaStreamSource(stream); source.connect(analyserNode); // Store references audioContextRef.current = audioContext; sourceRef.current = source; setAnalyser(analyserNode); // Start updating audio data const bufferLength = analyserNode.frequencyBinCount; const dataArray = new Uint8Array(bufferLength); const updateAudioData = () => { if (analyserNode) { analyserNode.getByteFrequencyData(dataArray); setAudioData(new Uint8Array(dataArray)); animationFrameRef.current = requestAnimationFrame(updateAudioData); } }; updateAudioData(); } catch (error) { console.error('Error setting up audio analyzer:', error); } }; const disconnect = () => { // Cancel animation frame if (animationFrameRef.current) { cancelAnimationFrame(animationFrameRef.current); animationFrameRef.current = null; } // Disconnect audio nodes if (sourceRef.current) { sourceRef.current.disconnect(); sourceRef.current = null; } // Close audio context if (audioContextRef.current) { audioContextRef.current.close(); audioContextRef.current = null; } setAnalyser(null); setAudioData(new Uint8Array(fftSize / 2)); }; // Cleanup on unmount useEffect(() => { return () => { disconnect(); }; }, []); return { audioData, analyser, connectToStream, disconnect, }; };