import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'dart:async'; import 'dart:convert'; import 'package:get_storage/get_storage.dart'; import '../../../data/services/volcano_translation_service.dart'; import '../../../data/services/azure_asr_service.dart'; import '../../../data/services/azure_tts_service.dart'; import '../../../data/services/language_manager.dart'; import '../../../core/utils/logger.dart'; class TranslationController extends GetxController { // 服务 final AzureAsrService _asrService = Get.find(); final VolcanoTranslationService _translationService = Get.find(); final AzureTtsService _ttsService = Get.find(); final LanguageManager _languageManager = Get.find(); final GetStorage _storage = GetStorage(); // 存储相关 static const String _historyKey = 'translation_history'; static const int _maxHistoryItems = 100; // 状态变量 final isRecognizing = false.obs; final isTranslating = false.obs; final isTtsEnabled = true.obs; // 语言相关 final sourceLanguage = '中文(简体)'.obs; final targetLanguage = '英语'.obs; final sourceLanguageCode = 'zh-CN'.obs; final targetLanguageCode = 'en-US'.obs; var detectedLanguageCode = ''; // 翻译历史和当前项 final translationHistory = [].obs; final Rx currentItem = Rx(null); String? currentSessionId; // UI相关 final ScrollController scrollController = ScrollController(); // 其他 StreamSubscription? _recognitionSubscription; Timer? _translationDebounceTimer; Timer? _scrollDebounceTimer; // 滚动防抖定时器 bool _isNearBottom = true; // 标记是否在底部附近 // 获取支持的语言列表 Map get supportedLanguages => _languageManager.getChineseNameToAsrCodeMap(); @override void onInit() { super.onInit(); _initController(); } // 初始化控制器 void _initController() { _initLanguageCodes(); loadTranslationHistory(); _initServices(); _setupListeners(); // 初始化完成后滚动到底部 WidgetsBinding.instance.addPostFrameCallback((_) { // 延迟 300ms 再次尝试滚动,确保列表已完全渲染 Future.delayed(const Duration(milliseconds: 300), () { scrollToBottom(animate: false); }); }); } // 重新初始化控制器(公共方法) void reinitialize() { _setupListeners(); // 初始化完成后滚动到底部 WidgetsBinding.instance.addPostFrameCallback((_) { scrollToBottom(animate: false); }); } // 仅设置滚动监听器(不启动翻译) void setupScrollListener() { // 移除之前的监听器,避免重复添加 scrollController.removeListener(_onScroll); // 重新添加滚动监听器 scrollController.addListener(_onScroll); } // 初始化服务 Future _initServices() async { await _initTranslationService(); await _initAzureAsrService(); } // 初始化语言代码 void _initLanguageCodes() { final sourceAsrCode = _languageManager.getAsrCodeByChineseName(sourceLanguage.value); if (sourceAsrCode != null) { sourceLanguageCode.value = sourceAsrCode; } final targetAsrCode = _languageManager.getAsrCodeByChineseName(targetLanguage.value); if (targetAsrCode != null) { targetLanguageCode.value = targetAsrCode; } } // 初始化翻译服务 Future _initTranslationService() async { try { await _translationService.initialize(); } catch (e) { Logger.error('初始化翻译服务失败: $e'); } } // 初始化语音识别服务 Future _initAzureAsrService() async { try { final List asrSupportedLanguages = [sourceLanguageCode.value, targetLanguageCode.value]; await _ttsService.initialize(); await _asrService.initialize( autoDetectLanguage: true, supportedLanguages: asrSupportedLanguages, ); } catch (e) { Logger.error('初始化语音识别服务失败: $e'); } } // 设置监听器 void _setupListeners() { // 监听历史记录变化,当有新项添加时滚动到底部 ever(translationHistory, (_) { if (_isNearBottom) { scrollToBottom(); } }); // 监听当前项变化,当有新项添加时滚动到底部 ever(currentItem, (item) { if (item != null && _isNearBottom) { scrollToBottom(); } }); // 监听滚动位置,判断是否在底部附近 scrollController.addListener(_onScroll); } // 滚动监听器 void _onScroll() { if (!scrollController.hasClients) return; // 判断是否在底部附近(距离底部20像素内) final maxScroll = scrollController.position.maxScrollExtent; final currentScroll = scrollController.position.pixels; _isNearBottom = (maxScroll - currentScroll) < 20; } @override void onClose() { stopRecognition(); _recognitionSubscription?.cancel(); _translationDebounceTimer?.cancel(); _scrollDebounceTimer?.cancel(); scrollController.removeListener(_onScroll); scrollController.dispose(); super.onClose(); } // 更新ASR支持的语言 Future _updateAsrSupportedLanguages() async { try { final List asrSupportedLanguages = [sourceLanguageCode.value, targetLanguageCode.value]; await _asrService.setSupportedLanguages(asrSupportedLanguages); } catch (e) { Logger.error('更新ASR支持的语言失败: $e'); } } // 切换TTS功能 void toggleTts() => isTtsEnabled.toggle(); // 开始语音识别 Future startRecognition() async { if (isRecognizing.value) return; try { await _updateAsrSupportedLanguages(); currentSessionId = DateTime.now().millisecondsSinceEpoch.toString(); // 初始化新的翻译项 currentItem.value = TranslationItem( sourceText: '', translatedText: '', sourceLanguageCode: sourceLanguageCode.value, targetLanguageCode: targetLanguageCode.value, timestamp: DateTime.now(), sessionId: currentSessionId!, isFirstInSession: true, ); // 开始连续语音识别 final recognitionStream = await _asrService.startContinuousRecognition(); isRecognizing.value = true; // 监听识别结果 _recognitionSubscription?.cancel(); _recognitionSubscription = recognitionStream.listen(_handleRecognitionEvent); } catch (e) { isRecognizing.value = false; Logger.error('启动语音识别失败: $e'); } } // 处理识别事件 void _handleRecognitionEvent(RecognitionEvent event) { if (event.type == RecognitionEventType.finalResult && event.text.isNotEmpty) { detectedLanguageCode = event.detectedLanguage; handleFinalResult(event.text); } else if (event.type == RecognitionEventType.intermediateResult && event.text.isNotEmpty) { if (event.detectedLanguage.isNotEmpty) { detectedLanguageCode = event.detectedLanguage; } handleIntermediateResult(event.text); } else if (event.type == RecognitionEventType.error) { isRecognizing.value = false; Logger.error('语音识别错误: ${event.error}'); } else if (event.type == RecognitionEventType.canceled) { isRecognizing.value = false; Logger.error('语音识别取消: ${event.error}'); } } // 停止语音识别 Future stopRecognition() async { if (!isRecognizing.value) return; try { await _asrService.stopContinuousRecognition(); _recognitionSubscription?.cancel(); _recognitionSubscription = null; isRecognizing.value = false; // 如果当前有中间结果,将其作为最终结果处理 if (currentItem.value != null && currentItem.value!.sourceText.isNotEmpty) { // 标记为最终结果 currentItem.value!.isIntermediate = false; // 如果还没有翻译结果,尝试翻译 if (currentItem.value!.translatedText.isEmpty) { await translateText(currentItem.value!.sourceText); } translationHistory.add(currentItem.value!); saveTranslationHistory(); } currentSessionId = null; currentItem.value = null; } catch (e) { Logger.error('停止语音识别失败: $e'); } } // 处理中间识别结果 void handleIntermediateResult(String text) { if (text.isEmpty) return; if (currentItem.value == null) { currentSessionId ??= DateTime.now().millisecondsSinceEpoch.toString(); currentItem.value = TranslationItem( sourceText: text, translatedText: '', sourceLanguageCode: sourceLanguageCode.value, targetLanguageCode: targetLanguageCode.value, timestamp: DateTime.now(), sessionId: currentSessionId!, isFirstInSession: translationHistory.isEmpty || translationHistory.last.sessionId != currentSessionId, isIntermediate: true, // 保留标记,用于内部逻辑 ); } else { // 更新当前项 currentItem.value!.sourceText = text; currentItem.value!.isIntermediate = true; // 保留标记,用于内部逻辑 currentItem.refresh(); } // 防抖处理翻译请求 _translationDebounceTimer?.cancel(); _translationDebounceTimer = Timer(const Duration(milliseconds: 300), () { translateCurrentItem(isIntermediate: true); }); } // 翻译当前项 Future translateCurrentItem({bool isIntermediate = false}) async { if (currentItem.value == null || currentItem.value!.sourceText.isEmpty) return; await translateText(currentItem.value!.sourceText); } // 处理最终识别结果 Future handleFinalResult(String text) async { if (text.isEmpty || currentItem.value == null) return; // 更新当前项,但不立即添加到历史记录 currentItem.value!.sourceText = text; currentItem.value!.isIntermediate = false; // 标记为最终结果 currentItem.refresh(); // 先翻译,保持UI稳定 final translationResult = await translateText(text); // 给UI一点时间更新 await Future.delayed(const Duration(milliseconds: 100)); // 翻译完成后再添加到历史记录 if (currentItem.value != null) { // 确保添加到历史记录的是最终结果 translationHistory.add(currentItem.value!); saveTranslationHistory(); // 播放TTS if (translationResult != null && isTtsEnabled.value) { await playTranslatedText(translationResult); } // 重置当前项,准备下一次输入 currentItem.value = null; } } // 翻译文本 Future translateText(String sourceText) async { if (sourceText.isEmpty) return null; isTranslating.value = true; String? translationResult; try { // 根据检测到的语言确定源语言和目标语言 final bool shouldSwap = detectedLanguageCode == sourceLanguageCode.value; final detectedSourceLanguageCode = shouldSwap ? sourceLanguageCode.value : targetLanguageCode.value; final detectedTargetLanguageCode = shouldSwap ? targetLanguageCode.value : sourceLanguageCode.value; // 调用翻译服务 translationResult = await _translationService.translateText( text: sourceText, sourceLanguageCode: detectedSourceLanguageCode, targetLanguageCode: detectedTargetLanguageCode, ); if (translationResult != null) { _updateTranslationResult(sourceText, translationResult, detectedSourceLanguageCode, detectedTargetLanguageCode); } } catch (e) { Logger.error('翻译失败: $e'); } finally { isTranslating.value = false; } return translationResult; } // 更新翻译结果 void _updateTranslationResult(String sourceText, String result, String sourceCode, String targetCode) { if (currentItem.value != null && currentItem.value!.sourceText.contains(sourceText)) { // 更新当前项 currentItem.value!.translatedText = result; currentItem.value!.sourceLanguageCode = sourceCode; currentItem.value!.targetLanguageCode = targetCode; currentItem.refresh(); } else { // 更新历史记录 final index = translationHistory.indexWhere((item) => item.sourceText == sourceText); if (index != -1) { translationHistory[index].translatedText = result; translationHistory[index].sourceLanguageCode = sourceCode; translationHistory[index].targetLanguageCode = targetCode; translationHistory.refresh(); saveTranslationHistory(); } } } // 播放翻译文本 Future playTranslatedText(String text) async { if (text.isEmpty) return; try { final bool shouldSwap = detectedLanguageCode == targetLanguageCode.value; final detectedTargetLanguageCode = shouldSwap ? sourceLanguageCode.value : targetLanguageCode.value; final voiceName = _languageManager.getTtsVoiceNameByAsrCode(detectedTargetLanguageCode) ?? 'en-US-AriaNeural'; await _ttsService.setVoice(voiceName); await _ttsService.speak(text); } catch (e) { Logger.error('播放翻译失败: $e'); } } // 停止所有操作 Future stopAll() async { try { if (isRecognizing.value) { await stopRecognition(); } // 取消所有定时器 _translationDebounceTimer?.cancel(); _scrollDebounceTimer?.cancel(); await _ttsService.stop(); } catch (e) { Logger.error('停止操作失败: $e'); } } // 播放特定翻译项 Future playTranslationItem(TranslationItem item) async { if (item.translatedText.isEmpty) return; try { final voiceName = _languageManager.getTtsVoiceNameByAsrCode(item.targetLanguageCode) ?? 'en-US-AriaNeural'; await _ttsService.setVoice(voiceName); await _ttsService.speak(item.translatedText); } catch (e) { Logger.error('播放翻译项失败: $e'); } } // 播放文本 Future playText(String text, {String? languageCode}) async { if (text.isEmpty) return; try { final voiceName = languageCode != null ? _languageManager.getTtsVoiceNameByAsrCode(languageCode) ?? 'en-US-AriaNeural' : _languageManager.getTtsVoiceNameByAsrCode(targetLanguageCode.value) ?? 'en-US-AriaNeural'; await _ttsService.setVoice(voiceName); await _ttsService.speak(text); } catch (e) { Logger.error('播放文本失败: $e'); } } // 简洁高效的滚动到底部方法 void scrollToBottom({bool animate = true}) { if (!scrollController.hasClients) return; // 取消之前的定时器 _scrollDebounceTimer?.cancel(); // 创建新的定时器,防抖处理 _scrollDebounceTimer = Timer(const Duration(milliseconds: 100), () { if (!scrollController.hasClients) return; if (animate) { scrollController.animateTo( scrollController.position.maxScrollExtent, duration: const Duration(milliseconds: 200), curve: Curves.easeOut, ); } else { scrollController.jumpTo(scrollController.position.maxScrollExtent); } }); } // 底部控制栏高度 double _bottomBarHeight = 0.0; // 设置底部控制栏高度 void setBottomBarHeight(double height) { _bottomBarHeight = height; } // 获取底部控制栏高度 double get bottomBarHeight => _bottomBarHeight; // 切换源语言和目标语言 void swapLanguages() { // 先停止当前的翻译操作 stopAll(); final tempName = sourceLanguage.value; sourceLanguage.value = targetLanguage.value; targetLanguage.value = tempName; final tempCode = sourceLanguageCode.value; sourceLanguageCode.value = targetLanguageCode.value; targetLanguageCode.value = tempCode; } // 设置源语言 void setSourceLanguage(String language) { if (sourceLanguage.value != language) { // 先停止当前的翻译操作 stopAll(); sourceLanguage.value = language; final asrCode = _languageManager.getAsrCodeByChineseName(language); if (asrCode != null) { sourceLanguageCode.value = asrCode; } _updateAsrSupportedLanguages(); if (isRecognizing.value) { stopRecognition(); startRecognition(); } } } // 设置目标语言 void setTargetLanguage(String language) { if (targetLanguage.value != language) { // 先停止当前的翻译操作 stopAll(); targetLanguage.value = language; final asrCode = _languageManager.getAsrCodeByChineseName(language); if (asrCode != null) { targetLanguageCode.value = asrCode; } _updateAsrSupportedLanguages(); } } // 加载翻译历史 void loadTranslationHistory() { final historyJson = _storage.read(_historyKey); if (historyJson != null) { try { final List historyList = jsonDecode(historyJson); translationHistory.value = historyList.map((item) => TranslationItem.fromJson(item)).toList(); } catch (e) { _storage.remove(_historyKey); Logger.error('加载翻译历史失败: $e'); } } } // 保存翻译历史 void saveTranslationHistory() { if (translationHistory.length > _maxHistoryItems) { translationHistory.removeRange(0, translationHistory.length - _maxHistoryItems); } try { final List> historyJson = translationHistory.map((item) => item.toJson()).toList(); _storage.write(_historyKey, jsonEncode(historyJson)); } catch (e) { Logger.error('保存翻译历史失败: $e'); } } // 清除翻译历史 void clearTranslationHistory() { translationHistory.clear(); _storage.remove(_historyKey); } } // 翻译项模型 class TranslationItem { String sourceText; String translatedText; String sourceLanguageCode; String targetLanguageCode; final DateTime timestamp; String sessionId; final bool isFirstInSession; bool isIntermediate; // 是否为中间结果 TranslationItem({ required this.sourceText, required this.translatedText, required this.sourceLanguageCode, required this.targetLanguageCode, required this.timestamp, required this.sessionId, this.isFirstInSession = false, this.isIntermediate = false, }); factory TranslationItem.fromJson(Map json) { DateTime timestamp; try { timestamp = DateTime.parse(json['timestamp']); } catch (e) { timestamp = DateTime.now(); } return TranslationItem( sourceText: json['sourceText'] ?? '', translatedText: json['translatedText'] ?? '', sourceLanguageCode: json['sourceLanguageCode'] ?? 'zh-CN', targetLanguageCode: json['targetLanguageCode'] ?? 'en-US', timestamp: timestamp, sessionId: json['sessionId'] ?? DateTime.now().millisecondsSinceEpoch.toString(), isFirstInSession: json['isFirstInSession'] ?? false, isIntermediate: false, // 从存储加载的都是最终结果 ); } Map toJson() { return { 'sourceText': sourceText, 'translatedText': translatedText, 'sourceLanguageCode': sourceLanguageCode, 'targetLanguageCode': targetLanguageCode, 'timestamp': timestamp.toIso8601String(), 'sessionId': sessionId, 'isFirstInSession': isFirstInSession, // 不保存isIntermediate,因为存储的都是最终结果 }; } // 获取源语言的中文名称 String get sourceLanguageName { final languageManager = Get.find(); return languageManager.getChineseNameByAsrCode(sourceLanguageCode) ?? '未知语言'; } // 获取目标语言的中文名称 String get targetLanguageName { final languageManager = Get.find(); return languageManager.getChineseNameByAsrCode(targetLanguageCode) ?? '未知语言'; } }