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.
197 lines
5.3 KiB
197 lines
5.3 KiB
import 'dart:async';
|
|
import 'package:get/get.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import '../../../data/services/voice_recognition_service.dart';
|
|
|
|
class VoiceInputController extends GetxController {
|
|
// Observable states
|
|
final isConnecting = true.obs;
|
|
final isMuted = false.obs;
|
|
final isRecording = false.obs;
|
|
final recognizedText = ''.obs;
|
|
|
|
// 语音识别服务
|
|
late final VoiceRecognitionService _voiceService;
|
|
|
|
// 连续识别相关
|
|
StreamSubscription? _recognitionSubscription;
|
|
|
|
// Callbacks
|
|
final Function(String) onRecordingResult;
|
|
final VoidCallback onClosePanel;
|
|
final Function(String) onRecognizing;
|
|
|
|
VoiceInputController({
|
|
required this.onRecordingResult,
|
|
required this.onClosePanel,
|
|
required this.onRecognizing,
|
|
});
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
_initializeVoiceService();
|
|
}
|
|
|
|
Future<void> _initializeVoiceService() async {
|
|
try {
|
|
// 创建语音识别服务实例
|
|
_voiceService = VoiceRecognitionService();
|
|
|
|
// 初始化语音识别服务
|
|
await _voiceService.initialize();
|
|
|
|
// 初始化完成后更新状态
|
|
isConnecting.value = false;
|
|
|
|
// 自动开始连续语音识别
|
|
await startContinuousRecognition();
|
|
} catch (e) {
|
|
print('初始化语音识别服务失败: $e');
|
|
Get.snackbar(
|
|
'Error',
|
|
'初始化语音识别服务失败: $e',
|
|
snackPosition: SnackPosition.BOTTOM,
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<void> startContinuousRecognition() async {
|
|
if (isConnecting.value) return;
|
|
if (isRecording.value) return; // 已经在录音中
|
|
|
|
try {
|
|
isRecording.value = true;
|
|
recognizedText.value = '';
|
|
|
|
// 开始连续语音识别
|
|
final stream = await _voiceService.startContinuousRecognition();
|
|
|
|
// 订阅识别事件流
|
|
_recognitionSubscription = stream.listen((event) {
|
|
switch (event.type) {
|
|
case RecognitionEventType.intermediateResult:
|
|
// 更新中间识别结果
|
|
recognizedText.value = event.text;
|
|
// 调用识别中回调
|
|
onRecognizing(event.text);
|
|
break;
|
|
|
|
case RecognitionEventType.finalResult:
|
|
// 更新最终识别结果
|
|
if (event.text.isNotEmpty) {
|
|
recognizedText.value = event.text;
|
|
// 调用最终结果回调,发送识别到的句子
|
|
onRecordingResult(event.text);
|
|
// 清空识别文本,准备下一句
|
|
recognizedText.value = '';
|
|
// 不停止录音,继续监听下一句
|
|
}
|
|
break;
|
|
|
|
case RecognitionEventType.error:
|
|
case RecognitionEventType.canceled:
|
|
// 处理错误
|
|
print('语音识别错误: ${event.error}');
|
|
Get.snackbar(
|
|
'Error',
|
|
'语音识别错误: ${event.error}',
|
|
snackPosition: SnackPosition.BOTTOM,
|
|
);
|
|
// 尝试重新启动识别
|
|
restartRecognition();
|
|
break;
|
|
|
|
case RecognitionEventType.sessionStopped:
|
|
// 会话结束,尝试重新启动
|
|
isRecording.value = false;
|
|
restartRecognition();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}, onError: (error) {
|
|
print('语音识别流错误: $error');
|
|
Get.snackbar(
|
|
'Error',
|
|
'语音识别流错误: $error',
|
|
snackPosition: SnackPosition.BOTTOM,
|
|
);
|
|
isRecording.value = false;
|
|
// 尝试重新启动识别
|
|
restartRecognition();
|
|
});
|
|
|
|
} catch (e) {
|
|
print('语音识别失败: $e');
|
|
Get.snackbar(
|
|
'Error',
|
|
'启动语音识别失败: $e',
|
|
snackPosition: SnackPosition.BOTTOM,
|
|
);
|
|
isRecording.value = false;
|
|
}
|
|
}
|
|
|
|
// 重新启动识别
|
|
Future<void> restartRecognition() async {
|
|
try {
|
|
// 先停止当前识别
|
|
await stopRecording();
|
|
// 延迟一下再重新启动
|
|
await Future.delayed(const Duration(milliseconds: 500));
|
|
// 重新启动连续识别
|
|
await startContinuousRecognition();
|
|
} catch (e) {
|
|
print('重新启动语音识别失败: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> stopRecording() async {
|
|
if (!isRecording.value) return;
|
|
|
|
try {
|
|
// 取消事件订阅
|
|
await _recognitionSubscription?.cancel();
|
|
_recognitionSubscription = null;
|
|
|
|
// 停止连续识别
|
|
if (_voiceService.isContinuousRecognitionActive()) {
|
|
await _voiceService.stopContinuousRecognition();
|
|
}
|
|
|
|
isRecording.value = false;
|
|
} catch (e) {
|
|
print('停止语音识别失败: $e');
|
|
isRecording.value = false;
|
|
}
|
|
}
|
|
|
|
void toggleMute() {
|
|
// 只切换静音状态,不再影响录音
|
|
// 因为已启用回声消除,不需要在录音时停止TTS
|
|
isMuted.value = !isMuted.value;
|
|
}
|
|
|
|
// 手动开始录音(用户点击按钮)
|
|
void startRecording() {
|
|
if (!isRecording.value) {
|
|
startContinuousRecognition();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
// 停止录音
|
|
stopRecording();
|
|
|
|
// 重置状态
|
|
isConnecting.value = false;
|
|
isMuted.value = false;
|
|
isRecording.value = false;
|
|
recognizedText.value = '';
|
|
|
|
super.onClose();
|
|
}
|
|
}
|