import 'dart:async'; import 'dart:math' as math; import 'package:flutter/services.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:get/get.dart'; import '../../core/utils/logger.dart'; /// 识别事件类型 enum RecognitionEventType { started, recognizing, finalResult, error, completed, } /// 识别事件 class RecognitionEvent { final RecognitionEventType type; final String text; final String? error; RecognitionEvent({ required this.type, this.text = '', this.error, }); } /// 火山语音识别服务 /// /// 该服务提供了通过平台通道与 Android 上的火山语音识别 SDK 交互的接口 class VolcanoVoiceRecognitionService extends GetxService { // 平台通道 static const MethodChannel _channel = MethodChannel('com.example.deep_voice/volcano_asr'); static const EventChannel _eventChannel = EventChannel('com.example.deep_voice/speech_recognition_events'); // 配置参数 late final String _appId; late final String _token; late final String _cluster; // 集群区域 // 状态变量 final _isListening = false.obs; final _isInitialized = false.obs; final _errorMessage = ''.obs; final _recognitionResults = [].obs; // 是否使用标准语音识别SDK final bool _useStandardASR = true; // 使用标准语音识别SDK (API v2) // 添加识别结果流控制器 final _recognitionStreamController = StreamController.broadcast(); // 事件订阅 StreamSubscription? _eventSubscription; // 最新的识别结果 final _latestRecognizedText = ''.obs; String get latestRecognizedText => _latestRecognizedText.value; /// 检查连续识别是否处于活动状态 bool isContinuousRecognitionActive() { return _isListening.value; } VolcanoVoiceRecognitionService() { // 从环境变量获取配置 _appId = dotenv.env['VOLCANO_ASR_APP_ID'] ?? ''; _token = dotenv.env['VOLCANO_ASR_APP_KEY'] ?? ''; // 从环境变量获取集群区域 _cluster = dotenv.env['VOLCANO_ASR_CLUSTER'] ?? ''; Logger.info('火山语音识别配置: APP_ID=${_appId.isNotEmpty ? "已设置" : "未设置"}, APP_KEY=${_token.isNotEmpty ? "已设置" : "未设置"}'); Logger.info('使用标准语音识别SDK: $_useStandardASR'); Logger.info('集群区域: $_cluster'); if (_appId.isEmpty || _token.isEmpty) { _errorMessage.value = '火山语音识别配置不完整,请检查环境变量'; Logger.error(_errorMessage.value); } } // 获取可观察状态 RxBool get isListening => _isListening; bool get isInitialized => _isInitialized.value; String get errorMessage => _errorMessage.value; List get recognitionResults => _recognitionResults; // 添加识别结果流getter Stream get recognitionStream => _recognitionStreamController.stream; @override void onInit() { super.onInit(); _setupMethodCallHandler(); } /// 设置方法通道处理器 Future _setupMethodCallHandler() async { _channel.setMethodCallHandler((call) async { switch (call.method) { case 'onRecognitionResult': final String result = call.arguments as String; _onRecognitionResult(result); break; case 'onRecognitionError': final String error = call.arguments as String; _errorMessage.value = error; Logger.error('识别错误: $error'); _recognitionStreamController.add(RecognitionEvent( type: RecognitionEventType.error, error: error, )); break; case 'onListeningStateChanged': final bool isListening = call.arguments as bool; _isListening.value = isListening; Logger.info('监听状态变化: $isListening'); break; } }); } /// 处理识别结果 void _onRecognitionResult(String result) { if (result.isNotEmpty) { _recognitionResults.add(result); // 将结果发送到流 _recognitionStreamController.add(RecognitionEvent( type: RecognitionEventType.finalResult, text: result, )); Logger.info('识别结果: $result'); } } /// 初始化语音识别引擎 Future initialize() async { if (_isInitialized.value) return true; if (_appId.isEmpty || _token.isEmpty) { _errorMessage.value = '火山语音识别配置不完整,请检查环境变量'; Logger.error(_errorMessage.value); return false; } try { _errorMessage.value = ''; Logger.info('开始初始化火山语音识别服务,APP_ID: ${_appId.substring(0, math.min(3, _appId.length))}***,APP_KEY: ${_token.length > 10 ? "${_token.substring(0, 5)}..." : _token}'); Logger.info('使用标准语音识别SDK配置 (API v2)'); Logger.info('集群区域: $_cluster'); final Map params = { 'appId': _appId, 'token': _token, 'cluster': _cluster, }; final bool result = await _channel.invokeMethod('initialize', params); if (result) { _isInitialized.value = true; Logger.info('火山语音识别服务初始化成功'); } else { _errorMessage.value = '初始化失败,请检查配置'; Logger.error('火山语音识别服务初始化失败'); Logger.info('请检查以下可能的问题:'); Logger.info('1. 确保您的APP_ID和APP_KEY正确'); Logger.info('2. 确保您的应用ID和密钥正确且有效'); Logger.info('3. 确保您的应用已被授权使用语音识别服务'); } return result; } catch (e) { _errorMessage.value = '初始化异常: $e'; Logger.error('初始化火山语音识别服务异常: $e'); Logger.info('请检查以下可能的问题:'); Logger.info('1. 确保您的网络连接正常'); Logger.info('2. 确保集群区域设置正确 (当前: $_cluster)'); Logger.info('3. 确保请求参数格式正确'); Logger.info('4. 确保Token格式正确,需要添加Bearer;前缀'); _isInitialized.value = false; return false; } } /// 开始一次性识别 Future startOneTimeRecognition() async { if (_isListening.value) { Logger.warning('已经在进行语音识别,请先停止当前识别'); return false; } if (!_isInitialized.value) { try { final bool initialized = await initialize(); if (!initialized) { Logger.error('语音识别服务未初始化,无法开始识别'); _errorMessage.value = '语音识别服务未初始化,无法开始识别'; return false; } } catch (e) { Logger.error('初始化语音识别服务失败: $e'); _errorMessage.value = '初始化语音识别服务失败: $e'; return false; } } try { _errorMessage.value = ''; _recognitionResults.clear(); final bool result = await _channel.invokeMethod('startOneTimeRecognition'); _isListening.value = result; if (result) { _recognitionStreamController.add(RecognitionEvent( type: RecognitionEventType.started, text: '', )); } return result; } catch (e) { Logger.error('开始一次性识别失败: $e'); _errorMessage.value = e.toString(); return false; } } /// 开始连续识别 Future startContinuousRecognition() async { if (_isListening.value) { Logger.warning('已经在进行语音识别,请先停止当前识别'); return false; } if (!_isInitialized.value) { try { final bool initialized = await initialize(); if (!initialized) { Logger.error('语音识别服务未初始化,无法开始识别'); _errorMessage.value = '语音识别服务未初始化,无法开始识别'; return false; } } catch (e) { Logger.error('初始化语音识别服务失败: $e'); _errorMessage.value = '初始化语音识别服务失败: $e'; return false; } } try { _errorMessage.value = ''; _recognitionResults.clear(); // 设置事件监听 _eventSubscription = _eventChannel .receiveBroadcastStream() .listen(_handleNativeEvent, onError: (error) { _errorMessage.value = error.toString(); _isListening.value = false; _recognitionStreamController.add(RecognitionEvent( type: RecognitionEventType.error, error: error.toString(), )); }); final bool result = await _channel.invokeMethod('startContinuousRecognition'); _isListening.value = result; if (result) { _recognitionStreamController.add(RecognitionEvent( type: RecognitionEventType.started, text: '', )); } return result; } catch (e) { Logger.error('开始连续识别失败: $e'); _errorMessage.value = e.toString(); _cleanupEventStream(); return false; } } /// 处理来自原生端的事件 void _handleNativeEvent(dynamic event) { if (event is! Map) return; final Map eventMap = event; final String eventType = eventMap['eventType'] as String? ?? ''; switch (eventType) { case 'recognizing': final String text = eventMap['text'] as String? ?? ''; _recognitionStreamController.add(RecognitionEvent( type: RecognitionEventType.recognizing, text: text, )); break; case 'finalResult': final String text = eventMap['text'] as String? ?? ''; _latestRecognizedText.value = text; _recognitionResults.add(text); _recognitionStreamController.add(RecognitionEvent( type: RecognitionEventType.finalResult, text: text, )); break; case 'error': final String error = eventMap['error'] as String? ?? '未知错误'; _errorMessage.value = error; _isListening.value = false; _recognitionStreamController.add(RecognitionEvent( type: RecognitionEventType.error, error: error, )); break; } } /// 清理事件流 void _cleanupEventStream() { _eventSubscription?.cancel(); _eventSubscription = null; } /// 停止识别 Future stopRecognition() async { if (!_isListening.value) { Logger.warning('当前没有进行语音识别'); return false; } try { final bool result = await _channel.invokeMethod('stopRecognition'); _isListening.value = !result; if (result) { _cleanupEventStream(); } return result; } catch (e) { Logger.error('停止识别失败: $e'); _errorMessage.value = e.toString(); return false; } } /// 清理资源 Future dispose() async { try { if (_isListening.value) { await stopRecognition(); } _cleanupEventStream(); } catch (e) { Logger.error('清理语音识别资源失败: $e'); } } @override void onClose() { _eventSubscription?.cancel(); _recognitionStreamController.close(); super.onClose(); } }