({
onTranscript,
onUsageLimitExceeded,
language,
workspaceId,
}: UseSpeechToTextProps)
| 39 | } |
| 40 | |
| 41 | export function useSpeechToText({ |
| 42 | onTranscript, |
| 43 | onUsageLimitExceeded, |
| 44 | language, |
| 45 | workspaceId, |
| 46 | }: UseSpeechToTextProps): UseSpeechToTextReturn { |
| 47 | const [isListening, setIsListening] = useState(false) |
| 48 | const [isSupported, setIsSupported] = useState(false) |
| 49 | const [permissionState, setPermissionState] = useState<PermissionState>('prompt') |
| 50 | |
| 51 | const onTranscriptRef = useRef(onTranscript) |
| 52 | const onUsageLimitExceededRef = useRef(onUsageLimitExceeded) |
| 53 | const languageRef = useRef(language) |
| 54 | const workspaceIdRef = useRef(workspaceId) |
| 55 | const mountedRef = useRef(true) |
| 56 | const startingRef = useRef(false) |
| 57 | |
| 58 | const wsRef = useRef<WebSocket | null>(null) |
| 59 | const streamRef = useRef<MediaStream | null>(null) |
| 60 | const audioContextRef = useRef<AudioContext | null>(null) |
| 61 | const processorRef = useRef<ScriptProcessorNode | null>(null) |
| 62 | |
| 63 | const pcmBufferRef = useRef<Float32Array[]>([]) |
| 64 | const sendIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null) |
| 65 | const sessionTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null) |
| 66 | const stopStreamingRef = useRef<() => void>(() => {}) |
| 67 | const isFirstChunkRef = useRef(true) |
| 68 | const committedTextRef = useRef('') |
| 69 | |
| 70 | onTranscriptRef.current = onTranscript |
| 71 | onUsageLimitExceededRef.current = onUsageLimitExceeded |
| 72 | languageRef.current = language |
| 73 | workspaceIdRef.current = workspaceId |
| 74 | |
| 75 | useEffect(() => { |
| 76 | const browserOk = |
| 77 | typeof window !== 'undefined' && |
| 78 | typeof AudioContext !== 'undefined' && |
| 79 | typeof WebSocket !== 'undefined' && |
| 80 | typeof navigator?.mediaDevices?.getUserMedia === 'function' |
| 81 | |
| 82 | if (!browserOk) { |
| 83 | setIsSupported(false) |
| 84 | return |
| 85 | } |
| 86 | |
| 87 | requestJson(getVoiceSettingsContract, {}) |
| 88 | .then((data) => { |
| 89 | if (mountedRef.current) setIsSupported(data.sttAvailable === true) |
| 90 | }) |
| 91 | .catch(() => { |
| 92 | if (mountedRef.current) setIsSupported(false) |
| 93 | }) |
| 94 | }, []) |
| 95 | |
| 96 | const flushAudioBuffer = useCallback(() => { |
| 97 | const ws = wsRef.current |
| 98 | if (!ws || ws.readyState !== WebSocket.OPEN) return |
no test coverage detected