import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:get/get.dart'; import 'package:speech_to_text/speech_to_text.dart' as stt; import 'package:speech_to_text/speech_recognition_result.dart'; import 'package:speech_to_text/speech_recognition_error.dart'; import '../../../core/utils/logger.dart'; import '../asr_service.dart'; /// ASR状态枚举 enum AsrState { /// 未初始化 notInitialized, /// 已初始化但未开始监听 initialized, /// 正在监听 listening, /// 正在处理识别结果 processing, /// 识别完成 done, /// 发生错误 error, } /// Flutter ASR服务,负责本地语音转文本功能 /// 使用speech_to_text插件实现语音识别功能 class FlutterAsrService extends GetxService implements AsrService { static final FlutterAsrService to = Get.put(FlutterAsrService()); // speech_to_text 实例 final stt.SpeechToText _speech = stt.SpeechToText(); // 可用的语音识别语言 final List _supportedLanguages = []; @override List get supportedLanguages => _supportedLanguages; // 当前选择的语言 final RxString _currentLocale = ''.obs; String get currentLocale => _currentLocale.value; // 是否可用 final RxBool _isAvailable = false.obs; bool get isAvailable => _isAvailable.value; // 是否正在监听 bool _isListening = false; // 当前ASR状态 final Rx _asrState = AsrState.notInitialized.obs; AsrState get asrState => _asrState.value; // 连续识别模式的结果流控制器 StreamController? _continuousRecognitionController; FlutterAsrService() { _initSpeech(); } /// 初始化语音识别 Future _initSpeech() async { try { _asrState.value = AsrState.notInitialized; // 初始化语音识别,使用较短的最终超时时间,提高响应速度 _isAvailable.value = await _speech.initialize( onStatus: _onSpeechStatus, onError: _onSpeechError, debugLogging: false, finalTimeout: const Duration(milliseconds: 800), // 更短的最终超时时间,加速结果返回 ); if (_isAvailable.value) { // 获取支持的语言列表 final systemLocales = await _speech.locales(); // 清空并重新填充支持的语言列表 _supportedLanguages.clear(); for (var locale in systemLocales) { if (!_supportedLanguages.contains(locale.localeId)) { _supportedLanguages.add(locale.localeId); } } // 设置默认语言(优先使用系统语言或中文) var systemLocale = await _speech.systemLocale(); if (systemLocale != null) { _currentLocale.value = systemLocale.localeId; } else if (_supportedLanguages.contains('zh-CN')) { _currentLocale.value = 'zh-CN'; } else if (_supportedLanguages.isNotEmpty) { _currentLocale.value = _supportedLanguages.first; } _asrState.value = AsrState.initialized; Logger.info('语音识别初始化成功,支持 ${_supportedLanguages.length} 种语言'); } else { _asrState.value = AsrState.error; Logger.error('语音识别不可用'); } } catch (e) { _isAvailable.value = false; _asrState.value = AsrState.error; Logger.error('语音识别初始化失败: $e'); } } /// 处理语音识别状态变化 void _onSpeechStatus(String status) { Logger.info('语音识别状态: $status'); switch (status) { case 'listening': _asrState.value = AsrState.listening; _isListening = true; break; case 'notListening': if (_asrState.value == AsrState.listening) { _asrState.value = AsrState.processing; } _isListening = false; break; case 'done': _asrState.value = AsrState.done; _isListening = false; // 在连续识别模式下,当一次识别完成后立即开始下一次识别 if (_continuousRecognitionController != null && !_continuousRecognitionController!.isClosed) { _restartContinuousRecognition(); } break; } } /// 处理语音识别错误 void _onSpeechError(SpeechRecognitionError error) { Logger.error('语音识别错误: ${error.errorMsg} (永久性错误: ${error.permanent})'); _asrState.value = AsrState.error; // 向连续识别控制器发送错误事件(如果存在) _continuousRecognitionController ?.add(RecognitionEvent.error(error.errorMsg)); // 在连续识别模式下,尝试从错误中恢复(非永久性错误) if (!error.permanent && _continuousRecognitionController != null && !_continuousRecognitionController!.isClosed) { _restartContinuousRecognition(); } } /// 启动语音识别的统一方法 Future _startListening({ required Function(SpeechRecognitionResult) onResult, Duration listenFor = const Duration(seconds: 30), Duration pauseFor = const Duration(seconds: 3), bool autoPunctuation = true, }) async { try { // 如果当前正在监听,先停止 if (_speech.isListening) { await _speech.stop(); // 延长等待时间,确保资源完全释放 await Future.delayed(const Duration(milliseconds: 800)); } // 在启动前再次检查状态 if (_speech.isListening) { Logger.warning('停止识别后,仍处于监听状态,可能有系统冲突'); await _speech.stop(); await Future.delayed(const Duration(milliseconds: 500)); } // 启动语音识别前确保状态正确 _asrState.value = AsrState.listening; // 启动语音识别 final result = await _speech.listen( onResult: onResult, localeId: _currentLocale.value, listenFor: listenFor, pauseFor: pauseFor, listenOptions: stt.SpeechListenOptions( partialResults: true, autoPunctuation: autoPunctuation, // 使用独白模式,减少与系统交互冲突 listenMode: stt.ListenMode.dictation, ), ) ?? false; if (!result) { Logger.error('语音识别启动失败'); _asrState.value = AsrState.error; } return result; } catch (e) { Logger.error('启动语音识别失败: $e'); _asrState.value = AsrState.error; return false; } } /// 重新启动连续识别 Future _restartContinuousRecognition() async { try { // 确保控制器有效 if (_continuousRecognitionController == null || _continuousRecognitionController!.isClosed) { Logger.info('连续识别控制器已关闭,跳过重启'); return; } // 检查系统状态,避免频繁重启 if (_speech.isListening) { Logger.info('当前已在监听中,无需重启'); return; } // 延长等待时间,避免与系统资源冲突 await Future.delayed(const Duration(milliseconds: 1000)); // 再次检查状态 if (_continuousRecognitionController == null || _continuousRecognitionController!.isClosed) { Logger.info('延迟期间控制器已关闭,跳过重启'); return; } Logger.info('尝试重新启动连续识别'); // 使用统一方法启动识别 final success = await _startListening( onResult: _onSpeechResult, // 增加超时时间和暂停时间 listenFor: const Duration(seconds: 30), pauseFor: const Duration(seconds: 4), ); if (success) { Logger.info('连续识别重启成功'); } else { Logger.error('连续识别重启失败'); _continuousRecognitionController ?.add(RecognitionEvent.error('重启语音识别失败,可能存在系统资源冲突')); } } catch (e) { Logger.error('重启连续识别失败: $e'); // 发送错误给控制器 _continuousRecognitionController?.add(RecognitionEvent.error('重启失败: $e')); } } /// 处理语音识别结果 void _onSpeechResult(SpeechRecognitionResult result) { // 如果是连续识别,发送到连续识别控制器 if (_continuousRecognitionController != null) { if (result.finalResult) { Logger.info('收到最终识别结果: "${result.recognizedWords}"'); _continuousRecognitionController!.add(RecognitionEvent.finalResult( text: result.recognizedWords, detectedLanguage: currentLocale, )); } else { _continuousRecognitionController!.add(RecognitionEvent( type: RecognitionEventType.intermediateResult, text: result.recognizedWords, detectedLanguage: currentLocale, )); } } } /// 设置当前语言 void setLocale(String localeId) { if (_supportedLanguages.contains(localeId)) { _currentLocale.value = localeId; Logger.info('设置语音识别语言: $localeId'); } else { Logger.error('不支持的语言: $localeId'); } } @override Future initialize({ List? supportedLanguages, }) async { try { if (supportedLanguages != null && supportedLanguages.isNotEmpty) { _supportedLanguages.clear(); _supportedLanguages.addAll(supportedLanguages); } // 初始化语音识别 await _initSpeech(); return _isAvailable.value; } catch (e) { Logger.error('语音识别初始化失败: $e'); return false; } } @override Future recognizeOnce() async { try { if (!_isAvailable.value) { return RecognitionEvent.error('语音识别不可用'); } if (_isListening) { await stopContinuousRecognition(); } // 创建Completer来等待最终结果 final completer = Completer(); // 开始监听 _asrState.value = AsrState.listening; // 定义结果处理函数 void resultListener(SpeechRecognitionResult result) { if (result.finalResult) { // 只有在最终结果时才完成completer completer.complete(RecognitionEvent.finalResult( text: result.recognizedWords, detectedLanguage: currentLocale, )); } } // 使用统一方法启动识别,单次识别使用更短的时间参数 final success = await _startListening( onResult: resultListener, listenFor: const Duration(seconds: 15), pauseFor: const Duration(seconds: 2), ); if (!success) { return RecognitionEvent.error('启动语音识别失败'); } // 设置较短的超时时间 return await completer.future.timeout( const Duration(seconds: 20), // 略长于listenFor以确保获取到结果 onTimeout: () { _speech.stop(); return RecognitionEvent.error('语音识别超时'); }, ); } catch (e) { Logger.error('一次性语音识别失败: $e'); return RecognitionEvent.error('$e'); } } @override Future> startContinuousRecognition() async { try { if (!_isAvailable.value) { final controller = StreamController.broadcast(); controller.add(RecognitionEvent.error('语音识别不可用')); controller.close(); return controller.stream; } // 如果已经在监听,先停止 if (_isListening) { await stopContinuousRecognition(); } // 创建新的流控制器 _continuousRecognitionController = StreamController.broadcast(); // 开始会话 _continuousRecognitionController! .add(RecognitionEvent(type: RecognitionEventType.sessionStarted)); // 开始连续监听 _asrState.value = AsrState.listening; // 使用统一方法启动识别,连续识别使用较长的参数 final success = await _startListening( onResult: _onSpeechResult, ); if (!success) { // 添加错误事件,但保留流 _continuousRecognitionController! .add(RecognitionEvent.error('启动连续语音识别失败')); // 返回已有的流控制器 return _continuousRecognitionController!.stream; } _isListening = true; return _continuousRecognitionController!.stream; } catch (e) { Logger.error('启动连续语音识别失败: $e'); // 如果已经创建了控制器,使用它 if (_continuousRecognitionController != null) { _continuousRecognitionController!.add(RecognitionEvent.error('$e')); return _continuousRecognitionController!.stream; } else { // 否则创建新的控制器 final controller = StreamController.broadcast(); controller.add(RecognitionEvent.error('$e')); return controller.stream; } } } @override Future stopContinuousRecognition() async { try { // 无论是否正在监听,都尝试停止 if (_speech.isListening) { await _speech.stop(); } _isListening = false; // 发送会话结束事件 _continuousRecognitionController ?.add(RecognitionEvent(type: RecognitionEventType.sessionStopped)); // 关闭流控制器 await _continuousRecognitionController?.close(); _continuousRecognitionController = null; _asrState.value = AsrState.done; return true; } catch (e) { Logger.error('停止连续语音识别失败: $e'); // 强制重置状态 _isListening = false; _asrState.value = AsrState.error; return false; } } @override bool isContinuousRecognitionActive() { return _isListening; } @override Future dispose() async { try { // 确保停止所有识别活动 if (_speech.isListening) { await _speech.stop(); } // 关闭控制器 await _continuousRecognitionController?.close(); _continuousRecognitionController = null; _isListening = false; _asrState.value = AsrState.notInitialized; } catch (e) { Logger.error('释放语音识别资源失败: $e'); } } @override Future pushAudioData(Uint8List data) async {} }