From 090d80321dda80bcefb4ad10fc126e29956c5b1a Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Tue, 25 Mar 2025 19:34:47 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=80=89=E6=8B=A9=E9=9F=B3?= =?UTF-8?q?=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data/services/speech_factory.dart | 17 +- .../chat/controllers/chat_controller.dart | 169 ++++++++++-------- 2 files changed, 94 insertions(+), 92 deletions(-) diff --git a/lib/data/services/speech_factory.dart b/lib/data/services/speech_factory.dart index b9c1cad0e..45e4f925a 100644 --- a/lib/data/services/speech_factory.dart +++ b/lib/data/services/speech_factory.dart @@ -1,7 +1,5 @@ import 'package:get/get.dart'; -import 'package:get_storage/get_storage.dart'; import 'asr_service.dart'; -import 'speech_impl/voice_clone_tts_service.dart'; import 'tts_service.dart'; import 'speech_impl/azure_asr_service.dart'; import 'speech_impl/azure_tts_service.dart'; @@ -59,26 +57,15 @@ class SpeechFactory extends GetxService { Get.delete(force: true); } - final GetStorage storage = GetStorage(); - String timbre = storage.read('timbre') ?? '1'; - // 根据类型注册新服务 switch (_currentServiceType) { case SpeechServiceType.azure: Get.lazyPut(() => AzureAsrService()); - if (timbre == '1') { - Get.lazyPut(() => AzureTtsService()); - } else { - Get.lazyPut(() => VoiceCloneTtsService()); - } + Get.lazyPut(() => AzureTtsService()); break; case SpeechServiceType.flutter: Get.lazyPut(() => FlutterAsrService()); - if (timbre == '1') { - Get.lazyPut(() => FlutterTtsService()); - } else { - Get.lazyPut(() => VoiceCloneTtsService()); - } + Get.lazyPut(() => FlutterTtsService()); break; } } diff --git a/lib/modules/chat/controllers/chat_controller.dart b/lib/modules/chat/controllers/chat_controller.dart index 25d567714..57ebc4d4c 100644 --- a/lib/modules/chat/controllers/chat_controller.dart +++ b/lib/modules/chat/controllers/chat_controller.dart @@ -1,6 +1,8 @@ import 'dart:async'; import 'package:get/get.dart'; import 'package:flutter/widgets.dart'; +import 'package:get_storage/get_storage.dart'; +import '../../../data/services/speech_impl/voice_clone_tts_service.dart'; import '../models/message_model.dart'; import '../../../core/utils/logger.dart'; import '../../../data/services/volcano_ai_service.dart'; @@ -15,10 +17,11 @@ import '../../../data/services/voice_interaction_service.dart'; class ChatController extends GetxController { // 服务 final VolcanoAIService _aiService = Get.find(); - final TtsService _ttsService = Get.find(); + late TtsService _ttsService; final AsrService _asrService = Get.find(); final ChatHistoryService _historyService = Get.find(); - final VoiceInteractionService _voiceInteractionService = Get.find(); + final VoiceInteractionService _voiceInteractionService = + Get.find(); // 添加ScrollController用于控制列表滚动 final ScrollController scrollController = ScrollController(); @@ -37,7 +40,7 @@ class ChatController extends GetxController { final RxBool isSending = false.obs; final RxBool isTtsEnabled = true.obs; final RxBool isVoiceRecognitionActive = false.obs; - + // 语音交互模式 bool _isVoiceInteractionMode = false; @@ -48,7 +51,7 @@ class ChatController extends GetxController { // 临时消息索引,用于更新临时消息 int _tempMessageIndex = -1; - + // 防抖计时器 Timer? _scrollDebounceTimer; @@ -59,10 +62,10 @@ class ChatController extends GetxController { // 取消标志 bool _shouldCancelAiResponse = false; - + // 自动停止语音识别计时器 Timer? _autoStopTimer; - + // 记录最后一次活动时间 DateTime _lastActivityTime = DateTime.now(); @@ -70,11 +73,11 @@ class ChatController extends GetxController { Future onInit() async { super.onInit(); Logger.info('ChatController onInit'); - - + // 从路由参数获取Agent信息 final Map args = Get.arguments ?? {}; - final String agentId = args['agent'] ?? args['agentId'] ?? 'personal_assistant'; + final String agentId = + args['agent'] ?? args['agentId'] ?? 'personal_assistant'; // 获取Agent final Agent? foundAgent = AgentProvider.getAgentById(agentId); @@ -85,10 +88,20 @@ class ChatController extends GetxController { } agent = foundAgent; - + + final GetStorage storage = GetStorage(); + String timbre = storage.read('timbre') ?? '1'; + + if (timbre == '1') { + _ttsService = Get.find(); + } else { + Get.put(VoiceCloneTtsService()); + _ttsService = Get.find(); + } + _ttsService.initialize(); _asrService.initialize(); - + // 设置语音交互模式 _isVoiceInteractionMode = args['isVoiceInteraction'] == true; @@ -130,7 +143,8 @@ class ChatController extends GetxController { /// 订阅VoiceInteractionService事件 void _subscribeToVoiceInteractionEvents() { if (_voiceInteractionService.eventStream != null) { - _voiceInteractionSubscription = _voiceInteractionService.eventStream.listen((event) { + _voiceInteractionSubscription = + _voiceInteractionService.eventStream.listen((event) { if (event is RecognitionStartedEvent) { // 收到recognitionStarted事件时,停止当前的AI交互、录音和TTS _stopCurrentInteraction(); @@ -145,26 +159,27 @@ class ChatController extends GetxController { /// 处理聊天历史事件 void _processChatHistoryEvent(ChatHistoryEvent event) { Logger.info('收到后台聊天历史事件: agentId=${event.agentId}'); - + // 如果消息不是空的且不是当前Agent的,不处理 if (event.userMessage.isEmpty || event.agentId != agent.id) { return; } - + // 添加用户消息 final userMsg = Message( role: 'user', content: event.userMessage, timestamp: DateTime.fromMillisecondsSinceEpoch(event.timestamp), ); - + // 添加助手消息 final assistantMsg = Message( role: 'assistant', content: event.assistantMessage, - timestamp: DateTime.fromMillisecondsSinceEpoch(event.timestamp + 1), // 助手消息稍后一毫秒 + timestamp: + DateTime.fromMillisecondsSinceEpoch(event.timestamp + 1), // 助手消息稍后一毫秒 ); - + // 防止消息重复 if (!_isDuplicateMessage(userMsg) && !_isDuplicateMessage(assistantMsg)) { messages.add(userMsg); @@ -176,15 +191,15 @@ class ChatController extends GetxController { /// 停止当前交互 void _stopCurrentInteraction() { Logger.info('收到后台语音识别开始事件,停止当前AI交互'); - + // 停止当前AI交互 if (isSending.value || _aiResponseSubscription != null) { _interruptCurrentResponse("收到后台语音识别开始事件,中断当前响应"); } - + // 停止TTS _ttsService.stop(); - + // 停止语音识别 if (isVoiceRecognitionActive.value) { stopVoiceRecognition(); @@ -194,7 +209,7 @@ class ChatController extends GetxController { @override void onClose() { Logger.info('ChatController onClose'); - + // 清理资源 scrollController.dispose(); _aiResponseSubscription?.cancel(); @@ -202,10 +217,10 @@ class ChatController extends GetxController { _voiceInteractionSubscription?.cancel(); _autoStopTimer?.cancel(); _scrollDebounceTimer?.cancel(); // 清理防抖计时器 - + // 停止TTS _ttsService.stop(); - + if (isVoiceRecognitionActive.value) { _asrService.stopContinuousRecognition(); } @@ -220,33 +235,32 @@ class ChatController extends GetxController { /// 滚动到列表底部方法,添加防抖功能 void _scrollToBottom() { - - if (scrollController.hasClients && messages.isNotEmpty) { - try { - scrollController.animateTo( - scrollController.position.maxScrollExtent, - duration: Duration(milliseconds: 300), - curve: Curves.easeOut, - ); - } catch (e) { - Logger.error('滚动到底部失败', e); - } + if (scrollController.hasClients && messages.isNotEmpty) { + try { + scrollController.animateTo( + scrollController.position.maxScrollExtent, + duration: Duration(milliseconds: 300), + curve: Curves.easeOut, + ); + } catch (e) { + Logger.error('滚动到底部失败', e); } + } } /// 中断当前响应 void _interruptCurrentResponse(String reason) { Logger.info('${reason}'); - + // 停止TTS _ttsService.stop(); - + // 取消AI响应流订阅 if (_aiResponseSubscription != null) { _shouldCancelAiResponse = true; _aiResponseSubscription!.cancel(); _aiResponseSubscription = null; - + // 将加载中的消息转换为正常消息,保留已输出的内容 final index = messages.indexWhere((m) => m.isLoading); if (index != -1) { @@ -255,7 +269,7 @@ class ChatController extends GetxController { ); messages.refresh(); } - + // 重置发送状态 isSending.value = false; } @@ -278,14 +292,13 @@ class ChatController extends GetxController { ); messages.add(userMessage); - // 清空输入框 inputText.value = ''; // 添加加载中的消息 final loadingMessage = Message.loading(); messages.add(loadingMessage); - + WidgetsBinding.instance.addPostFrameCallback((_) { _scrollToBottom(); }); @@ -298,7 +311,7 @@ class ChatController extends GetxController { // 重置取消标志 _shouldCancelAiResponse = false; - + String fullResponse = ''; // 获取AI响应流 @@ -327,12 +340,12 @@ class ChatController extends GetxController { // 完成响应,将isLoading设为false _finalizeResponse(fullResponse); - + // 如果TTS启用,处理缓冲区中的剩余文本 if (isTtsEnabled.value) { _ttsService.flushStream(); } - + // 保存聊天历史 _saveChatHistory(); }, @@ -345,7 +358,7 @@ class ChatController extends GetxController { ); } catch (e) { Logger.error('发送消息失败', e); - + _handleResponseError(); } } @@ -353,10 +366,11 @@ class ChatController extends GetxController { // 构建消息历史 List> _buildMessageHistory() { final List> messageHistory = []; - + // 只取最近的10条消息作为上下文 final recentMessages = messages.where((m) => !m.isLoading).toList(); - final startIndex = recentMessages.length > 10 ? recentMessages.length - 10 : 0; + final startIndex = + recentMessages.length > 10 ? recentMessages.length - 10 : 0; for (int i = startIndex; i < recentMessages.length; i++) { final m = recentMessages[i]; @@ -365,7 +379,7 @@ class ChatController extends GetxController { 'content': m.content, }); } - + return messageHistory; } @@ -393,7 +407,7 @@ class ChatController extends GetxController { messages.refresh(); _scrollToBottom(); } - + isSending.value = false; _aiResponseSubscription = null; } @@ -408,7 +422,7 @@ class ChatController extends GetxController { ); messages.refresh(); } - + isSending.value = false; _aiResponseSubscription = null; } @@ -429,18 +443,17 @@ class ChatController extends GetxController { _voiceInteractionService.pauseVoiceInteraction(); try { - // 启动连续语音识别 // 注意:在UI更新后,使用模拟波形动画替代实际麦克风输入的波形数据 final recognitionStream = await _asrService.startContinuousRecognition(); - + isVoiceRecognitionActive.value = true; isListening.value = true; recognizedText.value = ''; - + // 重置临时消息索引 _tempMessageIndex = -1; - + // 启动自动停止计时器 _startAutoStopTimer(); @@ -460,7 +473,7 @@ class ChatController extends GetxController { void _handleRecognitionEvent(RecognitionEvent event) { // 更新活动时间 _updateActivityTime(); - + switch (event.type) { case RecognitionEventType.intermediateResult: _handleRecognizing(event.text); @@ -486,24 +499,24 @@ class ChatController extends GetxController { void _startAutoStopTimer() { // 取消现有计时器 _autoStopTimer?.cancel(); - + // 初始化最后活动时间 _updateActivityTime(); - + // 根据模式设置超时时间 final int timeoutSeconds = _isVoiceInteractionMode ? 8 : 15; - + // 创建新的计时器,每秒检查一次 _autoStopTimer = Timer.periodic(Duration(seconds: 1), (timer) { // 检查是否有活动 bool isActive = _ttsService.isSpeaking || isSpeechDetected.value; - + // 如果有活动,更新时间并继续等待 if (isActive) { _updateActivityTime(); return; } - + // 检查空闲时间 int idleSeconds = DateTime.now().difference(_lastActivityTime).inSeconds; if (idleSeconds >= timeoutSeconds) { @@ -516,17 +529,17 @@ class ChatController extends GetxController { // 处理语音识别中的状态 void _handleRecognizing(String text) { if (text.isEmpty) return; - + recognizedText.value = text; - + // 设置语音检测状态为true,用于控制波形动画 if (!isSpeechDetected.value) { isSpeechDetected.value = true; } - + // 更新活动时间 _updateActivityTime(); - + // 用户开始讲话时,立即停止当前TTS和中断AI响应 if (isSending.value || _aiResponseSubscription != null) { _interruptCurrentResponse("检测到用户开始讲话,中断当前响应"); @@ -558,10 +571,10 @@ class ChatController extends GetxController { // 处理语音识别最终结果 void _handleFinalResult(String text) { if (text.isEmpty) return; - + Logger.info('处理语音识别最终结果: $text'); recognizedText.value = text; - + // 更新活动时间 _updateActivityTime(); @@ -569,7 +582,7 @@ class ChatController extends GetxController { if (_tempMessageIndex >= 0 && _tempMessageIndex < messages.length) { messages.removeAt(_tempMessageIndex); } - + // 重置临时消息索引 _tempMessageIndex = -1; @@ -578,7 +591,7 @@ class ChatController extends GetxController { // 重置识别文本,但不关闭语音识别框 recognizedText.value = ''; - + // 重置语音检测状态 isSpeechDetected.value = false; } @@ -586,14 +599,14 @@ class ChatController extends GetxController { // 处理语音识别事件错误 void _handleRecognitionEventError(String? error) { Logger.error('语音识别错误: ${error ?? "未知错误"}'); - + _cleanupRecognition(); } // 处理语音识别流错误 void _handleRecognitionError(dynamic error) { Logger.error('语音识别错误', error); - + _cleanupRecognition(); } @@ -605,10 +618,10 @@ class ChatController extends GetxController { messages.removeAt(_tempMessageIndex); } } - + // 重置临时消息索引 _tempMessageIndex = -1; - + // 停止语音识别 isVoiceRecognitionActive.value = false; isListening.value = false; @@ -636,7 +649,7 @@ class ChatController extends GetxController { messages.removeAt(_tempMessageIndex); } } - + // 重置临时消息索引 _tempMessageIndex = -1; } catch (e) { @@ -691,10 +704,12 @@ class ChatController extends GetxController { /// 检查是否为重复消息 bool _isDuplicateMessage(Message message) { - return messages.any((m) => - m.role == message.role && - m.content == message.content && - (m.timestamp.millisecondsSinceEpoch - message.timestamp.millisecondsSinceEpoch).abs() < 5000 - ); + return messages.any((m) => + m.role == message.role && + m.content == message.content && + (m.timestamp.millisecondsSinceEpoch - + message.timestamp.millisecondsSinceEpoch) + .abs() < + 5000); } }