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.

484 lines
13 KiB

import 'dart:async';
import 'dart:collection';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:flutter_tts/flutter_tts.dart';
import '../../../core/utils/logger.dart';
import '../tts_service.dart';
/// Flutter TTS服务,负责基本的文本转语音功能
///
/// 使用flutter_tts插件实现TtsService接口
class FlutterTtsService extends GetxService implements TtsService {
static final FlutterTtsService to = Get.put(FlutterTtsService());
// flutter_tts实例
late final FlutterTts _flutterTts;
// 支持的语音列表
final List<String> _supportedVoices = [];
List<String> get supportedVoices => _supportedVoices;
// 当前语音
final RxString _currentVoice = ''.obs;
@override
String get currentVoice => _currentVoice.value;
// 播放状态
final RxBool _isSpeaking = false.obs;
@override
bool get isSpeaking => _isSpeaking.value;
// 文本队列
final Queue<_SpeechItem> _textQueue = Queue<_SpeechItem>();
bool _isProcessingQueue = false;
// 事件流控制器
final StreamController<TtsEvent> _eventController = StreamController<TtsEvent>.broadcast();
@override
Stream<TtsEvent> get onEvent => _eventController.stream;
// 语言
String _language = 'zh-CN';
// 流式文本缓冲区
String _streamBuffer = '';
// 当前参数设置
double _volume = 1.0;
double _rate = 0.5;
double _pitch = 1.0;
FlutterTtsService() {
_flutterTts = FlutterTts();
_initTts();
}
/// 初始化TTS
Future<void> _initTts() async {
try {
// 设置完成回调
_flutterTts.setCompletionHandler(() {
_isSpeaking.value = false;
_eventController.add(TtsEvent(type: TtsEventType.completed));
});
// 设置开始回调
_flutterTts.setStartHandler(() {
_isSpeaking.value = true;
_eventController.add(TtsEvent(type: TtsEventType.started));
});
// 设置错误回调
_flutterTts.setErrorHandler((msg) {
_isSpeaking.value = false;
_eventController.add(TtsEvent.error(msg));
Logger.error('TTS错误: $msg');
});
// 设置取消回调
_flutterTts.setCancelHandler(() {
_isSpeaking.value = false;
_eventController.add(TtsEvent(type: TtsEventType.canceled));
});
// 设置默认语言
await _flutterTts.setLanguage(_language);
// 设置音量
await _flutterTts.setVolume(_volume);
// 设置语速
await _flutterTts.setSpeechRate(_rate);
// 设置音调
await _flutterTts.setPitch(_pitch);
Logger.info('FlutterTtsService 已初始化');
} catch (e) {
Logger.error('TTS初始化失败: $e');
}
}
@override
Future<bool> initialize({
List<String>? supportedLanguages,
}) async {
try {
// 获取可用的语音
await _loadVoices();
// 更新支持的语言列表(如果提供)
if (supportedLanguages != null && supportedLanguages.isNotEmpty) {
_language = supportedLanguages.first;
await _flutterTts.setLanguage(_language);
}
Logger.info('FlutterTtsService 初始化成功');
Logger.info('支持的语音: ${_supportedVoices.join(", ")}');
return true;
} catch (e) {
Logger.error('TTS初始化失败: $e');
return false;
}
}
/// 加载可用的语音
Future<void> _loadVoices() async {
try {
_supportedVoices.clear();
// 获取可用的语音
final voices = await _flutterTts.getVoices;
if (voices != null && voices is List) {
for (final voice in voices) {
if (voice is Map && voice.containsKey('name')) {
_supportedVoices.add(voice['name'].toString());
}
}
}
// 如果没有找到语音,添加默认语音
if (_supportedVoices.isEmpty) {
_supportedVoices.add('default');
}
// 设置默认语音
if (_currentVoice.value.isEmpty && _supportedVoices.isNotEmpty) {
_currentVoice.value = _supportedVoices.first;
}
} catch (e) {
Logger.error('加载TTS语音失败: $e');
// 添加默认语音
if (_supportedVoices.isEmpty) {
_supportedVoices.add('default');
_currentVoice.value = 'default';
}
}
}
@override
Future<bool> setVoice(String voiceName) async {
try {
// 检查语音是否存在
if (!_supportedVoices.contains(voiceName) && voiceName != 'default') {
Logger.warning('语音不存在: $voiceName,使用默认语音');
voiceName = _supportedVoices.isNotEmpty ? _supportedVoices.first : 'default';
}
// 设置语音
await _flutterTts.setVoice({"name": voiceName});
_currentVoice.value = voiceName;
Logger.info('设置TTS语音: $voiceName');
return true;
} catch (e) {
Logger.error('设置TTS语音失败: $e');
return false;
}
}
/// 设置语音参数
Future<bool> setSpeechParams({double? rate, double? pitch, double? volume}) async {
try {
if (volume != null) {
await _flutterTts.setVolume(volume);
_volume = volume;
}
if (rate != null) {
await _flutterTts.setSpeechRate(rate);
_rate = rate;
}
if (pitch != null) {
await _flutterTts.setPitch(pitch);
_pitch = pitch;
}
return true;
} catch (e) {
Logger.error('设置语音参数失败: $e');
// 发送错误事件
_eventController.add(TtsEvent.error('设置语音参数失败: $e'));
return false;
}
}
@override
Future<bool> speakOnce(String text) async {
if (text.isEmpty) return false;
try {
// 如果正在播放,先停止
if (_isSpeaking.value) {
await stop();
}
// 播放文本
await _flutterTts.speak(text);
return true;
} catch (e) {
// 发送错误事件
_eventController.add(TtsEvent.error(e.toString()));
Logger.error('TTS播放失败: $e');
return false;
}
}
@override
Future<bool> speakStream(String text) async {
if (text.isEmpty) return false;
try {
// 添加文本到缓冲区
_streamBuffer += text;
// 如果缓冲区为空,直接返回
if (_streamBuffer.isEmpty) return true;
// 使用正则表达式匹配句子,包括结束符号
// 匹配任意字符,直到遇到句子结束符号
final sentenceRegex = RegExp(
r'([^。.!!??;;:\n\r]+[。.!!??;;::\n\r]|[^。.!!??;;:\n\r]+(?:\.{3,}|…)|[^。.!!??;;:\n\r]+["」』])'
);
bool hasProcessed = false;
// 查找所有完整句子
final matches = sentenceRegex.allMatches(_streamBuffer);
final List<String> sentences = [];
int lastMatchEnd = 0;
for (final match in matches) {
// 提取完整句子(包含结束符号)
final sentence = match.group(1)?.trim();
if (sentence != null && sentence.isNotEmpty) {
sentences.add(sentence);
lastMatchEnd = match.end;
}
}
// 处理找到的句子
for (final sentence in sentences) {
_textQueue.add(_SpeechItem(
text: sentence,
rate: _rate,
pitch: _pitch,
volume: _volume,
));
hasProcessed = true;
}
// 更新缓冲区,只保留未完成的部分
if (lastMatchEnd > 0) {
_streamBuffer = _streamBuffer.substring(lastMatchEnd);
}
// 如果处理了文本并且队列未在处理中,开始处理队列
if (hasProcessed && !_isProcessingQueue) {
_processQueue();
}
return true;
} catch (e) {
Logger.error('处理流式文本失败: $e');
// 发送错误事件
_eventController.add(TtsEvent.error(e.toString()));
return false;
}
}
@override
Future<bool> flushStream() async {
if (_textQueue.isEmpty && _streamBuffer.isEmpty) return true;
try {
// 处理缓冲区中可能的完整句子
await speakStream('');
// 如果缓冲区仍有剩余文本,将其添加到播放队列
if (_streamBuffer.isNotEmpty && _streamBuffer.trim().isNotEmpty) {
_textQueue.add(_SpeechItem(
text: _streamBuffer,
rate: _rate,
pitch: _pitch,
volume: _volume,
));
// 清空缓冲区
_streamBuffer = '';
// 如果队列未在处理中,开始处理队列
if (!_isProcessingQueue) {
_processQueue();
}
}
Logger.info('等待TTS队列播放完成,剩余${_textQueue.length}条');
// 等待队列处理完成
while (_isProcessingQueue || _isSpeaking.value) {
await Future.delayed(const Duration(milliseconds: 100));
}
Logger.info('TTS队列播放完成');
return true;
} catch (e) {
Logger.error('等待TTS队列播放完成失败: $e');
// 发送错误事件
_eventController.add(TtsEvent.error(e.toString()));
return false;
}
}
/// 处理队列中的文本
Future<void> _processQueue() async {
if (_isProcessingQueue) return;
_isProcessingQueue = true;
try {
while (_textQueue.isNotEmpty) {
if (!_isSpeaking.value) {
final item = _textQueue.removeFirst();
// 如果参数有变化,更新参数
if (item.rate != _rate || item.pitch != _pitch || item.volume != _volume) {
await setSpeechParams(
rate: item.rate,
pitch: item.pitch,
volume: item.volume,
);
}
// 播放文本
await _flutterTts.speak(item.text);
// 等待开始播放
int attempts = 0;
while (!_isSpeaking.value && attempts < 10) {
await Future.delayed(const Duration(milliseconds: 50));
attempts++;
}
// 等待播放完成
while (_isSpeaking.value) {
await Future.delayed(const Duration(milliseconds: 100));
}
} else {
// 等待一小段时间再检查队列
await Future.delayed(const Duration(milliseconds: 100));
}
}
} catch (e) {
Logger.error('TTS处理队列失败: $e');
// 发送错误事件
_eventController.add(TtsEvent.error(e.toString()));
} finally {
_isProcessingQueue = false;
}
}
@override
Future<bool> stop() async {
try {
Logger.info('尝试停止TTS播放...');
// 清空队列
_textQueue.clear();
_streamBuffer = '';
// 如果正在播放,停止播放
if (_isSpeaking.value) {
// 先重置状态标志,避免处理队列循环继续等待
_isProcessingQueue = false;
// 执行停止
final result = await _flutterTts.stop();
Logger.info('TTS停止结果: $result');
// 短暂延迟等待状态更新
await Future.delayed(const Duration(milliseconds: 100));
// 强制更新状态
_isSpeaking.value = false;
// 发送取消事件
_eventController.add(TtsEvent(type: TtsEventType.canceled));
}
Logger.info('TTS停止播放完成');
return true;
} catch (e) {
Logger.error('TTS停止播放失败: $e');
// 即使出错也尝试强制重置状态
_isSpeaking.value = false;
_isProcessingQueue = false;
return false;
}
}
@override
Future<void> dispose() async {
try {
await stop();
_textQueue.clear();
// 关闭事件流控制器
await _eventController.close();
Logger.info('TTS资源已释放');
} catch (e) {
Logger.error('释放TTS资源失败: $e');
}
}
/// 处理AI返回的文本流,将其转换为语音
///
/// [textStream] 文本流
/// 返回一个Future,当流处理完成时完成
Future<bool> processAiTextStream(Stream<String> textStream) async {
try {
await for (final text in textStream) {
if (text.isEmpty) continue;
await speakStream(text);
}
// 处理完流后,刷新缓冲区中的剩余文本
await flushStream();
return true;
} catch (e) {
Logger.error('处理文本流时出错: $e');
return false;
}
}
}
/// 语音合成项目
class _SpeechItem {
final String text;
final double rate;
final double pitch;
final double volume;
_SpeechItem({
required this.text,
this.rate = 0.5,
this.pitch = 1.0,
this.volume = 1.0,
});
}