import 'dart:async'; import 'package:get/get.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import '../../../data/services/azure_tts_service.dart'; import '../../../core/utils/logger.dart'; // 导入 TtsState 枚举 import '../../../data/services/azure_tts_service.dart' show TtsState; class AzureTtsTestController extends GetxController { final AzureTtsService _ttsService = Get.find(); // 可观察状态 final isSpeaking = false.obs; final errorMessage = ''.obs; final isLoading = false.obs; final statusMessage = '未连接'.obs; // 添加实际音频播放状态 final isAudioPlaying = false.obs; // 播放状态检查定时器 Timer? _playbackCheckTimer; // 当前播放的索引 final currentPlayingIndex = RxInt(-1); // 当前使用的语音 final currentVoice = 'zh-CN-XiaoxiaoNeural'.obs; // 可用的语音列表 final List> availableVoices = [ {'id': 'zh-CN-XiaoxiaoNeural', 'name': '晓晓(女声)'}, {'id': 'zh-CN-YunxiNeural', 'name': '云希(男声)'}, {'id': 'zh-CN-YunyangNeural', 'name': '云扬(男声)'}, {'id': 'zh-CN-XiaohanNeural', 'name': '晓涵(女声)'}, {'id': 'zh-CN-XiaomoNeural', 'name': '晓墨(女声)'}, {'id': 'zh-CN-XiaoxuanNeural', 'name': '晓萱(女声)'}, {'id': 'en-US-AriaNeural', 'name': 'Aria(英语女声)'}, ]; // 固定的播报内容 final RxList speechTexts = [ '这是第一条播报测试,请听取Azure合成效果。', '这是第二条播报测试,Azure语音合成支持连续多条播报。', '这是第三条播报测试,可以测试语音合成的流畅度和连贯性。', 'This is an English test sentence. Azure Text-to-Speech supports multiple languages.', ].obs; // 事件订阅 StreamSubscription? _eventSubscription; @override void onInit() { super.onInit(); // 初始化UI状态 isSpeaking.value = false; isAudioPlaying.value = false; errorMessage.value = ''; isLoading.value = false; // 监听TTS服务状态 _setupEventListener(); // 启动播放状态检查定时器 _startPlaybackCheckTimer(); } // 设置事件监听 void _setupEventListener() { _eventSubscription?.cancel(); _eventSubscription = _ttsService.synthesisStream?.listen(_handleEvent); } // 处理事件 void _handleEvent(SynthesisEvent event) { switch (event.type) { case SynthesisEventType.synthesisStarted: statusMessage.value = '合成开始'; errorMessage.value = ''; break; case SynthesisEventType.synthesisCompleted: statusMessage.value = '合成完成'; errorMessage.value = ''; break; case SynthesisEventType.synthesisCanceled: statusMessage.value = '合成取消'; errorMessage.value = event.reason ?? '未知原因'; currentPlayingIndex.value = -1; break; case SynthesisEventType.error: statusMessage.value = '错误'; errorMessage.value = event.error ?? '未知错误'; currentPlayingIndex.value = -1; break; default: statusMessage.value = '事件: ${event.type}'; break; } // 强制更新UI update(); } /// 更新UI状态 void updateUIState() { // 强制更新UI update(); } // 启动播放状态检查定时器 void _startPlaybackCheckTimer() { _playbackCheckTimer?.cancel(); _playbackCheckTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) { // 更新实际播放状态 final actuallyPlaying = _ttsService.ttsState == TtsState.playing || _ttsService.ttsState == TtsState.synthesizing; // 只有状态变化时才更新 if (isAudioPlaying.value != actuallyPlaying) { isAudioPlaying.value = actuallyPlaying; // 更新UI update(); } }); } /// 连续播放所有文本 Future speakContinuousTexts() async { if (isLoading.value) { return; } try { isLoading.value = true; isSpeaking.value = true; errorMessage.value = ''; final speaker = currentVoice.value; // 设置语音参数 _ttsService.currentVoice = speaker; // 连续播放所有文本 for (int i = 0; i < speechTexts.length; i++) { // 如果播放被中断,退出循环 if (!isSpeaking.value) break; // 在主线程上更新UI WidgetsBinding.instance.addPostFrameCallback((_) { currentPlayingIndex.value = i; statusMessage.value = '正在播放第 ${i + 1}/${speechTexts.length} 条文本'; update(); }); // 播放当前文本,不等待完成 _ttsService.speak(speechTexts[i]); // 短暂延迟,确保文本被送入队列 await Future.delayed(const Duration(milliseconds: 300)); } // 在主线程上更新UI WidgetsBinding.instance.addPostFrameCallback((_) { statusMessage.value = '所有文本已送入播放队列'; update(); }); } catch (e) { errorMessage.value = '播放出错'; if (kDebugMode) { print('连续播放出错: $e'); } } finally { isLoading.value = false; // 强制更新UI update(); } } /// 播放单条文本 Future playSingle(int index) async { if (index < 0 || index >= speechTexts.length) { return; } try { isSpeaking.value = true; errorMessage.value = ''; currentPlayingIndex.value = index; statusMessage.value = '准备播放...'; final text = speechTexts[index]; final speaker = currentVoice.value; // 设置语音参数 _ttsService.currentVoice = speaker; // 使用异步操作,避免阻塞主线程 Future(() { // 播放文本,不等待完成 _ttsService.speak(text); // 在主线程上更新UI WidgetsBinding.instance.addPostFrameCallback((_) { statusMessage.value = '文本已送入播放队列'; update(); }); }); } catch (e) { errorMessage.value = '播放出错'; if (kDebugMode) { print('播放出错: $e'); } } } /// 停止播放 void stopSpeaking() { try { isSpeaking.value = false; statusMessage.value = '正在停止播放...'; // 使用异步操作,避免阻塞主线程 Future(() async { // 停止播放 await _ttsService.stop(); // 在主线程上更新UI WidgetsBinding.instance.addPostFrameCallback((_) { // 重置UI状态 currentPlayingIndex.value = -1; statusMessage.value = '已停止播放'; update(); }); }); } catch (e) { if (kDebugMode) { print('停止播放出错: $e'); } errorMessage.value = '停止播放出错'; } } // 设置当前语音 void setVoice(String voiceId) { currentVoice.value = voiceId; } // 获取当前语音名称 String getCurrentVoiceName() { final voice = availableVoices.firstWhere( (v) => v['id'] == currentVoice.value, orElse: () => {'id': currentVoice.value, 'name': '未知语音'}, ); return voice['name'] ?? '未知语音'; } @override void onClose() { // 取消事件订阅 _eventSubscription?.cancel(); // 取消播放状态检查定时器 _playbackCheckTimer?.cancel(); super.onClose(); } /// 初始化Azure TTS服务 Future initialize({ String? subscriptionKey, String? region, }) async { try { isLoading.value = true; statusMessage.value = '正在初始化Azure TTS...'; if (subscriptionKey == null || region == null) { statusMessage.value = '尝试从环境变量读取配置...'; } await _ttsService.initialize( subscriptionKey: subscriptionKey, region: region, ); // 检查是否初始化成功 final isSuccess = _ttsService.isInitialized; if (isSuccess) { isSpeaking.value = false; statusMessage.value = 'Azure TTS初始化成功'; } else { statusMessage.value = 'Azure TTS初始化失败'; } } catch (e) { statusMessage.value = '初始化异常: $e'; errorMessage.value = e.toString(); if (kDebugMode) { print('初始化异常: $e'); } } finally { isLoading.value = false; // 强制更新UI update(); } } }