You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

421 lines
11 KiB

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<RecognitionEvent> _eventStreamController =
StreamController<RecognitionEvent>.broadcast();
/// 事件订阅
StreamSubscription? _eventSubscription;
/// 是否初始化完成
bool _isInitialized = false;
/// 是否正在连续识别
bool _isContinuousRecognitionActive = false;
/// 当前语言
String _currentLanguage = 'zh-CN';
/// 支持的语言列表
final List<String> _supportedLanguages = ['zh-CN', 'en-US'];
@override
List<String> get supportedLanguages => _supportedLanguages;
/// 构造函数
VolcanoAsrService() {
_setupEventListener();
}
/// 初始化语音识别服务
@override
Future<bool> initialize({
List<String>? 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<bool>('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<void> _configureAsr() async {
// 设置默认语言
await _channel.invokeMethod<bool>('setLanguage', {
'language': _currentLanguage,
});
}
/// 设置事件监听
void _setupEventListener() {
_eventSubscription = _eventChannel.receiveBroadcastStream().listen(
(dynamic event) {
if (event is! Map) return;
_handleAsrEvent(Map<String, dynamic>.from(event as Map));
},
onError: (Object error) {
debugPrint('ASR事件流错误: $error');
_eventStreamController.add(RecognitionEvent(
type: RecognitionEventType.error,
error: '事件流错误: $error',
));
},
);
}
/// 处理ASR事件
void _handleAsrEvent(Map<String, dynamic> 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<bool> _ensureInitialized() async {
if (!_isInitialized) {
return await initialize();
}
return true;
}
/// 执行一次性语音识别
@override
Future<RecognitionEvent> recognizeOnce() async {
if (!await _ensureInitialized()) {
return RecognitionEvent(
type: RecognitionEventType.error,
error: '语音识别服务未初始化',
);
}
try {
// 创建一个Completer用于等待最终结果
final completer = Completer<RecognitionEvent>();
// 创建一次性事件监听
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<bool>('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<Stream<RecognitionEvent>> startContinuousRecognition(
// bool audioSourceType) async {
// if (!await _ensureInitialized()) {
// _eventStreamController.add(RecognitionEvent(
// type: RecognitionEventType.error,
// error: '语音识别服务未初始化',
// ));
// return _eventStreamController.stream;
// }
// try {
// final success = await _channel.invokeMethod<bool>('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<bool> stopContinuousRecognition() async {
if (!_isInitialized || !_isContinuousRecognitionActive) {
return false;
}
try {
final success =
await _channel.invokeMethod<bool>('stopContinuousRecognition') ??
false;
if (success) {
_isContinuousRecognitionActive = false;
}
return success;
} catch (e) {
debugPrint('停止连续识别异常:$e');
return false;
}
}
/// 检查连续识别是否活跃
@override
bool isContinuousRecognitionActive() {
return _isContinuousRecognitionActive;
}
/// 释放资源
@override
Future<void> dispose() async {
if (_isContinuousRecognitionActive) {
await stopContinuousRecognition();
}
await _eventSubscription?.cancel();
_eventSubscription = null;
if (!_eventStreamController.isClosed) {
await _eventStreamController.close();
}
if (_isInitialized) {
await _channel.invokeMethod<bool>('release');
_isInitialized = false;
}
}
@override
Future<bool> enableRecord(String filePath, {bool acceptAudioData = false}) {
// TODO: implement enableRecord
throw UnimplementedError();
}
@override
Future<bool> stopRecord(bool isSave) {
// TODO: implement stopRecord
throw UnimplementedError();
}
@override
Future<bool> pauseRecord() {
// TODO: implement pauseRecord
throw UnimplementedError();
}
@override
Future<bool> moveFile(String sourcePath, String destPath) {
// TODO: implement moveFile
throw UnimplementedError();
}
@override
Future<bool> renameFile(String filePath, String newName) {
// TODO: implement renameFile
throw UnimplementedError();
}
@override
Future<void> disableBluetoothAudio() {
// TODO: implement disableBluetoothAudio
throw UnimplementedError();
}
@override
Future<void> restoreOriginalAudioState() {
// TODO: implement restoreOriginalAudioState
throw UnimplementedError();
}
@override
Future<Stream<RecognitionEvent>> recognizeCallback() {
// TODO: implement recognizeCallback
throw UnimplementedError();
}
@override
Future<bool> startContinuousRecognition(bool audioSourceType) {
// TODO: implement startContinuousRecognition
throw UnimplementedError();
}
@override
Future<bool> setAudioConfig({int sampleRate = 16000, int channels = 1}) {
// TODO: implement setAudioConfig
throw UnimplementedError();
}
@override
Future<bool> resumeRecord() {
// TODO: implement resumeRecord
throw UnimplementedError();
}
@override
Stream<Uint8List>? getAudioDataStream() {
// TODO: implement getAudioDataStream
throw UnimplementedError();
}
}