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.

312 lines
8.7 KiB

import 'dart:async';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import '../services/azure_asr_service.dart';
import '../services/azure_tts_service.dart';
import '../services/chat_history_service.dart';
import '../../modules/chat/models/message_model.dart';
import '../../core/utils/logger.dart';
/// 语音交互服务,用于管理原生的 VoiceInteractionService
class VoiceInteractionService extends GetxService {
static VoiceInteractionService get to => Get.find();
// 服务运行状态
final _isRunning = false.obs;
bool get isRunning => _isRunning.value;
// 语音识别状态
final _isRecognitionActive = false.obs;
bool get isRecognitionActive => _isRecognitionActive.value;
// 聊天记录 - 可被 ChatView 监听
final RxList<Message> personalAssistantMessages = <Message>[].obs;
// 方法通道
static const MethodChannel _channel = MethodChannel('com.deep_voice.voice_interaction');
// 事件通道
static const EventChannel _eventChannel = EventChannel('com.deep_voice.voice_interaction_events');
// 配置信息
late final String _azureSpeechKey;
late final String _azureSpeechRegion;
late final String _volcanoAiApiKey;
// 事件通道状态
bool _isEventChannelReady = false;
Completer<void>? _eventChannelReadyCompleter;
// 初始化标记,避免重复初始化
bool _isInitialized = false;
VoiceInteractionService() {
_loadConfig();
_setupEventChannel();
}
/// 从环境变量加载配置
void _loadConfig() {
_azureSpeechKey = dotenv.env['AZURE_SPEECH_KEY'] ?? '';
_azureSpeechRegion = dotenv.env['AZURE_SPEECH_REGION'] ?? '';
_volcanoAiApiKey = dotenv.env['VOLCANO_AI_API_KEY'] ?? '';
if (_azureSpeechKey.isEmpty || _azureSpeechRegion.isEmpty) {
Logger.error('未找到 Azure 语音服务配置。请在 .env 文件中设置 AZURE_SPEECH_KEY 和 AZURE_SPEECH_REGION');
}
if (_volcanoAiApiKey.isEmpty) {
Logger.error('未找到火山 AI 配置。请在 .env 文件中设置 VOLCANO_AI_API_KEY');
}
}
/// 设置事件通道监听
void _setupEventChannel() {
_eventChannelReadyCompleter = Completer<void>();
_eventChannel.receiveBroadcastStream().listen(
(event) {
if (event is Map) {
final String eventType = event['type'] as String? ?? '';
// 处理通道准备好的事件
if (eventType == 'channelReady') {
_isEventChannelReady = true;
if (!_eventChannelReadyCompleter!.isCompleted) {
_eventChannelReadyCompleter!.complete();
}
return;
}
_handleEvent(event);
}
},
onError: (error) {
Logger.error('语音交互事件通道错误: $error');
}
);
}
/// 等待事件通道准备好
Future<bool> _waitForEventChannel({Duration timeout = const Duration(seconds: 5)}) async {
if (_isEventChannelReady) return true;
try {
await _eventChannelReadyCompleter!.future.timeout(timeout);
return true;
} on TimeoutException {
Logger.error('等待事件通道准备好超时');
return false;
}
}
/// 初始化服务
Future<VoiceInteractionService> init() async {
// 如果已经初始化过,直接返回
if (_isInitialized) {
return this;
}
try {
// 等待事件通道准备好
final bool eventChannelReady = await _waitForEventChannel();
if (!eventChannelReady) {
Logger.error('事件通道未准备好,初始化失败');
return this;
}
// 获取服务运行状态
await _updateServiceStatus();
// 加载个人助手的历史记录
_loadPersonalAssistantHistory();
// 标记为已初始化
_isInitialized = true;
return this;
} catch (e) {
Logger.error('VoiceInteractionService 初始化失败: $e');
return this;
}
}
/// 加载个人助手的历史记录
void _loadPersonalAssistantHistory() {
if (Get.isRegistered<ChatHistoryService>()) {
final historyService = Get.find<ChatHistoryService>();
final messages = historyService.loadHistory('personal_assistant');
if (messages.isNotEmpty) {
personalAssistantMessages.assignAll(messages);
}
}
}
/// 处理来自原生端的事件
void _handleEvent(dynamic event) {
try {
if (event is! Map) {
return;
}
final type = event['type'] as String?;
if (type == null) {
return;
}
switch (type) {
case 'recognitionStarted':
_isRecognitionActive.value = true;
// 检测到后台服务的语音识别开始运行,主动停止Flutter端的语音识别和TTS
// _stopFlutterVoiceServices();
break;
case 'chatHistory':
// 处理聊天记录
_handleChatHistory(event);
break;
}
} catch (e) {
Logger.error('处理原生端事件时出错: $e');
}
}
/// 处理聊天记录事件
void _handleChatHistory(Map<dynamic, dynamic> event) {
try {
final agentId = event['agentId'] as String?;
final userMessage = event['userMessage'] as String?;
final assistantMessage = event['assistantMessage'] as String?;
final timestamp = event['timestamp'] as int?;
if (agentId == null || userMessage == null || assistantMessage == null || timestamp == null) {
return;
}
// 只处理个人助手的聊天记录
if (agentId != 'personal_assistant') {
return;
}
// 添加用户消息
personalAssistantMessages.add(Message(
role: 'user',
content: userMessage,
timestamp: DateTime.fromMillisecondsSinceEpoch(timestamp),
));
// 添加助手消息
personalAssistantMessages.add(Message(
role: 'assistant',
content: assistantMessage,
timestamp: DateTime.fromMillisecondsSinceEpoch(timestamp + 1000), // 助手消息时间戳稍后
));
// 保存到历史记录
if (Get.isRegistered<ChatHistoryService>()) {
final historyService = Get.find<ChatHistoryService>();
historyService.saveHistory('personal_assistant', personalAssistantMessages);
}
} catch (e) {
Logger.error('处理聊天记录事件失败: $e');
}
}
/// 停止 Flutter 端的语音服务
void _stopFlutterVoiceServices() {
// 停止 TTS
if (Get.isRegistered<AzureTtsService>()) {
Get.find<AzureTtsService>().stop();
}
// 停止 ASR
if (Get.isRegistered<AzureAsrService>()) {
Get.find<AzureAsrService>().stopContinuousRecognition();
}
}
/// 启动语音交互服务
Future<bool> startService() async {
if (!_isInitialized) {
await init();
}
try {
// 启动服务
final result = await _channel.invokeMethod<bool>('startVoiceInteractionService', {
'azure_speech_key': _azureSpeechKey,
'azure_speech_region': _azureSpeechRegion,
'volcano_ai_api_key': _volcanoAiApiKey,
}) ?? false;
// 更新状态
if (result) {
await _updateServiceStatus();
}
return result;
} catch (e) {
Logger.error('启动语音交互服务失败: $e');
return false;
}
}
/// 停止语音交互服务
Future<bool> stopService() async {
try {
final result = await _channel.invokeMethod<bool>('stopVoiceInteractionService') ?? false;
// 更新状态
if (result) {
await _updateServiceStatus();
}
return result;
} catch (e) {
Logger.error('停止语音交互服务失败: $e');
return false;
}
}
/// 暂停语音交互
Future<bool> pauseVoiceInteraction() async {
try {
final result = await _channel.invokeMethod<bool>('pauseVoiceInteraction') ?? false;
return result;
} catch (e) {
Logger.error('暂停语音交互失败: $e');
return false;
}
}
/// 更新服务运行状态
Future<void> _updateServiceStatus() async {
try {
final status = await _channel.invokeMethod<bool>('isVoiceInteractionServiceRunning') ?? false;
_isRunning.value = status;
} catch (e) {
Logger.error('获取语音交互服务状态失败: $e');
_isRunning.value = false;
}
}
/// 释放资源
Future<void> dispose() async {
try {
if (_isRunning.value) {
await stopService();
}
_isInitialized = false;
} catch (e) {
Logger.error('释放语音交互服务资源失败: $e');
}
}
@override
void onClose() {
// 清理资源
dispose();
super.onClose();
}
}