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.

332 lines
10 KiB

import 'dart:async';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import '../../core/utils/logger.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import '../../modules/chat/models/message_model.dart';
import 'chat_history_service.dart';
import '../../data/services/azure_asr_service.dart';
import '../../data/services/azure_tts_service.dart';
/// 语音交互服务
///
/// 该服务提供了与Android端的VoiceInteractionService.kt通信的接口,
/// 用于管理后台语音交互服务的生命周期和接收语音交互事件
class VoiceInteractionService extends GetxService {
static VoiceInteractionService get to => Get.find();
// 方法通道和事件通道
static const MethodChannel _channel = MethodChannel('com.deep_voice.voice_interaction');
static const EventChannel _eventChannel = EventChannel('com.deep_voice.voice_interaction_events');
// 服务状态
final _isServiceRunning = false.obs;
bool get isServiceRunning => _isServiceRunning.value;
// 事件流控制器
StreamController<VoiceInteractionEvent>? _eventStreamController;
Stream<VoiceInteractionEvent>? _eventStream;
Stream<VoiceInteractionEvent>? get eventStream => _eventStream;
// 事件通道状态
bool _isEventChannelReady = false;
Completer<void>? _eventChannelReadyCompleter;
StreamSubscription? _eventSubscription;
// 配置信息
late String _azureSpeechKey;
late String _azureSpeechRegion;
late String _volcanoAiApiKey;
// 初始化状态标志
static bool _isInitialized = false;
/// 构造函数
VoiceInteractionService() {
}
/// 从环境变量加载配置
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.warning('未找到 Azure 语音服务配置。请在 .env 文件中设置 AZURE_SPEECH_KEY 和 AZURE_SPEECH_REGION');
}
if (_volcanoAiApiKey.isEmpty) {
Logger.warning('未找到火山 AI API 密钥。请在 .env 文件中设置 VOLCANO_AI_API_KEY');
}
}
/// 设置事件通道
void _setupEventChannel() {
_eventChannelReadyCompleter = Completer<void>();
_eventSubscription = _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;
}
_handleVoiceInteractionEvent(event);
}
}, onError: (error) {
Logger.error('语音交互事件通道错误: $error');
});
}
/// 创建事件流
void _createEventStream() {
_eventStreamController = StreamController<VoiceInteractionEvent>.broadcast();
_eventStream = _eventStreamController?.stream;
}
/// 等待事件通道准备好
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> initialize() async {
try {
if (_isInitialized) {
return this;
}
_isInitialized = true;
_loadConfig();
_setupEventChannel();
_createEventStream();
// 检查服务是否正在运行
await _checkServiceStatus();
// 等待事件通道准备好
await _waitForEventChannel();
Logger.info('语音交互服务初始化完成');
return this;
} catch (e) {
Logger.error('语音交互服务初始化失败: $e');
return this;
}
}
/// 检查服务状态
Future<void> _checkServiceStatus() async {
try {
final bool isRunning = await _channel.invokeMethod('isVoiceInteractionServiceRunning') ?? false;
_isServiceRunning.value = isRunning;
Logger.info('语音交互服务状态: ${isRunning ? "运行中" : "未运行"}');
} catch (e) {
Logger.error('检查语音交互服务状态失败: $e');
_isServiceRunning.value = false;
}
}
/// 启动语音交互服务
Future<bool> startService() async {
if (_isServiceRunning.value) {
Logger.info('语音交互服务已经在运行');
return true;
}
try {
final bool result = await _channel.invokeMethod('startVoiceInteractionService', {
'azure_speech_key': _azureSpeechKey,
'azure_speech_region': _azureSpeechRegion,
'volcano_ai_api_key': _volcanoAiApiKey,
}) ?? false;
if (result) {
_isServiceRunning.value = true;
Logger.info('语音交互服务启动成功');
} else {
Logger.error('语音交互服务启动失败');
}
return result;
} catch (e) {
Logger.error('启动语音交互服务失败: $e');
return false;
}
}
/// 停止语音交互服务
Future<bool> stopService() async {
if (!_isServiceRunning.value) {
Logger.info('语音交互服务未运行');
return true;
}
try {
final bool result = await _channel.invokeMethod('stopVoiceInteractionService') ?? false;
if (result) {
_isServiceRunning.value = false;
Logger.info('语音交互服务停止成功');
} else {
Logger.error('语音交互服务停止失败');
}
return result;
} catch (e) {
Logger.error('停止语音交互服务失败: $e');
return false;
}
}
/// 暂停语音交互(停止TTS和语音识别,但保持服务运行)
Future<bool> pauseVoiceInteraction() async {
if (!_isServiceRunning.value) {
Logger.info('语音交互服务未运行,无法暂停');
return false;
}
try {
final bool result = await _channel.invokeMethod('pauseVoiceInteraction') ?? false;
if (result) {
Logger.info('语音交互暂停成功');
} else {
Logger.error('语音交互暂停失败');
}
return result;
} catch (e) {
Logger.error('暂停语音交互失败: $e');
return false;
}
}
/// 处理来自原生端的语音交互事件
void _handleVoiceInteractionEvent(dynamic event) {
if (event is! Map || _eventStreamController == null) return;
final Map<dynamic, dynamic> eventMap = event;
final String eventType = eventMap['type'] as String? ?? '';
final int timestamp = eventMap['timestamp'] as int? ?? 0;
// 添加时间戳日志,帮助调试
Logger.info('收到原生端事件: $eventType, 时间戳: $timestamp, 当前时间: ${DateTime.now().millisecondsSinceEpoch}');
switch (eventType) {
case 'chatHistory':
final String agentId = eventMap['agentId'] as String? ?? '';
final String userMessage = eventMap['userMessage'] as String? ?? '';
final String assistantMessage = eventMap['assistantMessage'] as String? ?? '';
_eventStreamController?.add(ChatHistoryEvent(
agentId: agentId,
userMessage: userMessage,
assistantMessage: assistantMessage,
timestamp: timestamp,
));
// 保存聊天记录
_saveChatHistory(agentId, userMessage, assistantMessage, timestamp);
Logger.info('收到聊天历史事件: agentId=$agentId');
break;
case 'recognitionStarted':
_eventStreamController?.add(RecognitionStartedEvent(
timestamp: timestamp,
));
Logger.info('收到语音识别开始事件');
break;
default:
Logger.warning('收到未知类型的语音交互事件: $eventType');
break;
}
}
@override
void onClose() {
// 清理资源
_eventSubscription?.cancel();
_eventStreamController?.close();
super.onClose();
}
/// 保存聊天记录
void _saveChatHistory(String agentId, String userMessage, String assistantMessage, int timestamp) {
try {
// 获取ChatHistoryService实例
final chatHistoryService = Get.find<ChatHistoryService>();
// 创建用户消息和助手消息
final userMsg = Message(
role: 'user',
content: userMessage,
timestamp: DateTime.fromMillisecondsSinceEpoch(timestamp),
);
final assistantMsg = Message(
role: 'assistant',
content: assistantMessage,
timestamp: DateTime.fromMillisecondsSinceEpoch(timestamp + 1), // 确保助手消息时间戳晚于用户消息
);
// 加载现有历史记录
final existingMessages = chatHistoryService.loadHistory(agentId);
// 添加新消息
existingMessages.addAll([userMsg, assistantMsg]);
// 保存更新后的历史记录
chatHistoryService.saveHistory(agentId, existingMessages);
} catch (e) {
Logger.error('保存聊天记录失败: $e');
}
}
}
/// 语音交互事件基类
abstract class VoiceInteractionEvent {
final int timestamp;
VoiceInteractionEvent({required this.timestamp});
}
/// 聊天历史事件
class ChatHistoryEvent extends VoiceInteractionEvent {
final String agentId;
final String userMessage;
final String assistantMessage;
ChatHistoryEvent({
required this.agentId,
required this.userMessage,
required this.assistantMessage,
required int timestamp,
}) : super(timestamp: timestamp);
}
/// 语音识别开始事件
class RecognitionStartedEvent extends VoiceInteractionEvent {
RecognitionStartedEvent({
required int timestamp,
}) : super(timestamp: timestamp);
}