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.
426 lines
13 KiB
426 lines
13 KiB
import 'dart:async';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
|
|
import '../../../data/services/asr_service.dart';
|
|
import '../../../data/services/tts_service.dart';
|
|
import '../../../data/services/speech_factory.dart';
|
|
import '../../../core/utils/logger.dart';
|
|
|
|
/// 语音测试控制器
|
|
class SpeechTestController extends GetxController {
|
|
// 语音服务工厂
|
|
final SpeechFactory _speechFactory = Get.find<SpeechFactory>();
|
|
|
|
// 当前使用的TTS服务
|
|
late TtsService _ttsService;
|
|
|
|
// 当前使用的ASR服务
|
|
late AsrService _asrService;
|
|
|
|
// 状态变量
|
|
final RxBool isInitialized = false.obs;
|
|
final RxBool isProcessing = false.obs;
|
|
final RxBool isListening = false.obs;
|
|
final RxBool isSpeaking = false.obs;
|
|
|
|
// 识别结果
|
|
final RxString intermediateResult = ''.obs; // 中间结果
|
|
final RxString finalResult = ''.obs; // 最终结果
|
|
final RxString recognitionStatus = '准备就绪'.obs;
|
|
final RxString detectedLanguage = ''.obs; // 检测到的语言
|
|
|
|
// TTS事件
|
|
final RxString ttsStatus = '准备就绪'.obs;
|
|
|
|
// 当前选择的语音服务类型
|
|
final Rx<SpeechServiceType> selectedServiceType = SpeechServiceType.azure.obs;
|
|
// 音频输入源
|
|
bool _audioSourceType = false;
|
|
// 连续识别结果流
|
|
StreamSubscription<RecognitionEvent>? _recognitionSubscription;
|
|
|
|
// TTS事件流
|
|
StreamSubscription<TtsEvent>? _ttsEventSubscription;
|
|
|
|
// 文本控制器
|
|
final TextEditingController textController = TextEditingController();
|
|
|
|
// 连续播放的测试句子
|
|
final List<String> testSentences = [
|
|
'这是第一句测试语音。',
|
|
'这是第二句测试语音,语音合成支持连续播放。',
|
|
'这是第三句测试语音,将在连续模式下播放完毕。'
|
|
];
|
|
|
|
var _currsessionid = '';
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
// 设置默认待播放文本
|
|
textController.text = '欢迎使用语音测试,这是一段默认的测试文本。您可以修改此文本,然后点击播放按钮测试语音合成效果。';
|
|
_initServices();
|
|
}
|
|
|
|
/// 初始化服务
|
|
Future<void> _initServices() async {
|
|
try {
|
|
isProcessing.value = true;
|
|
|
|
// 获取服务实例
|
|
_ttsService = _speechFactory.getTtsService();
|
|
_asrService = _speechFactory.getAsrService();
|
|
|
|
// 初始化服务
|
|
await _ttsService.initialize();
|
|
await _asrService.initialize();
|
|
|
|
// 监听TTS事件
|
|
_setupTtsEventListener();
|
|
// 获取识别事件流 - 这启动了异步识别过程
|
|
final stream = await _asrService.recognizeCallback();
|
|
// 设置事件监听 - 异步处理识别结果
|
|
_setupAsrEventListener(stream);
|
|
isInitialized.value = true;
|
|
ttsStatus.value = '准备就绪';
|
|
recognitionStatus.value = '准备就绪';
|
|
} catch (e) {
|
|
ttsStatus.value = '初始化失败';
|
|
recognitionStatus.value = '初始化失败';
|
|
Logger.error('语音测试初始化失败: ${e.toString()}');
|
|
} finally {
|
|
isProcessing.value = false;
|
|
}
|
|
}
|
|
|
|
/// 设置TTS事件监听
|
|
void _setupTtsEventListener() {
|
|
// 取消旧的订阅
|
|
_ttsEventSubscription?.cancel();
|
|
|
|
// 设置新的订阅
|
|
_ttsEventSubscription = _ttsService.onEvent.listen((event) {
|
|
switch (event.type) {
|
|
case TtsEventType.started:
|
|
isSpeaking.value = true;
|
|
ttsStatus.value = '正在播放...';
|
|
break;
|
|
case TtsEventType.completed:
|
|
isSpeaking.value = false;
|
|
ttsStatus.value = '播放完成';
|
|
break;
|
|
case TtsEventType.canceled:
|
|
isSpeaking.value = false;
|
|
ttsStatus.value = '播放已取消';
|
|
break;
|
|
case TtsEventType.error:
|
|
isSpeaking.value = false;
|
|
ttsStatus.value = '播放错误: ${event.error}';
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
/// 设置ASR事件监听
|
|
void _setupAsrEventListener(Stream<RecognitionEvent> stream) {
|
|
// 取消旧的订阅
|
|
_recognitionSubscription?.cancel();
|
|
|
|
// 重置识别结果
|
|
intermediateResult.value = '';
|
|
finalResult.value = '';
|
|
detectedLanguage.value = '';
|
|
recognitionStatus.value = '正在等待语音输入...';
|
|
|
|
// 设置新的订阅
|
|
_recognitionSubscription = stream.listen((event) {
|
|
switch (event.type) {
|
|
case RecognitionEventType.sessionStarted:
|
|
isListening.value = true;
|
|
recognitionStatus.value = '开始聆听...';
|
|
break;
|
|
|
|
case RecognitionEventType.sessionStopped:
|
|
isListening.value = false;
|
|
recognitionStatus.value = '聆听已结束';
|
|
// 清空中间结果,保留最终结果
|
|
intermediateResult.value = '';
|
|
break;
|
|
|
|
case RecognitionEventType.intermediateResult:
|
|
// 更新中间结果,显示为斜体或不同颜色
|
|
intermediateResult.value = event.text;
|
|
if (event.detectedLanguage.isNotEmpty) {
|
|
detectedLanguage.value = event.detectedLanguage;
|
|
}
|
|
recognitionStatus.value = '正在识别...';
|
|
break;
|
|
|
|
case RecognitionEventType.finalResult:
|
|
// 更新最终结果,显示为正常文本
|
|
finalResult.value = event.text;
|
|
intermediateResult.value = ''; // 清空中间结果
|
|
if (event.detectedLanguage.isNotEmpty) {
|
|
detectedLanguage.value = event.detectedLanguage;
|
|
}
|
|
recognitionStatus.value = '识别完成';
|
|
break;
|
|
case RecognitionEventType.finalResult1:
|
|
// 更新最终结果,显示为正常文本
|
|
finalResult.value = event.text;
|
|
intermediateResult.value = ''; // 清空中间结果
|
|
if (event.detectedLanguage.isNotEmpty) {
|
|
detectedLanguage.value = event.detectedLanguage;
|
|
}
|
|
recognitionStatus.value = '识别完成';
|
|
break;
|
|
case RecognitionEventType.error:
|
|
isListening.value = false;
|
|
recognitionStatus.value = '识别错误: ${event.error}';
|
|
break;
|
|
|
|
case RecognitionEventType.canceled:
|
|
isListening.value = false;
|
|
recognitionStatus.value = '识别已取消';
|
|
// 清空中间结果
|
|
intermediateResult.value = '';
|
|
break;
|
|
}
|
|
}, onError: (error) {
|
|
isListening.value = false;
|
|
recognitionStatus.value = '识别错误: $error';
|
|
intermediateResult.value = '';
|
|
});
|
|
}
|
|
|
|
/// 切换语音服务类型
|
|
void switchServiceType(SpeechServiceType type) async {
|
|
if (selectedServiceType.value == type) return;
|
|
|
|
try {
|
|
isProcessing.value = true;
|
|
|
|
// 停止当前的所有服务状态
|
|
await stopAll();
|
|
|
|
// 切换服务类型
|
|
_speechFactory.setServiceType(type);
|
|
selectedServiceType.value = type;
|
|
|
|
// 重新初始化服务
|
|
await _initServices();
|
|
} catch (e) {
|
|
Logger.error('切换语音服务类型失败: ${e.toString()}');
|
|
} finally {
|
|
isProcessing.value = false;
|
|
}
|
|
}
|
|
|
|
/// 停止所有活动
|
|
Future<void> stopAll() async {
|
|
// 停止ASR
|
|
if (isListening.value) {
|
|
await stopContinuousRecognition();
|
|
}
|
|
|
|
// 停止TTS
|
|
if (isSpeaking.value) {
|
|
await stopSpeaking();
|
|
}
|
|
|
|
// 取消所有订阅
|
|
await _recognitionSubscription?.cancel();
|
|
_recognitionSubscription = null;
|
|
}
|
|
|
|
/// 单次播放文本
|
|
Future<void> speakOnce() async {
|
|
if (!isInitialized.value || isProcessing.value) return;
|
|
|
|
final text = textController.text.trim();
|
|
if (text.isEmpty) {
|
|
ttsStatus.value = '请输入要播放的文本';
|
|
return;
|
|
}
|
|
|
|
try {
|
|
isProcessing.value = true;
|
|
ttsStatus.value = '准备播放...';
|
|
|
|
final result = await _ttsService.speakOnce(_currsessionid, text);
|
|
|
|
if (!result) {
|
|
ttsStatus.value = '播放失败';
|
|
}
|
|
} catch (e) {
|
|
ttsStatus.value = '播放异常: $e';
|
|
Logger.error('单次播放异常: ${e.toString()}');
|
|
} finally {
|
|
isProcessing.value = false;
|
|
}
|
|
}
|
|
|
|
/// 连续播放测试
|
|
Future<void> speakContinuously() async {
|
|
if (!isInitialized.value || isProcessing.value) return;
|
|
|
|
try {
|
|
isProcessing.value = true;
|
|
ttsStatus.value = '准备连续播放...';
|
|
|
|
// 为避免阻塞,使用一个句子接一个句子的方式添加到队列
|
|
for (final sentence in testSentences) {
|
|
if (!await _ttsService.speakStream(_currsessionid, sentence)) {
|
|
ttsStatus.value = '添加句子到队列失败';
|
|
break;
|
|
}
|
|
// 给前一个句子一点处理时间
|
|
await Future.delayed(const Duration(milliseconds: 50));
|
|
}
|
|
|
|
// 刷新队列,确保播放完成
|
|
await _ttsService.flushStream(_currsessionid);
|
|
} catch (e) {
|
|
ttsStatus.value = '连续播放异常: $e';
|
|
Logger.error('连续播放异常: ${e.toString()}');
|
|
} finally {
|
|
isProcessing.value = false;
|
|
}
|
|
}
|
|
|
|
/// 停止播放
|
|
Future<void> stopSpeaking() async {
|
|
if (!isInitialized.value) return;
|
|
|
|
try {
|
|
await _ttsService.stop();
|
|
} catch (e) {
|
|
ttsStatus.value = '停止播放异常: $e';
|
|
Logger.error('停止播放异常: ${e.toString()}');
|
|
}
|
|
}
|
|
|
|
/// 一次性识别
|
|
Future<void> recognizeOnce() async {
|
|
if (!isInitialized.value || isProcessing.value || isListening.value) return;
|
|
|
|
try {
|
|
isProcessing.value = true;
|
|
recognitionStatus.value = '开始识别...';
|
|
|
|
// 清空之前的结果
|
|
intermediateResult.value = '';
|
|
finalResult.value = '';
|
|
detectedLanguage.value = '';
|
|
|
|
// 显示正在进行的状态
|
|
recognitionStatus.value = '正在聆听...';
|
|
|
|
// 执行一次性识别 - 这是同步等待结果的操作
|
|
final result = await _asrService.recognizeOnce();
|
|
|
|
if (result.isError) {
|
|
recognitionStatus.value = '识别错误: ${result.error}';
|
|
Logger.error('一次性识别错误: ${result.error}');
|
|
} else {
|
|
// 更新最终结果
|
|
finalResult.value = result.text;
|
|
|
|
// 更新语言信息
|
|
if (result.detectedLanguage.isNotEmpty) {
|
|
detectedLanguage.value = result.detectedLanguage;
|
|
Logger.debug('检测到语言: ${result.detectedLanguage}');
|
|
}
|
|
|
|
recognitionStatus.value = '识别完成';
|
|
}
|
|
} catch (e) {
|
|
recognitionStatus.value = '识别异常: $e';
|
|
Logger.error('一次性识别异常: ${e.toString()}');
|
|
} finally {
|
|
isProcessing.value = false;
|
|
}
|
|
}
|
|
|
|
/// 开始连续识别
|
|
Future<void> startContinuousRecognition() async {
|
|
if (!isInitialized.value || isProcessing.value || isListening.value) return;
|
|
|
|
try {
|
|
isProcessing.value = true;
|
|
|
|
// 清空之前的结果
|
|
intermediateResult.value = '';
|
|
finalResult.value = '';
|
|
detectedLanguage.value = '';
|
|
recognitionStatus.value = '准备连续识别...';
|
|
|
|
// 获取识别事件流 - 这启动了异步识别过程
|
|
final stream =
|
|
await _asrService.startContinuousRecognition(_audioSourceType);
|
|
} catch (e) {
|
|
recognitionStatus.value = '启动连续识别异常: $e';
|
|
Logger.error('启动连续识别异常: ${e.toString()}');
|
|
isListening.value = false;
|
|
intermediateResult.value = '';
|
|
finalResult.value = '';
|
|
} finally {
|
|
isProcessing.value = false;
|
|
}
|
|
}
|
|
|
|
/// 停止连续识别
|
|
Future<void> stopContinuousRecognition() async {
|
|
if (!isInitialized.value || !isListening.value) return;
|
|
|
|
try {
|
|
// 首先更新状态,防止重复调用
|
|
isListening.value = false;
|
|
recognitionStatus.value = '正在停止识别...';
|
|
|
|
// 停止识别过程
|
|
await _asrService.stopContinuousRecognition();
|
|
|
|
// 更新UI状态
|
|
recognitionStatus.value = '连续识别已停止';
|
|
intermediateResult.value = '';
|
|
} catch (e) {
|
|
recognitionStatus.value = '停止连续识别异常: $e';
|
|
Logger.error('停止连续识别异常: ${e.toString()}');
|
|
}
|
|
}
|
|
|
|
// 获取识别结果(用于UI显示)
|
|
String getDisplayResult() {
|
|
// 如果有最终结果,优先显示
|
|
String displayText = '';
|
|
|
|
if (finalResult.value.isNotEmpty) {
|
|
displayText = finalResult.value;
|
|
}
|
|
|
|
// 如果有中间结果,添加到最终结果后(可在UI中使用不同样式显示)
|
|
if (intermediateResult.value.isNotEmpty) {
|
|
if (displayText.isNotEmpty) {
|
|
displayText = '$displayText\n\n正在识别: ${intermediateResult.value}';
|
|
} else {
|
|
displayText = '正在识别: ${intermediateResult.value}';
|
|
}
|
|
}
|
|
|
|
return displayText;
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
// 取消所有订阅
|
|
_recognitionSubscription?.cancel();
|
|
_ttsEventSubscription?.cancel();
|
|
|
|
// 释放控制器
|
|
textController.dispose();
|
|
|
|
super.onClose();
|
|
}
|
|
}
|
|
|