import 'dart:async'; import 'package:get/get.dart'; import '../../../data/services/volcano_asr_service.dart'; import '../../../data/services/volcano_tts_api_service.dart'; import '../../../core/utils/logger.dart'; import '../models/translation_message_model.dart'; class TranslationController extends GetxController { // Observable states final isListening = false.obs; final isTranslating = false.obs; final sourceLanguage = "Chinese".obs; final targetLanguage = "English".obs; final originalText = "".obs; final translatedText = "".obs; final isUserSpeaking = false.obs; final messages = [].obs; // TTS启用状态 final isTtsEnabled = true.obs; // Services late final VolcanoAsrService _voiceService; late final VolcanoTtsApiService _ttsService; // Stream subscriptions StreamSubscription? _recognitionSubscription; // Timers Timer? _silenceTimer; Timer? _speakingCheckTimer; // State tracking bool _hasRecognizedSpeech = false; bool _hasFinalResult = false; DateTime? _lastSpeechTime; Completer? _recognitionCompleter; // TTS事件订阅 StreamSubscription? _ttsEventSubscription; bool _isTtsSpeaking = false; @override void onInit() { super.onInit(); _initializeServices(); _startSpeakingCheckTimer(); _ttsService = Get.find(); // 设置TTS事件监听 _setupTtsEventListener(); } Future _initializeServices() async { try { _voiceService = Get.find(); _ttsService = Get.find(); await _voiceService.initialize(); await startListening(); } catch (e) { Logger.error('Failed to initialize translation services', e); Get.snackbar( 'Error', 'Failed to initialize translation services: $e', snackPosition: SnackPosition.BOTTOM, ); } } Future startListening() async { if (isListening.value) return; try { isListening.value = true; originalText.value = ''; translatedText.value = ''; _hasRecognizedSpeech = false; _hasFinalResult = false; _recognitionCompleter = Completer(); final success = await _voiceService.startContinuousRecognition(); if (!success) { isListening.value = false; throw Exception('Failed to start voice recognition'); } _recognitionSubscription = _voiceService.recognitionStream?.listen( (event) { switch (event.type) { case RecognitionEventType.recognizing: if (event.text.isNotEmpty) { originalText.value = event.text; _hasRecognizedSpeech = true; _lastSpeechTime = DateTime.now(); isUserSpeaking.value = true; // Cancel previous silence timer _silenceTimer?.cancel(); // Set new silence timer - if no new recognition results in 2 seconds, // consider the user has stopped speaking _silenceTimer = Timer(const Duration(seconds: 2), () { if (_hasRecognizedSpeech && !_hasFinalResult) { _processTranslation(event.text); } }); // If TTS is playing and user starts speaking, stop TTS if (_isTtsSpeaking) { _ttsService.endSession(); } } break; case RecognitionEventType.finalResult: _hasFinalResult = true; isUserSpeaking.value = false; if (event.text.isNotEmpty) { originalText.value = event.text; _hasRecognizedSpeech = true; _lastSpeechTime = DateTime.now(); _processTranslation(event.text); } else if (_hasRecognizedSpeech) { // If final result is empty but we had intermediate results _processTranslation(originalText.value); } // Reset for next utterance _hasRecognizedSpeech = false; _hasFinalResult = false; break; case RecognitionEventType.error: Logger.error('Voice recognition error', event.error); break; default: break; } }, onError: (error) { Logger.error('Voice recognition stream error', error); isListening.value = false; restartListening(); } ); } catch (e) { Logger.error('Failed to start listening', e); isListening.value = false; } } Future _processTranslation(String text) async { if (text.isEmpty) return; try { isTranslating.value = true; // In a real implementation, you would call your translation service here // For now, we'll simulate translation with a delay await Future.delayed(const Duration(milliseconds: 300)); // Simulate translation (in a real app, call your translation API here) final translated = await _simulateTranslation(text); translatedText.value = translated; // Add to messages messages.add(TranslationMessage( original: text, translated: translated, timestamp: DateTime.now(), isFromUser: true, )); // Speak the translation if TTS is enabled if (isTtsEnabled.value) { await _synthesizeText(translated); } } catch (e) { Logger.error('Translation error', e); } finally { isTranslating.value = false; } } // This is a placeholder - in a real app, you would call your translation API Future _simulateTranslation(String text) async { // This is just a simulation - replace with actual translation API call if (sourceLanguage.value == "Chinese" && targetLanguage.value == "English") { // Simulate Chinese to English translation if (text.contains('测试')) return 'I am now starting to test the function of translating spoken Chinese into English.'; return 'Translated from Chinese: $text'; } else { // Simulate English to Chinese translation return '翻译自英语: $text'; } } void switchLanguages() { final temp = sourceLanguage.value; sourceLanguage.value = targetLanguage.value; targetLanguage.value = temp; // Clear current messages when switching languages messages.clear(); } Future restartListening() async { await stopListening(); await Future.delayed(const Duration(milliseconds: 500)); await startListening(); } Future stopListening() async { if (!isListening.value) return; try { await _recognitionSubscription?.cancel(); _recognitionSubscription = null; _silenceTimer?.cancel(); _silenceTimer = null; if (_voiceService.isContinuousRecognitionActive()) { await _voiceService.stopRecognition(); } isListening.value = false; isUserSpeaking.value = false; _hasRecognizedSpeech = false; _hasFinalResult = false; } catch (e) { Logger.error('Failed to stop listening', e); } } void _startSpeakingCheckTimer() { _speakingCheckTimer?.cancel(); _speakingCheckTimer = Timer.periodic(const Duration(milliseconds: 200), (_) { _checkIfUserIsSpeaking(); }); } void _checkIfUserIsSpeaking() { if (_hasRecognizedSpeech && !_hasFinalResult && _lastSpeechTime != null) { final timeSinceLastSpeech = DateTime.now().difference(_lastSpeechTime!); if (timeSinceLastSpeech.inMilliseconds < 1000) { isUserSpeaking.value = true; return; } } isUserSpeaking.value = false; } void _setupTtsEventListener() { _ttsEventSubscription = _ttsService.eventStream.listen(_handleTtsEvent); } void _handleTtsEvent(Map event) { final String eventType = event['type'] as String? ?? 'unknown'; switch (eventType) { case 'ttsSentenceStart': _isTtsSpeaking = true; break; case 'ttsSentenceEnd': case 'sessionFinished': case 'connectionFinished': _isTtsSpeaking = false; break; case 'connectionFailed': case 'sessionFailed': _isTtsSpeaking = false; print('TTS错误: ${event['message']}'); break; } } @override void onClose() { stopListening(); _speakingCheckTimer?.cancel(); // 取消TTS事件订阅 _ttsEventSubscription?.cancel(); // 结束TTS会话 _ttsService.endSession(); super.onClose(); } // Add a method to synthesize text using the new TTS service Future _synthesizeText(String text) async { if (text.isEmpty || !isTtsEnabled.value) return; try { // 如果当前有正在进行的TTS会话,先结束它 if (_isTtsSpeaking) { await _ttsService.endSession(); } // 确保连接已建立 if (!_ttsService.isConnected.value) { final connected = await _ttsService.connect(); if (!connected) { print('无法连接到TTS服务'); return; } } // 开始合成 await _ttsService.synthesize(text, 'zh_female_1'); // 使用默认说话人 } catch (e) { print('TTS播放出错: $e'); } } // Add a method to toggle TTS void toggleTTS() { isTtsEnabled.toggle(); if (!isTtsEnabled.value) { _ttsService.endSession(); } } }