import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import '../asr_service.dart'; /// 火山语音识别服务实现 class VolcanoAsrService extends GetxService implements AsrService { /// 方法通道 static const MethodChannel _channel = MethodChannel('volcano_speech/asr'); /// 事件通道 static const EventChannel _eventChannel = EventChannel('volcano_speech/asr_events'); /// 事件流控制器 final StreamController _eventStreamController = StreamController.broadcast(); /// 事件订阅 StreamSubscription? _eventSubscription; /// 是否初始化完成 bool _isInitialized = false; /// 是否正在连续识别 bool _isContinuousRecognitionActive = false; /// 当前语言 String _currentLanguage = 'zh-CN'; /// 支持的语言列表 final List _supportedLanguages = ['zh-CN', 'en-US']; @override List get supportedLanguages => _supportedLanguages; /// 构造函数 VolcanoAsrService() { _setupEventListener(); } /// 初始化语音识别服务 @override Future initialize({ List? supportedLanguages, }) async { if (_isInitialized) return true; try { // 如果提供了支持的语言列表,则替换默认列表 if (supportedLanguages != null && supportedLanguages.isNotEmpty) { _supportedLanguages.clear(); _supportedLanguages.addAll(supportedLanguages); } // 从.env文件读取配置 final appId = dotenv.env['VOLCANO_SPEECH_APP_ID'] ?? ''; final appToken = dotenv.env['VOLCANO_SPEECH_APP_TOKEN'] ?? ''; final resourceId = dotenv.env['VOLCANO_ASR_RESOURCE_ID'] ?? 'volc.bigasr.sauc.duration'; if (appId.isEmpty || appToken.isEmpty) { debugPrint( '火山语音初始化失败:缺少VOLCANO_SPEECH_APP_ID或VOLCANO_SPEECH_APP_TOKEN环境变量'); return false; } // 初始化火山语音ASR final initResult = await _channel.invokeMethod('initialize', { 'appId': appId, 'appToken': appToken, 'resourceId': resourceId, }) ?? false; if (!initResult) { debugPrint('火山语音ASR初始化失败'); return false; } // 设置默认语言、VAD参数和启用音量返回 await _configureAsr(); _isInitialized = true; return true; } catch (e) { debugPrint('火山语音ASR初始化异常:$e'); return false; } } /// 配置ASR参数 Future _configureAsr() async { // 设置默认语言 await _channel.invokeMethod('setLanguage', { 'language': _currentLanguage, }); } /// 设置事件监听 void _setupEventListener() { _eventSubscription = _eventChannel.receiveBroadcastStream().listen( (dynamic event) { if (event is! Map) return; _handleAsrEvent(Map.from(event as Map)); }, onError: (Object error) { debugPrint('ASR事件流错误: $error'); _eventStreamController.add(RecognitionEvent( type: RecognitionEventType.error, error: '事件流错误: $error', )); }, ); } /// 处理ASR事件 void _handleAsrEvent(Map event) { final eventType = event['type'] as String?; switch (eventType) { case 'sessionStarted': _eventStreamController.add(RecognitionEvent( type: RecognitionEventType.sessionStarted, )); break; case 'sessionStopped': _eventStreamController.add(RecognitionEvent( type: RecognitionEventType.sessionStopped, )); _isContinuousRecognitionActive = false; break; case 'recognizing': final text = event['text'] as String?; final language = event['language'] as String?; if (text != null) { debugPrint('接收到中间识别结果: $text'); _eventStreamController.add(RecognitionEvent( type: RecognitionEventType.intermediateResult, text: text, detectedLanguage: language ?? _currentLanguage, )); } break; case 'result': final text = event['text'] as String?; final language = event['language'] as String?; if (text != null) { debugPrint('接收到最终识别结果: $text'); _eventStreamController.add(RecognitionEvent( type: RecognitionEventType.finalResult, text: text, detectedLanguage: language ?? _currentLanguage, )); } break; case 'error': final errorMessage = event['message'] as String?; debugPrint('接收到识别错误: ${errorMessage ?? "未知错误"}'); _eventStreamController.add(RecognitionEvent( type: RecognitionEventType.error, error: errorMessage ?? '未知错误', )); _isContinuousRecognitionActive = false; break; case 'volumeChanged': // 音量变化事件仅在日志中输出,不发送到事件流 final volume = event['volume'] as int?; if (volume != null && volume > 5) { // 仅记录有意义的音量变化 debugPrint('音量变化: $volume'); } break; } } /// 检查初始化状态 Future _ensureInitialized() async { if (!_isInitialized) { return await initialize(); } return true; } /// 执行一次性语音识别 @override Future recognizeOnce() async { if (!await _ensureInitialized()) { return RecognitionEvent( type: RecognitionEventType.error, error: '语音识别服务未初始化', ); } try { // 创建一个Completer用于等待最终结果 final completer = Completer(); // 创建一次性事件监听 StreamSubscription? subscription; subscription = _eventStreamController.stream.listen((event) { // 仅处理finalResult和error事件 if (event.type == RecognitionEventType.finalResult) { // 识别成功,返回结果 completer.complete(event); subscription?.cancel(); } else if (event.type == RecognitionEventType.error) { // 识别失败,返回错误 completer.complete(event); subscription?.cancel(); } }); // 启动一次性识别 final started = await _channel.invokeMethod('recognizeOnce') ?? false; if (!started) { subscription.cancel(); return RecognitionEvent( type: RecognitionEventType.error, error: '启动一次性识别失败', ); } // 设置超时,防止永久等待 Future.delayed(const Duration(seconds: 30), () { if (!completer.isCompleted) { completer.complete(RecognitionEvent( type: RecognitionEventType.error, error: '识别超时', )); subscription?.cancel(); // 停止识别 _channel.invokeMethod('stopRecognize'); } }); // 等待结果 return await completer.future; } catch (e) { // 确保停止识别 try { await _channel.invokeMethod('stopRecognize'); } catch (_) {} return RecognitionEvent( type: RecognitionEventType.error, error: e.toString(), ); } } /// 开始连续语音识别 @override // Future> startContinuousRecognition( // bool audioSourceType) async { // if (!await _ensureInitialized()) { // _eventStreamController.add(RecognitionEvent( // type: RecognitionEventType.error, // error: '语音识别服务未初始化', // )); // return _eventStreamController.stream; // } // try { // final success = await _channel.invokeMethod('audioSourceType', { // 'audioSourceType': audioSourceType, // }) ?? // false; // if (success) { // _isContinuousRecognitionActive = true; // } else { // _eventStreamController.add(RecognitionEvent( // type: RecognitionEventType.error, // error: '启动连续识别失败', // )); // } // } catch (e) { // _eventStreamController.add(RecognitionEvent( // type: RecognitionEventType.error, // error: '启动连续识别异常:$e', // )); // } // return _eventStreamController.stream; // } /// 停止连续语音识别 @override Future stopContinuousRecognition() async { if (!_isInitialized || !_isContinuousRecognitionActive) { return false; } try { final success = await _channel.invokeMethod('stopContinuousRecognition') ?? false; if (success) { _isContinuousRecognitionActive = false; } return success; } catch (e) { debugPrint('停止连续识别异常:$e'); return false; } } /// 检查连续识别是否活跃 @override bool isContinuousRecognitionActive() { return _isContinuousRecognitionActive; } /// 释放资源 @override Future dispose() async { if (_isContinuousRecognitionActive) { await stopContinuousRecognition(); } await _eventSubscription?.cancel(); _eventSubscription = null; if (!_eventStreamController.isClosed) { await _eventStreamController.close(); } if (_isInitialized) { await _channel.invokeMethod('release'); _isInitialized = false; } } @override Future enableRecord( AudioSourceType audioSourceType, String filePath, bool acceptAudioData, ) { // TODO: implement enableRecord throw UnimplementedError(); } @override Future stopRecord(bool isSave) { // TODO: implement stopRecord throw UnimplementedError(); } @override Future pauseRecord() { // TODO: implement pauseRecord throw UnimplementedError(); } @override Future moveFile(String sourcePath, String destPath) { // TODO: implement moveFile throw UnimplementedError(); } @override Future renameFile(String filePath, String newName) { // TODO: implement renameFile throw UnimplementedError(); } @override Future disableBluetoothAudio() { // TODO: implement disableBluetoothAudio throw UnimplementedError(); } @override Future restoreOriginalAudioState() { // TODO: implement restoreOriginalAudioState throw UnimplementedError(); } @override Future> recognizeCallback() { // TODO: implement recognizeCallback throw UnimplementedError(); } @override Future startContinuousRecognition( bool audioSourceType, { bool isRemoveFirstPunctuation = true, String mode = "normal", bool skipAsrForCallMode = false, }) { // TODO: implement startContinuousRecognition throw UnimplementedError(); } @override Future setAudioConfig({int sampleRate = 16000, int channels = 1}) { // TODO: implement setAudioConfig throw UnimplementedError(); } @override Future resumeRecord() { // TODO: implement resumeRecord throw UnimplementedError(); } @override Stream? getAudioDataStream() { // TODO: implement getAudioDataStream throw UnimplementedError(); } }