You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

334 lines
9.7 KiB

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 = <TranslationMessage>[].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<String>? _recognitionCompleter;
// TTS事件订阅
StreamSubscription? _ttsEventSubscription;
bool _isTtsSpeaking = false;
@override
void onInit() {
super.onInit();
_initializeServices();
_startSpeakingCheckTimer();
_ttsService = Get.find<VolcanoTtsApiService>();
// 设置TTS事件监听
_setupTtsEventListener();
}
Future<void> _initializeServices() async {
try {
_voiceService = Get.find<VolcanoAsrService>();
_ttsService = Get.find<VolcanoTtsApiService>();
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<void> startListening() async {
if (isListening.value) return;
try {
isListening.value = true;
originalText.value = '';
translatedText.value = '';
_hasRecognizedSpeech = false;
_hasFinalResult = false;
_recognitionCompleter = Completer<String>();
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<void> _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<String> _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<void> restartListening() async {
await stopListening();
await Future.delayed(const Duration(milliseconds: 500));
await startListening();
}
Future<void> 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<String, dynamic> 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<void> _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();
}
}
}