yt-chat-components 1.1.9 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -42,7 +42,6 @@ const CallInterface: React.FC<CallInterfaceProps> = ({
|
|
|
42
42
|
|
|
43
43
|
// 添加VAD相关状态
|
|
44
44
|
const isSpeakingRef = useRef<boolean>(false);
|
|
45
|
-
const [isListening, setIsListening] = useState(false);
|
|
46
45
|
const SILENCE_THRESHOLD = 35; // 静音阈值,可根据环境调整
|
|
47
46
|
const SPEECH_DELAY = 1500; // 停止说话多久后认为一段话结束(毫秒)
|
|
48
47
|
|
|
@@ -61,7 +60,8 @@ const CallInterface: React.FC<CallInterfaceProps> = ({
|
|
|
61
60
|
const speechTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
62
61
|
const vadProcessorRef = useRef<ScriptProcessorNode | null>(null);
|
|
63
62
|
const audioContextRef = useRef<AudioContext | null>(null);
|
|
64
|
-
|
|
63
|
+
const [isSpeakingNow,setIsSpeakingNow] = useState(false);
|
|
64
|
+
|
|
65
65
|
|
|
66
66
|
// 添加统一的WebSocket消息发送方法
|
|
67
67
|
const sendWebSocketMessage = (type: string, data: any) => {
|
|
@@ -73,7 +73,6 @@ const CallInterface: React.FC<CallInterfaceProps> = ({
|
|
|
73
73
|
};
|
|
74
74
|
|
|
75
75
|
const handleAudioData = (audioData: string) => {
|
|
76
|
-
setIsListening(true);
|
|
77
76
|
setWorkStatus('正在回复');
|
|
78
77
|
|
|
79
78
|
// 将base64音频转换为Blob
|
|
@@ -90,13 +89,6 @@ const CallInterface: React.FC<CallInterfaceProps> = ({
|
|
|
90
89
|
const audio = new Audio(audioUrl);
|
|
91
90
|
responseAudio = audio;
|
|
92
91
|
|
|
93
|
-
// 监听播放结束事件
|
|
94
|
-
audio.onended = () => {
|
|
95
|
-
URL.revokeObjectURL(audioUrl);
|
|
96
|
-
setIsListening(false);
|
|
97
|
-
setWorkStatus('正在聆听');
|
|
98
|
-
};
|
|
99
|
-
|
|
100
92
|
// 监听用户打断
|
|
101
93
|
audio.onplay = () => {
|
|
102
94
|
// 设置打断检测
|
|
@@ -106,9 +98,8 @@ const CallInterface: React.FC<CallInterfaceProps> = ({
|
|
|
106
98
|
console.log('用户打断播放');
|
|
107
99
|
audio.pause();
|
|
108
100
|
URL.revokeObjectURL(audioUrl);
|
|
109
|
-
setIsListening(false);
|
|
110
|
-
setWorkStatus('正在聆听');
|
|
111
101
|
clearInterval(interruptionCheck);
|
|
102
|
+
setWorkStatus('正在聆听');
|
|
112
103
|
}
|
|
113
104
|
}, 100);
|
|
114
105
|
|
|
@@ -116,7 +107,6 @@ const CallInterface: React.FC<CallInterfaceProps> = ({
|
|
|
116
107
|
audio.onended = () => {
|
|
117
108
|
clearInterval(interruptionCheck);
|
|
118
109
|
URL.revokeObjectURL(audioUrl);
|
|
119
|
-
setIsListening(false);
|
|
120
110
|
setWorkStatus('正在聆听');
|
|
121
111
|
};
|
|
122
112
|
};
|
|
@@ -124,7 +114,6 @@ const CallInterface: React.FC<CallInterfaceProps> = ({
|
|
|
124
114
|
// 开始播放
|
|
125
115
|
audio.play().catch(err => {
|
|
126
116
|
console.error('播放音频失败:', err);
|
|
127
|
-
setIsListening(false);
|
|
128
117
|
setWorkStatus('正在聆听');
|
|
129
118
|
});
|
|
130
119
|
};
|
|
@@ -210,7 +199,7 @@ const CallInterface: React.FC<CallInterfaceProps> = ({
|
|
|
210
199
|
vadProcessorRef.current = vadProcessor;
|
|
211
200
|
|
|
212
201
|
vadProcessor.onaudioprocess = (e) => {
|
|
213
|
-
if (
|
|
202
|
+
if (workStatus === '正在回复' || workStatus === '正在思考') return; // 正在听对方说话 or 思考,不处理
|
|
214
203
|
|
|
215
204
|
analyser.getByteFrequencyData(dataArray);
|
|
216
205
|
|
|
@@ -223,12 +212,13 @@ const CallInterface: React.FC<CallInterfaceProps> = ({
|
|
|
223
212
|
|
|
224
213
|
// 检测是否有语音
|
|
225
214
|
const isSpeakingNow = average > SILENCE_THRESHOLD;
|
|
226
|
-
|
|
215
|
+
|
|
227
216
|
if (isSpeakingNow && !isSpeakingRef.current) {
|
|
217
|
+
setIsSpeakingNow(true)
|
|
228
218
|
// 开始说话
|
|
229
219
|
console.log('开始说话', isSpeakingRef.current);
|
|
230
220
|
isSpeakingRef.current = true;
|
|
231
|
-
|
|
221
|
+
|
|
232
222
|
// 开始录制
|
|
233
223
|
if (!recorderRef.current && localStreamRef.current) {
|
|
234
224
|
console.log('准备录制');
|
|
@@ -257,13 +247,14 @@ const CallInterface: React.FC<CallInterfaceProps> = ({
|
|
|
257
247
|
});
|
|
258
248
|
recorder.start();
|
|
259
249
|
}
|
|
260
|
-
|
|
250
|
+
|
|
261
251
|
// 清除之前的超时
|
|
262
252
|
if (speechTimeoutRef.current) {
|
|
263
253
|
clearTimeout(speechTimeoutRef.current);
|
|
264
254
|
speechTimeoutRef.current = null;
|
|
265
255
|
}
|
|
266
|
-
}
|
|
256
|
+
}
|
|
257
|
+
else if (!isSpeakingNow && isSpeakingRef.current) {
|
|
267
258
|
console.log('准备停止说话');
|
|
268
259
|
// 可能停止说话,设置超时
|
|
269
260
|
if (!speechTimeoutRef.current) {
|
|
@@ -271,7 +262,7 @@ const CallInterface: React.FC<CallInterfaceProps> = ({
|
|
|
271
262
|
// 确认停止说话
|
|
272
263
|
console.log('停止说话');
|
|
273
264
|
isSpeakingRef.current = false;
|
|
274
|
-
|
|
265
|
+
|
|
275
266
|
// 停止录制
|
|
276
267
|
if (recorderRef.current) {
|
|
277
268
|
recorderRef.current.stop((blob: Blob, duration:any, mime:any) => {
|
|
@@ -289,8 +280,7 @@ const CallInterface: React.FC<CallInterfaceProps> = ({
|
|
|
289
280
|
audioData: base64Audio.split(',')[1], // 移除data URL前缀
|
|
290
281
|
userMessage: true,
|
|
291
282
|
audioFormat:"mp3",
|
|
292
|
-
sampleRate:16000
|
|
293
|
-
voice:"longxiang"
|
|
283
|
+
sampleRate:16000
|
|
294
284
|
});
|
|
295
285
|
console.log('发送完毕');
|
|
296
286
|
|
|
@@ -301,9 +291,8 @@ const CallInterface: React.FC<CallInterfaceProps> = ({
|
|
|
301
291
|
// timestamp: new Date().toISOString()
|
|
302
292
|
// });
|
|
303
293
|
|
|
304
|
-
// 设置为正在听模式
|
|
305
|
-
setIsListening(true);
|
|
306
294
|
setWorkStatus('正在思考');
|
|
295
|
+
setIsSpeakingNow(false)
|
|
307
296
|
}
|
|
308
297
|
};
|
|
309
298
|
|
|
@@ -316,11 +305,12 @@ const CallInterface: React.FC<CallInterfaceProps> = ({
|
|
|
316
305
|
recorderRef.current=null;
|
|
317
306
|
});
|
|
318
307
|
}
|
|
319
|
-
|
|
308
|
+
|
|
320
309
|
speechTimeoutRef.current = null;
|
|
321
310
|
}, SPEECH_DELAY);
|
|
322
311
|
}
|
|
323
|
-
}
|
|
312
|
+
}
|
|
313
|
+
else if (isSpeakingNow && isSpeakingRef.current) {
|
|
324
314
|
console.log('继续说话');
|
|
325
315
|
// 继续说话,重置超时
|
|
326
316
|
if (speechTimeoutRef.current) {
|
|
@@ -520,8 +510,8 @@ const CallInterface: React.FC<CallInterfaceProps> = ({
|
|
|
520
510
|
{/* <div className='recwave' style={{width: 120, height: 50, display: workStatus === '正在聆听' ? 'block' : 'none'}}></div>*/}
|
|
521
511
|
{/* }*/}
|
|
522
512
|
{/*</div>*/}
|
|
523
|
-
<div className='recwave' style={{width: 120, height:
|
|
524
|
-
|
|
513
|
+
<div className='recwave' style={{width: 120, height: 65}}>{
|
|
514
|
+
(isSpeakingNow && workStatus === '正在聆听')|| workStatus === '正在回复' ? <img src={ai_call_working}/>:<img src={ai_call_thinking}/>
|
|
525
515
|
}</div>
|
|
526
516
|
<p className="call-status">
|
|
527
517
|
{workStatus}
|