import { useState, useRef, useCallback } from "react"; export function useAudio() { const [isRecording, setIsRecording] = useState(false); const [audioBlob, setAudioBlob] = useState(null); const [audioUrl, setAudioUrl] = useState(null); const mediaRecorderRef = useRef(null); const chunksRef = useRef([]); const streamRef = useRef(null); const startRecording = useCallback(async () => { try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); streamRef.current = stream; // Web Audio API for gain normalization const ctx = new AudioContext(); const source = ctx.createMediaStreamSource(stream); const gain = ctx.createGain(); gain.gain.value = 3.5; const compressor = ctx.createDynamicsCompressor(); compressor.threshold.value = -50; compressor.knee.value = 40; compressor.ratio.value = 20; compressor.attack.value = 0.003; compressor.release.value = 0.25; const dest = ctx.createMediaStreamDestination(); source.connect(gain); gain.connect(compressor); compressor.connect(dest); const recorder = new MediaRecorder(dest.stream); chunksRef.current = []; recorder.ondataavailable = (e) => chunksRef.current.push(e.data); recorder.onstop = () => { const blob = new Blob(chunksRef.current, { type: "audio/webm" }); const url = URL.createObjectURL(blob); setAudioBlob(blob); setAudioUrl(url); stream.getTracks().forEach((t) => t.stop()); }; mediaRecorderRef.current = recorder; recorder.start(); setIsRecording(true); } catch (err) { console.error("Mic error:", err); } }, []); const stopRecording = useCallback(() => { if (mediaRecorderRef.current && isRecording) { mediaRecorderRef.current.stop(); setIsRecording(false); } }, [isRecording]); const reset = useCallback(() => { setAudioBlob(null); setAudioUrl(null); setIsRecording(false); }, []); return { isRecording, audioBlob, audioUrl, startRecording, stopRecording, reset }; }