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 _supportedVoices = []; List 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 _eventController = StreamController.broadcast(); @override Stream 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 _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 initialize({ List? 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 _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 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 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 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 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 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 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 _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 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 dispose() async { try { await stop(); _textQueue.clear(); // 关闭事件流控制器 await _eventController.close(); Logger.info('TTS资源已释放'); } catch (e) { Logger.error('释放TTS资源失败: $e'); } } /// 处理AI返回的文本流,将其转换为语音 /// /// [textStream] 文本流 /// 返回一个Future,当流处理完成时完成 Future processAiTextStream(Stream 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, }); }