diff --git a/src/pages/ReportEditor.tsx b/src/pages/ReportEditor.tsx index 6cdf32a..3744034 100644 --- a/src/pages/ReportEditor.tsx +++ b/src/pages/ReportEditor.tsx @@ -923,31 +923,43 @@ export default function ReportEditor() { } const toggleListening = async () => { + // 专门提取一个彻底关闭物理麦克风的函数 + const stopMicrophone = () => { + if (xfAudioContextRef.current) { + try { xfAudioContextRef.current.close(); } catch {} + xfAudioContextRef.current = null; + } + if (xfMediaStreamRef.current) { + xfMediaStreamRef.current.getTracks().forEach(t => t.stop()); + xfMediaStreamRef.current = null; + } + }; + if (isListening) { setIsListening(false); - // 1. 发送结束帧告诉服务器录音结束 + stopMicrophone(); + if (xfWsRef.current && xfWsRef.current.readyState === WebSocket.OPEN) { try { const endFrame = { data: { status: 2, format: 'audio/L16;rate=16000', encoding: 'raw', audio: '' } }; xfWsRef.current.send(JSON.stringify(endFrame)); } catch {} } - // 2. 仅关闭本地麦克风收音,保留 WebSocket 等待服务器返回最终结果 - if (xfAudioContextRef.current) { try { xfAudioContextRef.current.close(); } catch {} xfAudioContextRef.current = null; } - if (xfMediaStreamRef.current) { xfMediaStreamRef.current.getTracks().forEach(t => t.stop()); xfMediaStreamRef.current = null; } return; } + const xfConfig = storage.get('systemSettings', {} as SystemSettings).xfSpeechConfig || DEFAULT_XF_SPEECH; if (!xfConfig?.appId || !xfConfig?.apiKey || !xfConfig?.apiSecret) { alert('请先在系统设置中配置讯飞语音 APPID/APIKey/APISecret'); return; } + try { const authUrl = await getXfAuthUrl(xfConfig.apiKey, xfConfig.apiSecret); const ws = new WebSocket(authUrl); xfWsRef.current = ws; let frameStatus = 0; - let transcript = chatInput; + ws.onopen = async () => { try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); @@ -956,8 +968,9 @@ export default function ReportEditor() { xfAudioContextRef.current = audioContext; const source = audioContext.createMediaStreamSource(stream); const processor = audioContext.createScriptProcessor(4096, 1, 1); + processor.onaudioprocess = (e) => { - if (ws.readyState !== WebSocket.OPEN) return; + if (ws.readyState !== WebSocket.OPEN || !xfAudioContextRef.current) return; const inputData = e.inputBuffer.getChannelData(0); const pcmBuffer = floatTo16BitPCM(inputData); const base64Audio = arrayBufferToBase64(pcmBuffer); @@ -969,6 +982,7 @@ export default function ReportEditor() { ws.send(JSON.stringify(frame)); frameStatus = 1; }; + source.connect(processor); processor.connect(audioContext.destination); setIsListening(true); @@ -978,31 +992,35 @@ export default function ReportEditor() { ws.close(); } }; + ws.onmessage = (event) => { try { const jsonData = JSON.parse(event.data); if (jsonData.code !== 0 && jsonData.code !== undefined) { alert(`讯飞语音报错: ${jsonData.message || '未知错误'} (错误码: ${jsonData.code})`); setIsListening(false); + stopMicrophone(); ws.close(); return; } if (jsonData.data?.result?.ws) { let seg = ''; for (const w of jsonData.data.result.ws) { if (w.cw?.[0]?.w) seg += w.cw[0].w; } - if (jsonData.data.result.ls) { transcript += seg; setChatInput(transcript); } - else { setChatInput(transcript + seg); } + if (seg) { + setChatInput(prev => prev + seg); + } } - // 当接收到服务端的最终状态码 status === 2 时,才彻底断开 websocket if (jsonData.data?.status === 2) { ws.close(); xfWsRef.current = null; setIsListening(false); + stopMicrophone(); } } catch {} }; - ws.onerror = () => { alert('讯飞语音连接失败'); setIsListening(false); }; - ws.onclose = () => { setIsListening(false); }; + + ws.onerror = () => { alert('讯飞语音连接失败'); setIsListening(false); stopMicrophone(); }; + ws.onclose = () => { setIsListening(false); stopMicrophone(); }; } catch (e: any) { alert('讯飞语音初始化失败: ' + e.message); }