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 _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 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 stopContinuousTranslation(bool isSave) async { try { final bool result = await _channel.invokeMethod('stopContinuousTranslation', { 'isSave': isSave, }); return result; } 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; Logger.info('Azure 语音识别服务初始化${result ? '成功' : '失败'}'); return result; } catch (e) { Logger.error('Azure 语音识别服务初始化失败: ${e.toString()}'); _isInitialized = false; rethrow; } } }