import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:realtime/realtime.dart'; import '../../../core/utils/logger.dart'; class RealtimeController extends GetxController with GetTickerProviderStateMixin { static const int _sampleRate = 16000; // Realtime服务实例 final RealtimeService _realtimeService = RealtimeService(); // 音频振幅值(RMS) final RxDouble audioAmplitude = 0.0.obs; // 连接状态 final RxBool isConnected = false.obs; final RxBool isConnecting = false.obs; // 语音状态 final RxBool isListening = false.obs; final RxBool isSpeaking = false.obs; // UI状态 final RxString currentStatus = ''.obs; // 移除默认文字 final RxString userSpeechText = ''.obs; final RxBool isUserSpeechFinal = true.obs; final RxList aiResponseMessages = [].obs; final RxBool isSubtitleMode = true.obs; // 字幕显示用的最新消息 final RxString latestUserMessage = ''.obs; final RxString latestAiMessage = ''.obs; // 实时字幕更新用的临时消息 final RxString partialUserMessage = ''.obs; final RxString streamingAiMessage = ''.obs; // 状态监听 StreamSubscription? _eventSubscription; @override void onInit() { super.onInit(); _setupEventListener(); // 自动开始连接 WidgetsBinding.instance.addPostFrameCallback((_) { _autoConnect(); }); } @override void onClose() { _cleanup(); super.onClose(); } /// 设置事件监听器 void _setupEventListener() { _eventSubscription = _realtimeService.eventStream.listen( (event) { _handleRealtimeEvent(event); }, onError: (error) { Logger.error('Realtime事件流错误: $error'); }, ); } /// 处理Realtime事件 void _handleRealtimeEvent(RealtimeEvent event) { switch (event.type) { case 'connectionStatusChanged': _handleConnectionStatusChanged(event.data); break; case 'voiceStatusChanged': _handleVoiceStatusChanged(event.data); break; case 'textReceived': _handleTextReceived(event.data); break; case 'audioRms': _handleAudioRms(event.data); break; case 'error': _handleError(event.data); break; } } /// 自动连接 Future _autoConnect() async { currentStatus.value = ''; // 不显示任何文字 await connect(); } /// 处理连接状态变化 void _handleConnectionStatusChanged(String status) { switch (status) { case 'connected': isConnected.value = true; isConnecting.value = false; currentStatus.value = ''; // 不显示任何文字 // 连接成功后自动开始录音 Timer(const Duration(milliseconds: 500), () { startListening(); }); break; case 'connecting': isConnecting.value = true; currentStatus.value = ''; // 不显示任何文字 break; case 'disconnected': isConnected.value = false; isConnecting.value = false; currentStatus.value = ''; // 不显示任何文字 _resetState(); break; case 'error': isConnected.value = false; isConnecting.value = false; currentStatus.value = '连接失败,请检查网络'; // 保留错误提示 _resetState(); Logger.error('Vocode服务器连接错误'); break; } } /// 处理语音状态变化 void _handleVoiceStatusChanged(String status) { switch (status) { case 'recording': isListening.value = true; isSpeaking.value = false; currentStatus.value = ''; // 不显示任何文字 break; case 'processing': isListening.value = false; currentStatus.value = ''; // 不显示任何文字 break; case 'playing': isListening.value = false; isSpeaking.value = true; currentStatus.value = ''; // 不显示任何文字 break; case 'idle': isListening.value = false; isSpeaking.value = false; currentStatus.value = ''; // 不显示任何文字 // 空闲时音频振幅归零 audioAmplitude.value = 0.0; break; } } /// 处理收到的文本消息 - 标准Vocode WebSocket协议 void _handleTextReceived(dynamic payload) { if (payload is String) { try { // 解析JSON消息 final Map json = jsonDecode(payload); final String messageType = json['type'] ?? ''; switch (messageType) { case 'websocket_transcript': // 处理统一的文本消息格式 final String text = json['text'] ?? ''; final String sender = json['sender'] ?? ''; final bool isFinal = json['is_final'] ?? false; // final dynamic timestamp = json['timestamp']; // 保留供未来使用 if (sender.toLowerCase() == 'human') { // 用户语音识别消息 if (isFinal) { // ASR最终结果 if (text.isNotEmpty) { latestUserMessage.value = text; userSpeechText.value = text; isUserSpeechFinal.value = true; } // 清空部分消息 partialUserMessage.value = ''; } else { // ASR实时识别 if (text.isNotEmpty) { partialUserMessage.value = text; isUserSpeechFinal.value = false; } } } else if (sender.toLowerCase() == 'bot') { // AI回复消息 if (isFinal) { // LLM回复完成标识 (text为空) if (streamingAiMessage.value.isNotEmpty) { // 将流式消息确认为最终消息 latestAiMessage.value = streamingAiMessage.value; if (aiResponseMessages.isNotEmpty) { aiResponseMessages[aiResponseMessages.length - 1] = streamingAiMessage.value; } else { aiResponseMessages.add(streamingAiMessage.value); } streamingAiMessage.value = ''; } } else { // LLM流式回复 if (text.isNotEmpty) { streamingAiMessage.value += text; } } } break; case 'websocket_ready': // 连接就绪 break; case 'websocket_stop': // 会话结束 break; case 'websocket_start': // 会话开始 break; default: Logger.error('收到未知消息类型: "$messageType"'); } } catch (e) { Logger.error('解析WebSocket消息失败: $e'); } } else { Logger.error('收到非字符串类型的payload: ${payload.runtimeType}'); } } /// 处理音频RMS值 void _handleAudioRms(dynamic rms) { if (rms is double || rms is num) { // 平滑处理RMS值,避免抖动太大,增大幅度 final double rawValue = rms.toDouble().clamp(0.0, 1.0); // 增强幅度:平方根函数减少小值,对数函数增强大值 final double enhancedValue = (rawValue * 3.0).clamp(0.0, 1.0); // 更强的平滑处理,减少抖动 audioAmplitude.value = audioAmplitude.value * 0.85 + enhancedValue * 0.15; } } /// 处理错误 void _handleError(String error) { Logger.error('Realtime错误: $error'); } /// 切换字幕模式 void toggleSubtitleMode() { isSubtitleMode.value = !isSubtitleMode.value; } /// 清空聊天记录 void clearChat() { userSpeechText.value = ''; isUserSpeechFinal.value = true; aiResponseMessages.clear(); latestUserMessage.value = ''; latestAiMessage.value = ''; partialUserMessage.value = ''; streamingAiMessage.value = ''; HapticFeedback.lightImpact(); } /// 连接到AI服务 Future connect() async { if (isConnecting.value || isConnected.value) return; try { HapticFeedback.lightImpact(); // 初始化Realtime服务 bool initialized = await _realtimeService.initialize( // serverUrl: 'ws://mcp.ideapsound.com:8000/ws', serverUrl: 'ws://192.168.1.11:8000/ws', sampleRate: _sampleRate, channels: 1, bitsPerSample: 16, ); if (!initialized) { throw Exception('初始化Realtime服务失败'); } // 连接服务器 final connected = await _realtimeService.connect(); if (!connected) { throw Exception('连接Vocode服务器失败'); } } catch (e) { Logger.error('连接失败: $e'); isConnecting.value = false; _resetState(); } } /// 断开连接 Future disconnect() async { try { HapticFeedback.mediumImpact(); if (isListening.value) { await _realtimeService.stopRecording(); } await _realtimeService.stopPlaying(); await _realtimeService.disconnect(); } catch (e) { Logger.error('断开连接失败: $e'); } } /// 开始聆听 void startListening() { if (!isConnected.value || isListening.value) return; HapticFeedback.lightImpact(); _realtimeService.startRecording().then((success) { if (!success) { Logger.error('开始录音失败'); } }); } /// 停止聆听 void stopListening() { if (!isListening.value) return; _realtimeService.stopRecording().then((success) { if (!success) { Logger.error('停止录音失败'); } }); } /// 切换聆听状态 void toggleListening() { if (!isConnected.value) return; if (isListening.value) { stopListening(); } else { startListening(); } } /// 关闭并返回上一页 Future closeAndGoBack() async { Get.back(); // 异步清理资源 Future.delayed(Duration.zero, () async { try { if (isListening.value) { await _realtimeService.stopRecording(); } await _realtimeService.stopPlaying(); await _realtimeService.disconnect(); await _realtimeService.dispose(); _resetState(); } catch (e) { Logger.error('关闭连接失败: $e'); } }); } /// 重置状态 void _resetState() { isConnected.value = false; isConnecting.value = false; isListening.value = false; isSpeaking.value = false; userSpeechText.value = ''; isUserSpeechFinal.value = true; aiResponseMessages.clear(); latestUserMessage.value = ''; latestAiMessage.value = ''; partialUserMessage.value = ''; streamingAiMessage.value = ''; audioAmplitude.value = 0.0; } /// 清理资源 void _cleanup() async { _eventSubscription?.cancel(); try { await _realtimeService.dispose(); } catch (e) { Logger.error('清理Realtime服务失败: $e'); } } }