import 'dart:async'; import '../../../data/models/appconfig.dart'; import 'package:flutter/services.dart'; import '../../../core/utils/logger.dart'; import 'package:get/get.dart'; import '../ast_service.dart'; /// 音频源类型 enum AudioSourceType { microphone, // 使用设备麦克风 external // 使用外部提供的音频数据 } /// Azure 语音识别服务 /// /// 该服务提供了通过平台通道与原生 Microsoft Speech SDK 交互的接口 class AzureAstService extends GetxService implements AstService { static final AzureAstService to = Get.put(AzureAstService()); static const MethodChannel _channel = MethodChannel('azure_speech/ast'); static const EventChannel _eventChannel = EventChannel('azure_speech/ast_events'); // final GetStorage _storage = GetStorage(); bool _isInitialized = false; late final String _subscriptionKey; late final String _serviceRegion; late String _baseUrl; final String _endpoint = '/'; // 修改为根路径 late final String _accessKey; late final String _secretKey; late final String _region; late final String _service; final List _defaultSupportedLanguages = ['zh-CN', 'en-US']; @override List get supportedLanguages => _defaultSupportedLanguages; //连续识别相关 bool _isContinuousRecognitionActive = false; StreamController? _eventStreamController; StreamSubscription? _eventSubscription; // 最新的识别结果 String _latestRecognizedText = ''; String get latestRecognizedText => _latestRecognizedText; // 最新检测到的语言 String _latestDetectedLanguage = ''; String get latestDetectedLanguage => _latestDetectedLanguage; // 当前音频源类型 AudioSourceType _audioSourceType = AudioSourceType.microphone; AzureAstService() { _loadConfig(); } /// 设置事件通道 void _setupEventChannel() { _eventSubscription?.cancel(); _eventSubscription = _eventChannel.receiveBroadcastStream().listen((event) { if (event is Map) { _handleRecognitionEvent(event); } }, onError: _handleRecognitionError); } /// 处理来自原生端的识别事件 void _handleRecognitionEvent(dynamic event) { if (event is! Map || _eventStreamController == null) return; final Map eventMap = event; final String eventType = eventMap['type'] as String? ?? ''; // 添加日志帮助调试 switch (eventType) { case 'result': final String text = eventMap['text'] as String? ?? ''; final String detectedLanguage = eventMap['detectedLanguage'] as String? ?? ''; _latestRecognizedText = text; _latestDetectedLanguage = detectedLanguage; _eventStreamController?.add(RecognitionEvent1( type: RecognitionEventType1.finalResult, text: text, detectedLanguage: detectedLanguage, )); break; case 'recognizing': final String text = eventMap['text'] as String? ?? ''; final String detectedLanguage = eventMap['detectedLanguage'] as String? ?? ''; _eventStreamController?.add(RecognitionEvent1( type: RecognitionEventType1.intermediateResult, text: text, detectedLanguage: detectedLanguage, )); break; case 'sessionStarted': _eventStreamController?.add(RecognitionEvent1( type: RecognitionEventType1.sessionStarted, )); break; case 'sessionStopped': _isContinuousRecognitionActive = false; _eventStreamController?.add(RecognitionEvent1( type: RecognitionEventType1.sessionStopped, )); break; case 'canceled': _isContinuousRecognitionActive = false; final String reason = eventMap['reason'] as String? ?? ''; final String errorDetails = eventMap['errorDetails'] as String? ?? ''; if (reason.isNotEmpty || errorDetails.isNotEmpty) { Logger.error('识别取消: $reason - ${errorDetails.toString()}'); } _eventStreamController?.add(RecognitionEvent1( type: RecognitionEventType1.canceled, error: '$reason: $errorDetails', )); break; case 'error': final String error = eventMap['message'] as String? ?? ''; Logger.error('识别错误: ${error.toString()}'); _eventStreamController?.add(RecognitionEvent1( type: RecognitionEventType1.error, error: error, )); break; } } /// 处理识别事件流错误 void _handleRecognitionError(Object error) { Logger.error('识别事件流错误: ${error.toString()}'); _eventStreamController?.addError(error); _cleanupEventStream(); } /// 清理事件流资源 void _cleanupEventStream() { _eventStreamController?.close(); _eventStreamController = null; _isContinuousRecognitionActive = false; } /// 从环境变量加载配置 void _loadConfig() { // final _env = _storage.read("ENV") as Map; _subscriptionKey = AppConfig.env('AZURE_SPEECH_KEY') ?? ''; _serviceRegion = AppConfig.env('AZURE_SPEECH_REGION') ?? ''; _accessKey = AppConfig.env('VOLCANO_TRANSLATION_ACCESS_KEY') ?? ''; _secretKey = AppConfig.env('VOLCANO_TRANSLATION_SECRET_KEY') ?? ''; _region = AppConfig.env('VOLCANO_TRANSLATION_REGION') ?? 'cn-north-1'; _service = 'translate'; _baseUrl = 'https://translate.volcengineapi.com'; if (_subscriptionKey.isEmpty || _serviceRegion.isEmpty) { throw Exception( '未找到 Azure 语音服务配置。请在 .env 文件中设置 AZURE_SPEECH_KEY 和 AZURE_SPEECH_REGION'); } } @override Future> recognizeCallback() async { if (!_isInitialized) { await initialize(); } try { _eventStreamController = StreamController.broadcast(); // 开始连续识别 final bool result = await _channel.invokeMethod('recognizeCallback'); if (!result) { _cleanupEventStream(); } return _eventStreamController!.stream; } catch (e) { Logger.error('开始连续语音识别失败: ${e.toString()}'); rethrow; } } @override Future enableRecord(String filePath) async { try { final bool result = await _channel.invokeMethod('enableRecord', { 'filePath': filePath, }); return result; } catch (e) { Logger.error('开始录音: ${e.toString()}'); rethrow; } } @override Future path(String filePath) async { try { final bool result = await _channel.invokeMethod('path', { 'filePath': filePath, }); return result; } catch (e) { Logger.error('开始录音: ${e.toString()}'); rethrow; } } @override Future startContinuousTranslation() async { try { final bool result = await _channel.invokeMethod('startContinuousTranslation'); return result; } catch (e) { Logger.error('停止录音: ${e.toString()}'); rethrow; } } @override Future stopContinuousTranslation() async { try { final bool result = await _channel.invokeMethod('stopContinuousTranslation'); return result; } catch (e) { Logger.error('停止录音: ${e.toString()}'); rethrow; } } @override Future dispose() async { try { final bool result = await _channel.invokeMethod('dispose'); return; } catch (e) { Logger.error('停止录音: ${e.toString()}'); rethrow; } } @override Future initialize({ List? supportedLanguages, bool useExternalAudio = false, bool useEchoCancellation = false, }) async { try { final List languages = supportedLanguages ?? _defaultSupportedLanguages; //底层会初始化前释放 // // 检查是否需要重新初始化 // if (_isInitialized) { // await dispose(); // } // 设置音频源类型 _audioSourceType = useExternalAudio ? AudioSourceType.external : AudioSourceType.microphone; // Future initialize({ // required String subscriptionKey, // required String region, // required List supportedLanguages, // required String audioSourceType, // required String translationAccessKey, // required String translationSecretKey, // String translationRegion = 'cn-north-1', // }); final bool result = await _channel.invokeMethod('initialize', { 'subscriptionKey': _subscriptionKey, 'region': _serviceRegion, 'supportedLanguages': languages, 'audioSourceType': _audioSourceType.toString().split('.').last, 'translationAccessKey': _accessKey, 'translationSecretKey': _secretKey, 'translationRegion': _region, }); _isInitialized = result; _setupEventChannel(); Logger.info('Azure 语音识别服务初始化${result ? '成功' : '失败'}'); return result; } catch (e) { Logger.error('Azure 语音识别服务初始化失败: ${e.toString()}'); _isInitialized = false; rethrow; } } }