All files / src/hooks useAudio.js

28.26% Statements 13/46
0% Branches 0/10
16.66% Functions 1/6
30.23% Lines 13/43

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74        10x 10x 10x   10x 10x 10x 10x 10x 10x   10x                                                           10x           10x             10x                              
import { useState, useRef } from 'react';
import { api } from '../api';
 
export function useAudio({ voiceEnabledRef, logAgentAction }) {
  const [isSpeaking, setIsSpeaking] = useState(false);
  const [isTestingVoice, setIsTestingVoice] = useState(false);
  const [isListening, setIsListening] = useState(false);
 
  const audioRef = useRef(null);
  const speechQueue = useRef([]);
  const isSpeakingRef = useRef(false);
  const mediaRecorderRef = useRef(null);
  const audioChunksRef = useRef([]);
  const sttIntervalRef = useRef(null);
 
  const processSpeechQueue = async () => {
    if (isSpeakingRef.current || speechQueue.current.length === 0) return;
    const nextText = speechQueue.current.shift();
    isSpeakingRef.current = true;
    setIsSpeaking(true);
    try {
      const blob = await api.tts(nextText);
      const url = URL.createObjectURL(blob);
      if (audioRef.current) {
        const cleanup = () => {
          audioRef.current.removeEventListener('ended', cleanup);
          audioRef.current.removeEventListener('error', cleanup);
          isSpeakingRef.current = false;
          setIsSpeaking(false);
          URL.revokeObjectURL(url);
          processSpeechQueue();
        };
        audioRef.current.addEventListener('ended', cleanup);
        audioRef.current.addEventListener('error', cleanup);
        audioRef.current.src = url;
        audioRef.current.load();
        await audioRef.current.play().catch(cleanup);
      }
    } catch (err) {
      isSpeakingRef.current = false;
      setIsSpeaking(false);
      processSpeechQueue();
    }
  };
 
  const speakText = (text) => {
    if (!voiceEnabledRef.current || !text) return;
    speechQueue.current.push(text);
    processSpeechQueue();
  };
 
  const testVoiceDiagnostic = () => {
    setIsTestingVoice(true);
    logAgentAction("Diagnostic", "Testing ElevenLabs voice bridge...");
    speakText("Scientific laboratory voice diagnostic complete. Communication flow is established.");
    setTimeout(() => setIsTestingVoice(false), 3000);
  };
 
  return {
    isSpeaking, setIsSpeaking,
    isTestingVoice, setIsTestingVoice,
    isListening, setIsListening,
    audioRef,
    speechQueue,
    isSpeakingRef,
    mediaRecorderRef,
    audioChunksRef,
    sttIntervalRef,
    processSpeechQueue,
    speakText,
    testVoiceDiagnostic,
  };
}