import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'dart:async'; import '../../../data/services/azure_asr_service.dart'; import '../../../data/services/volcano_translation_service.dart'; import '../../../data/services/volcano_tts_api_service.dart'; // import '../../../core/utils/logger.dart'; class TranslationController extends GetxController { // Services final AzureAsrService _asrService = Get.find(); final VolcanoTranslationService _translationService = Get.find(); final VolcanoTtsApiService _ttsService = Get.find(); // State variables final isRecognizing = false.obs; final isTranslating = false.obs; final isTtsEnabled = true.obs; final recognizedText = ''.obs; final translatedText = ''.obs; final sourceLanguage = '中文 (Chinese)'.obs; final targetLanguage = '英语 (English)'.obs; final translationHistory = [].obs; // Current translation item TranslationItem? currentItem; // Subscription for recognition stream StreamSubscription? _recognitionSubscription; // Scroll controller for ListView final ScrollController scrollController = ScrollController(); // Timer for debouncing translation requests Timer? _translationDebounceTimer; @override void onInit() { super.onInit(); // Initialize the translation service _initTranslationService(); // Start voice recognition automatically when the page is loaded startRecognition(); } @override void onClose() { stopRecognition(); _recognitionSubscription?.cancel(); _translationDebounceTimer?.cancel(); scrollController.dispose(); super.onClose(); } // Initialize the translation service Future _initTranslationService() async { try { await _translationService.initialize(); } catch (e) { Get.snackbar( 'Error', 'Failed to initialize translation service: ${e.toString()}', snackPosition: SnackPosition.BOTTOM, ); } } // Toggle TTS feature void toggleTts() { isTtsEnabled.toggle(); } // Start voice recognition Future startRecognition() async { if (isRecognizing.value) return; try { // Initialize a new translation item currentItem = TranslationItem( sourceText: '', translatedText: '', sourceLanguage: sourceLanguage.value, targetLanguage: targetLanguage.value, timestamp: DateTime.now(), ); // Start the voice recognition service final success = await _asrService.startContinuousRecognition(); if (success) { isRecognizing.value = true; // Reset the recognized text recognizedText.value = ''; translatedText.value = ''; // Listen for recognition events _recognitionSubscription = _asrService.recognitionStream?.listen((event) { switch (event.type) { case RecognitionEventType.finalResult: if (event.text.isNotEmpty) { handleFinalResult(event.text); } break; case RecognitionEventType.recognizing: // Update the UI with intermediate results if (event.text.isNotEmpty) { handleIntermediateResult(event.text); } break; case RecognitionEventType.error: Get.snackbar( 'Error', 'Recognition error: ${event.error}', snackPosition: SnackPosition.BOTTOM, ); break; default: break; } }); } else { throw Exception('Failed to start continuous recognition'); } } catch (e) { isRecognizing.value = false; Get.snackbar( 'Error', 'Failed to start voice recognition: ${e.toString()}', snackPosition: SnackPosition.BOTTOM, ); } } // Stop voice recognition Future stopRecognition() async { if (!isRecognizing.value) return; try { await _asrService.stopContinuousRecognition(); _recognitionSubscription?.cancel(); _recognitionSubscription = null; isRecognizing.value = false; // 添加当前项到历史(如果有内容) finalizeCurrentItem(); // 重置当前显示的文本 recognizedText.value = ''; translatedText.value = ''; } catch (e) { Get.snackbar( 'Error', 'Failed to stop voice recognition: ${e.toString()}', snackPosition: SnackPosition.BOTTOM, ); } } // Handle intermediate recognition result void handleIntermediateResult(String text) { if (text.isEmpty) return; // 如果当前没有活跃的翻译项,创建一个新的 if (currentItem == null) { currentItem = TranslationItem( sourceText: text, translatedText: '', sourceLanguage: sourceLanguage.value, targetLanguage: targetLanguage.value, timestamp: DateTime.now(), ); } else { // 更新当前翻译项 currentItem!.sourceText = text; } // 实时更新识别文本 recognizedText.value = text; // Debounce translation requests to avoid too many API calls _translationDebounceTimer?.cancel(); _translationDebounceTimer = Timer(const Duration(milliseconds: 300), () { translateText(text, isFinal: false); }); // Scroll to the bottom to show the latest content _scrollToBottom(); } // Handle final recognition result void handleFinalResult(String text) { if (text.isEmpty) return; // 更新当前翻译项(如果不存在则创建一个) if (currentItem == null) { currentItem = TranslationItem( sourceText: text, translatedText: '', sourceLanguage: sourceLanguage.value, targetLanguage: targetLanguage.value, timestamp: DateTime.now(), ); } else { currentItem!.sourceText = text; } // 更新识别文本 recognizedText.value = text; // 翻译最终结果 (添加到历史的逻辑已移至translateText方法) translateText(text, isFinal: true).then((_) { // 作为安全措施,确保当前项被添加到历史中 // 这是为了处理translateText中可能因为条件不满足而没有添加到历史的情况 finalizeCurrentItem(); }); // Scroll to the bottom to show the latest content _scrollToBottom(); } // Finalize the current translation item and add it to history void finalizeCurrentItem() { if (currentItem != null && currentItem!.sourceText.isNotEmpty && currentItem!.translatedText.isNotEmpty && !translationHistory.contains(currentItem)) { translationHistory.add(currentItem!); currentItem = null; } } // Handle voice recognition result (legacy method, kept for compatibility) void handleVoiceResult(String text) { handleFinalResult(text); } // Translate the recognized text Future translateText(String text, {bool isFinal = true}) async { if (text.isEmpty) return; isTranslating.value = true; // 保存当前的翻译项引用,确保异步返回时更新正确的项 final TranslationItem? itemToUpdate = currentItem; try { final result = await _translationService.translateText( text: text, sourceLanguage: sourceLanguage.value, targetLanguage: targetLanguage.value, ); if (result != null) { // 实时更新翻译文本 translatedText.value = result; // 确保更新的是发起翻译请求时的翻译项,而不是当前可能已经变化的翻译项 if (itemToUpdate != null && itemToUpdate.sourceText == text) { itemToUpdate.translatedText = result; // 如果是最终结果,并且当前项仍然是发起请求时的项,则添加到历史 if (isFinal && itemToUpdate == currentItem) { translationHistory.add(itemToUpdate); currentItem = null; // 重要:将当前项设为null,表示已完成 // 重置当前显示的文本,避免与历史项重复 recognizedText.value = ''; translatedText.value = ''; } // Play TTS if enabled and this is a final result if (isTtsEnabled.value && isFinal) { playTranslatedText(result); } } } } catch (e) { Get.snackbar( 'Error', 'Translation failed: ${e.toString()}', snackPosition: SnackPosition.BOTTOM, ); } finally { isTranslating.value = false; } } // Play the translated text using TTS Future playTranslatedText(String text) async { if (text.isEmpty) return; try { await _ttsService.speakSingle(text); } catch (e) { Get.snackbar( 'Error', 'Failed to play translation: ${e.toString()}', snackPosition: SnackPosition.BOTTOM, ); } } // Play a specific translation item Future playTranslationItem(TranslationItem item) async { await playTranslatedText(item.translatedText); } // Scroll to the bottom of the list void _scrollToBottom() { if (scrollController.hasClients) { Future.delayed(const Duration(milliseconds: 100), () { scrollController.animateTo( scrollController.position.maxScrollExtent, duration: const Duration(milliseconds: 300), curve: Curves.easeOut, ); }); } } } // Model class for translation items class TranslationItem { String sourceText; String translatedText; final String sourceLanguage; final String targetLanguage; final DateTime timestamp; TranslationItem({ required this.sourceText, required this.translatedText, required this.sourceLanguage, required this.targetLanguage, required this.timestamp, }); }