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.
343 lines
9.1 KiB
343 lines
9.1 KiB
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'dart:async';
|
|
import 'package:flutter/foundation.dart';
|
|
import '../../../data/services/volcano_tts_api_service.dart';
|
|
|
|
class TtsTestController extends GetxController {
|
|
final VolcanoTtsApiService _ttsService = Get.find<VolcanoTtsApiService>();
|
|
|
|
// 可观察状态
|
|
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 RxList<String> speechTexts = <String>[
|
|
'这是第一条播报测试,请听取合成效果。',
|
|
'这是第二条播报测试,火山语音合成支持连续多条播报。',
|
|
'这是第三条播报测试,可以测试语音合成的流畅度和连贯性。',
|
|
'这是第四条播报测试,感谢您的使用。',
|
|
].obs;
|
|
|
|
// 事件订阅
|
|
StreamSubscription? _eventSubscription;
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
|
|
// 初始化UI状态
|
|
isSpeaking.value = false;
|
|
isAudioPlaying.value = false;
|
|
errorMessage.value = '';
|
|
isLoading.value = false;
|
|
|
|
// 监听TTS服务状态
|
|
ever(_ttsService.isConnected, (bool connected) {
|
|
if (connected) {
|
|
statusMessage.value = '已连接';
|
|
} else {
|
|
statusMessage.value = '未连接';
|
|
isSpeaking.value = false;
|
|
isAudioPlaying.value = false;
|
|
}
|
|
});
|
|
|
|
// 监听TTS合成状态
|
|
ever(_ttsService.isSynthesizing, (bool synthesizing) {
|
|
isSpeaking.value = synthesizing;
|
|
});
|
|
|
|
// 监听TTS播放状态
|
|
ever(_ttsService.isPlaying.obs, (bool playing) {
|
|
isAudioPlaying.value = playing;
|
|
if (!playing && currentPlayingIndex.value >= 0) {
|
|
// 如果播放结束,重置当前播放索引
|
|
currentPlayingIndex.value = -1;
|
|
}
|
|
});
|
|
|
|
// 设置事件监听
|
|
_setupEventListener();
|
|
|
|
// 连接到TTS服务
|
|
_connectToTtsService();
|
|
|
|
// 启动播放状态检查定时器
|
|
_startPlaybackCheckTimer();
|
|
}
|
|
|
|
// 设置事件监听
|
|
void _setupEventListener() {
|
|
_eventSubscription?.cancel();
|
|
_eventSubscription = _ttsService.eventStream.listen(_handleEvent);
|
|
}
|
|
|
|
// 处理事件
|
|
void _handleEvent(Map<String, dynamic> event) {
|
|
final eventType = event['eventType'];
|
|
|
|
switch (eventType) {
|
|
case 'connectionStarted':
|
|
statusMessage.value = '连接成功';
|
|
errorMessage.value = '';
|
|
break;
|
|
case 'connectionFailed':
|
|
statusMessage.value = '连接失败';
|
|
errorMessage.value = event['error'] ?? '未知错误';
|
|
break;
|
|
case 'sessionStarted':
|
|
statusMessage.value = '会话开始';
|
|
errorMessage.value = '';
|
|
break;
|
|
case 'sessionFailed':
|
|
statusMessage.value = '会话失败';
|
|
errorMessage.value = event['error'] ?? '未知错误';
|
|
currentPlayingIndex.value = -1;
|
|
break;
|
|
case 'ttsSentenceStart':
|
|
statusMessage.value = '开始合成';
|
|
break;
|
|
case 'ttsSentenceEnd':
|
|
statusMessage.value = '合成完成';
|
|
break;
|
|
case 'sessionFinished':
|
|
statusMessage.value = '会话结束';
|
|
currentPlayingIndex.value = -1;
|
|
break;
|
|
case 'connectionFinished':
|
|
statusMessage.value = '连接关闭';
|
|
currentPlayingIndex.value = -1;
|
|
break;
|
|
case 'error':
|
|
statusMessage.value = '错误';
|
|
errorMessage.value = event['error'] ?? '未知错误';
|
|
break;
|
|
case 'disconnected':
|
|
statusMessage.value = '连接已断开';
|
|
currentPlayingIndex.value = -1;
|
|
break;
|
|
default:
|
|
statusMessage.value = '事件: $eventType';
|
|
break;
|
|
}
|
|
|
|
// 强制更新UI
|
|
update();
|
|
}
|
|
|
|
// 连接到TTS服务
|
|
Future<void> _connectToTtsService() async {
|
|
try {
|
|
isLoading.value = true;
|
|
statusMessage.value = '正在连接...';
|
|
|
|
final success = await _ttsService.connect();
|
|
|
|
if (!success) {
|
|
statusMessage.value = '连接失败';
|
|
errorMessage.value = '无法连接到火山语音服务';
|
|
}
|
|
} catch (e) {
|
|
statusMessage.value = '连接错误';
|
|
errorMessage.value = e.toString();
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
}
|
|
|
|
/// 更新UI状态
|
|
void updateUIState() {
|
|
// 强制更新UI
|
|
update();
|
|
}
|
|
|
|
/// 播放单条文本
|
|
Future<void> speakText(int index) async {
|
|
if (index < 0 || index >= speechTexts.length || isAudioPlaying.value) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
isLoading.value = true;
|
|
isSpeaking.value = true;
|
|
isAudioPlaying.value = true;
|
|
errorMessage.value = '';
|
|
currentPlayingIndex.value = index;
|
|
|
|
final text = speechTexts[index];
|
|
const speaker = 'zh_female_shuangkuaisisi_moon_bigtts';
|
|
|
|
// 确保TTS服务已连接
|
|
if (!_ttsService.isConnected.value) {
|
|
await _connectToTtsService();
|
|
}
|
|
|
|
// 开始会话
|
|
await _ttsService.startSession(speaker);
|
|
|
|
// 播放文本
|
|
await _ttsService.speak(text, speaker: speaker);
|
|
|
|
// 结束会话
|
|
await _ttsService.endSession();
|
|
|
|
// 确保播放列表被清空
|
|
await _ttsService.stop();
|
|
|
|
} catch (e) {
|
|
errorMessage.value = '播放出错';
|
|
if (kDebugMode) {
|
|
print('播放出错: $e');
|
|
}
|
|
} finally {
|
|
isLoading.value = false;
|
|
isSpeaking.value = false;
|
|
isAudioPlaying.value = false;
|
|
currentPlayingIndex.value = -1;
|
|
}
|
|
}
|
|
|
|
// 启动播放状态检查定时器
|
|
void _startPlaybackCheckTimer() {
|
|
_playbackCheckTimer?.cancel();
|
|
_playbackCheckTimer = Timer.periodic(const Duration(milliseconds: 200), (timer) {
|
|
// 更新实际播放状态
|
|
final actuallyPlaying = _ttsService.isPlaying;
|
|
isAudioPlaying.value = actuallyPlaying;
|
|
|
|
// 如果不再播放,确保重置状态
|
|
if (!actuallyPlaying && isSpeaking.value) {
|
|
// 检查是否真的播放完成了
|
|
if (!_ttsService.isSynthesizing.value && currentPlayingIndex.value >= 0) {
|
|
// 播放已完成,重置状态
|
|
isSpeaking.value = false;
|
|
currentPlayingIndex.value = -1;
|
|
// 更新UI
|
|
update();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/// 连续播放所有文本
|
|
Future<void> speakContinuousTexts() async {
|
|
if (isAudioPlaying.value || isLoading.value) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
isLoading.value = true;
|
|
isSpeaking.value = true;
|
|
isAudioPlaying.value = true;
|
|
errorMessage.value = '';
|
|
|
|
// 确保TTS服务已连接
|
|
if (!_ttsService.isConnected.value) {
|
|
await _connectToTtsService();
|
|
}
|
|
|
|
const speaker = 'zh_female_shuangkuaisisi_moon_bigtts';
|
|
|
|
// 停止之前的播放
|
|
await _ttsService.stop();
|
|
|
|
// 开始会话
|
|
await _ttsService.startSession(speaker);
|
|
|
|
// 连续播放所有文本
|
|
for (int i = 0; i < speechTexts.length; i++) {
|
|
// 如果播放被中断,退出循环
|
|
if (!isSpeaking.value) break;
|
|
|
|
currentPlayingIndex.value = i;
|
|
statusMessage.value = '正在播放第 ${i + 1}/${speechTexts.length} 条文本';
|
|
|
|
// 播放当前文本
|
|
await _ttsService.speak(speechTexts[i], speaker: speaker);
|
|
|
|
}
|
|
|
|
// 结束会话
|
|
await _ttsService.endSession();
|
|
|
|
|
|
statusMessage.value = '播放完成';
|
|
|
|
} catch (e) {
|
|
errorMessage.value = '播放出错';
|
|
if (kDebugMode) {
|
|
print('连续播放出错: $e');
|
|
}
|
|
|
|
// 尝试结束会话,忽略可能的错误
|
|
try {
|
|
await _ttsService.endSession();
|
|
} catch (_) {
|
|
// 忽略结束会话时的错误
|
|
}
|
|
} finally {
|
|
currentPlayingIndex.value = -1;
|
|
isSpeaking.value = false;
|
|
isLoading.value = false;
|
|
// 确保最终状态正确
|
|
isAudioPlaying.value = false;
|
|
|
|
// 强制更新UI
|
|
update();
|
|
}
|
|
}
|
|
|
|
/// 停止播放
|
|
void stopSpeaking() async {
|
|
try {
|
|
isSpeaking.value = false;
|
|
statusMessage.value = '正在停止播放...';
|
|
|
|
// 停止播放
|
|
await _ttsService.stop();
|
|
|
|
// 结束会话
|
|
await _ttsService.endSession();
|
|
|
|
// 重置UI状态
|
|
currentPlayingIndex.value = -1;
|
|
statusMessage.value = '已停止播放';
|
|
|
|
} catch (e) {
|
|
if (kDebugMode) {
|
|
print('停止播放出错: $e');
|
|
}
|
|
errorMessage.value = '停止播放出错';
|
|
} finally {
|
|
isSpeaking.value = false;
|
|
isLoading.value = false;
|
|
isAudioPlaying.value = false;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
// 取消事件订阅
|
|
_eventSubscription?.cancel();
|
|
|
|
// 取消播放状态检查定时器
|
|
_playbackCheckTimer?.cancel();
|
|
|
|
// 断开TTS服务连接
|
|
_ttsService.disconnect();
|
|
|
|
super.onClose();
|
|
}
|
|
}
|