|
|
|
@ -49,6 +49,12 @@ class BackgroundAgentService extends GetxService { |
|
|
|
// 添加一个订阅,用于监听 TTS 状态变化 |
|
|
|
StreamSubscription? _ttsSpeakingSubscription; |
|
|
|
|
|
|
|
// 添加一个变量来跟踪当前的AI响应流订阅 |
|
|
|
StreamSubscription? _aiResponseSubscription; |
|
|
|
|
|
|
|
// 添加一个标志,表示是否应该取消当前的AI响应 |
|
|
|
bool _shouldCancelAiResponse = false; |
|
|
|
|
|
|
|
BackgroundAgentService() |
|
|
|
: _aiService = VolcanoAIService(), |
|
|
|
_ttsService = Get.find<VolcanoTtsService>(), |
|
|
|
@ -76,6 +82,12 @@ class BackgroundAgentService extends GetxService { |
|
|
|
_shouldContinueInteraction = true; |
|
|
|
|
|
|
|
try { |
|
|
|
// 确保之前的语音识别已经停止 |
|
|
|
if (_isListening) { |
|
|
|
print('交互开始前确保语音识别已停止'); |
|
|
|
await stopVoiceRecognition(); |
|
|
|
} |
|
|
|
|
|
|
|
// 循环进行交互,直到用户 10 秒没有说话或手动停止 |
|
|
|
while (_shouldContinueInteraction) { |
|
|
|
await _processSingleInteraction(systemPrompt); |
|
|
|
@ -85,6 +97,14 @@ class BackgroundAgentService extends GetxService { |
|
|
|
if (_isListening) { |
|
|
|
await stopVoiceRecognition(); |
|
|
|
} |
|
|
|
|
|
|
|
// 重置语音识别服务 |
|
|
|
try { |
|
|
|
print('交互结束,重置语音识别服务'); |
|
|
|
await _voiceRecognitionService.initialize(); |
|
|
|
} catch (e) { |
|
|
|
print('重置语音识别服务失败: $e'); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -131,6 +151,12 @@ class BackgroundAgentService extends GetxService { |
|
|
|
String userInput = ''; |
|
|
|
|
|
|
|
try { |
|
|
|
// 确保之前的语音识别已经停止 |
|
|
|
if (_isListening) { |
|
|
|
print('检测到上一次语音识别未正确停止,先停止它'); |
|
|
|
await stopVoiceRecognition(); |
|
|
|
} |
|
|
|
|
|
|
|
// 尝试启动语音识别 |
|
|
|
await startVoiceRecognition(); |
|
|
|
recognitionStarted = true; |
|
|
|
@ -175,6 +201,15 @@ class BackgroundAgentService extends GetxService { |
|
|
|
print('语音识别过程出错: $e'); |
|
|
|
// 如果语音识别失败,尝试使用默认问候语 |
|
|
|
userInput = '你好,请帮我回答一个问题'; |
|
|
|
|
|
|
|
// 尝试重置语音识别服务 |
|
|
|
try { |
|
|
|
await stopVoiceRecognition(); |
|
|
|
await Future.delayed(const Duration(milliseconds: 500)); |
|
|
|
await _voiceRecognitionService.initialize(); |
|
|
|
} catch (resetError) { |
|
|
|
print('重置语音识别服务失败: $resetError'); |
|
|
|
} |
|
|
|
} finally { |
|
|
|
// 清理资源,但保持语音识别状态 |
|
|
|
_recognitionCompleter = null; |
|
|
|
@ -216,51 +251,91 @@ class BackgroundAgentService extends GetxService { |
|
|
|
|
|
|
|
String fullResponse = ''; |
|
|
|
try { |
|
|
|
await for (final chunk in _aiService.sendMessageStream( |
|
|
|
// 重置取消标志 |
|
|
|
_shouldCancelAiResponse = false; |
|
|
|
|
|
|
|
// 创建一个本地变量来跟踪是否已取消 |
|
|
|
bool isCancelled = false; |
|
|
|
|
|
|
|
// 获取AI响应流 |
|
|
|
final responseStream = _aiService.sendMessageStream( |
|
|
|
messages: messages, |
|
|
|
systemPrompt: systemPrompt, |
|
|
|
)) { |
|
|
|
fullResponse += chunk; |
|
|
|
|
|
|
|
// 累积文本并处理TTS |
|
|
|
_pendingTtsText += chunk; |
|
|
|
if (_ttsService.isEnabled.value) { |
|
|
|
if (_pendingTtsText.length >= _minTtsLength) { |
|
|
|
int lastSentenceEnd = _findLastSentenceEnd(_pendingTtsText); |
|
|
|
if (lastSentenceEnd > 0) { |
|
|
|
String textToSpeak = _pendingTtsText.substring(0, lastSentenceEnd + 1); |
|
|
|
); |
|
|
|
|
|
|
|
// 创建一个订阅来处理响应流 |
|
|
|
_aiResponseSubscription = responseStream.listen( |
|
|
|
(chunk) { |
|
|
|
// 如果已经设置了取消标志,则不处理这个块 |
|
|
|
if (_shouldCancelAiResponse) { |
|
|
|
isCancelled = true; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
fullResponse += chunk; |
|
|
|
|
|
|
|
// 累积文本并处理TTS |
|
|
|
_pendingTtsText += chunk; |
|
|
|
if (_ttsService.isEnabled.value) { |
|
|
|
if (_pendingTtsText.length >= _minTtsLength) { |
|
|
|
int lastSentenceEnd = _findLastSentenceEnd(_pendingTtsText); |
|
|
|
if (lastSentenceEnd > 0) { |
|
|
|
String textToSpeak = _pendingTtsText.substring(0, lastSentenceEnd + 1); |
|
|
|
try { |
|
|
|
_ttsService.speak(textToSpeak); |
|
|
|
} catch (e) { |
|
|
|
print('播放TTS失败: $e'); |
|
|
|
} |
|
|
|
_pendingTtsText = _pendingTtsText.substring(lastSentenceEnd + 1); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
onError: (e) { |
|
|
|
print('AI响应流错误: $e'); |
|
|
|
// 清理订阅 |
|
|
|
_aiResponseSubscription = null; |
|
|
|
}, |
|
|
|
onDone: () { |
|
|
|
// 如果已取消,不处理剩余的文本 |
|
|
|
if (!isCancelled && !_shouldCancelAiResponse) { |
|
|
|
// 处理剩余的文本 |
|
|
|
if (_ttsService.isEnabled.value && _pendingTtsText.isNotEmpty) { |
|
|
|
try { |
|
|
|
await _ttsService.speak(textToSpeak); |
|
|
|
_ttsService.speak(_pendingTtsText); |
|
|
|
} catch (e) { |
|
|
|
print('播放TTS失败: $e'); |
|
|
|
print('播放剩余TTS失败: $e'); |
|
|
|
} |
|
|
|
_pendingTtsText = _pendingTtsText.substring(lastSentenceEnd + 1); |
|
|
|
} |
|
|
|
} |
|
|
|
_pendingTtsText = ''; |
|
|
|
_aiResponseSubscription = null; |
|
|
|
} |
|
|
|
} |
|
|
|
); |
|
|
|
|
|
|
|
// 等待响应流完成 |
|
|
|
await _aiResponseSubscription!.asFuture(); |
|
|
|
|
|
|
|
} catch (e) { |
|
|
|
print('AI响应生成失败: $e'); |
|
|
|
// 如果AI响应失败,使用默认回复 |
|
|
|
fullResponse = '抱歉,我现在无法回答您的问题。请稍后再试。'; |
|
|
|
} finally { |
|
|
|
// 清理资源 |
|
|
|
_aiResponseSubscription?.cancel(); |
|
|
|
_aiResponseSubscription = null; |
|
|
|
_shouldCancelAiResponse = false; |
|
|
|
} |
|
|
|
|
|
|
|
// 处理剩余的文本 |
|
|
|
if (_ttsService.isEnabled.value && _pendingTtsText.isNotEmpty) { |
|
|
|
try { |
|
|
|
await _ttsService.speak(_pendingTtsText); |
|
|
|
} catch (e) { |
|
|
|
print('播放剩余TTS失败: $e'); |
|
|
|
} |
|
|
|
// 如果响应被取消,不保存到历史记录 |
|
|
|
if (!_shouldCancelAiResponse && fullResponse.isNotEmpty) { |
|
|
|
// 保存助手回复到历史记录 |
|
|
|
_messageHistory.add(Message( |
|
|
|
role: 'assistant', |
|
|
|
content: fullResponse, |
|
|
|
timestamp: DateTime.now(), |
|
|
|
)); |
|
|
|
} |
|
|
|
_pendingTtsText = ''; |
|
|
|
|
|
|
|
// 保存助手回复到历史记录 |
|
|
|
_messageHistory.add(Message( |
|
|
|
role: 'assistant', |
|
|
|
content: fullResponse, |
|
|
|
timestamp: DateTime.now(), |
|
|
|
)); |
|
|
|
|
|
|
|
// 限制历史记录长度 |
|
|
|
if (_messageHistory.length > 20) { |
|
|
|
@ -291,12 +366,20 @@ class BackgroundAgentService extends GetxService { |
|
|
|
|
|
|
|
// 开始语音识别 |
|
|
|
Future<void> startVoiceRecognition() async { |
|
|
|
if (_isListening) return; |
|
|
|
if (_isListening) { |
|
|
|
print('已经在监听中,先停止当前监听'); |
|
|
|
await stopVoiceRecognition(); |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
// 确保语音识别服务已初始化 |
|
|
|
if (!await _voiceRecognitionService.initialize()) { |
|
|
|
throw Exception('无法初始化语音识别服务'); |
|
|
|
print('语音识别服务初始化失败,尝试重新初始化'); |
|
|
|
// 尝试重新初始化 |
|
|
|
await Future.delayed(const Duration(milliseconds: 500)); |
|
|
|
if (!await _voiceRecognitionService.initialize()) { |
|
|
|
throw Exception('无法初始化语音识别服务'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 开始连续识别 |
|
|
|
@ -330,10 +413,20 @@ class BackgroundAgentService extends GetxService { |
|
|
|
_hasRecognizedSpeech = true; |
|
|
|
_lastSpeechTime = DateTime.now(); |
|
|
|
|
|
|
|
// 如果系统正在播放TTS,检测到用户开始说话时立即中断TTS播放 |
|
|
|
// 如果系统正在播放TTS或接收AI响应,检测到用户开始说话时立即中断 |
|
|
|
if (_isSpeaking && event.text.trim().isNotEmpty) { |
|
|
|
print('检测到用户开始说话,中断TTS播放'); |
|
|
|
print('检测到用户开始说话,中断TTS播放和AI响应'); |
|
|
|
_ttsService.stop(); // 停止当前TTS播放 |
|
|
|
|
|
|
|
// 设置标志,表示应该取消当前的AI响应 |
|
|
|
_shouldCancelAiResponse = true; |
|
|
|
|
|
|
|
// 取消当前的AI响应流订阅 |
|
|
|
_aiResponseSubscription?.cancel(); |
|
|
|
_aiResponseSubscription = null; |
|
|
|
|
|
|
|
// 清空待处理的TTS文本 |
|
|
|
_pendingTtsText = ''; |
|
|
|
} |
|
|
|
|
|
|
|
// 取消之前的静默计时器 |
|
|
|
@ -380,6 +473,8 @@ class BackgroundAgentService extends GetxService { |
|
|
|
if (!_isListening) return ''; |
|
|
|
|
|
|
|
try { |
|
|
|
print('停止语音识别...'); |
|
|
|
|
|
|
|
// 取消静默计时器 |
|
|
|
_silenceTimer?.cancel(); |
|
|
|
_silenceTimer = null; |
|
|
|
@ -402,11 +497,18 @@ class BackgroundAgentService extends GetxService { |
|
|
|
_isListening = false; |
|
|
|
isListening.value = false; |
|
|
|
|
|
|
|
print('语音识别已停止'); |
|
|
|
return result; |
|
|
|
} catch (e) { |
|
|
|
print('停止语音识别失败: $e'); |
|
|
|
_isListening = false; |
|
|
|
isListening.value = false; |
|
|
|
|
|
|
|
// 尝试强制重置语音识别服务 |
|
|
|
try { |
|
|
|
await _voiceRecognitionService.initialize(); |
|
|
|
} catch (_) {} |
|
|
|
|
|
|
|
return recognizedText.value; // 返回当前已识别的文本 |
|
|
|
} |
|
|
|
} |
|
|
|
@ -436,6 +538,7 @@ class BackgroundAgentService extends GetxService { |
|
|
|
_silenceTimer?.cancel(); |
|
|
|
_noSpeechTimer?.cancel(); |
|
|
|
_ttsSpeakingSubscription?.cancel(); |
|
|
|
_aiResponseSubscription?.cancel(); |
|
|
|
_shouldContinueInteraction = false; |
|
|
|
super.onClose(); |
|
|
|
} |
|
|
|
|