修改TTS前的分句和全屏功能

1、TTS前的分句已经可以正常移除图片url了
2、打开full_screen中的全屏后延迟出现“欢迎语”,避免报错
---------
Issue:
1、分句后的文字提交tts,会出现websocket空闲时间过长,导致语音提前结束的情况
This commit is contained in:
2025-08-19 11:44:18 +08:00
parent e9ba8d03db
commit 486e16cfeb
2 changed files with 59 additions and 23 deletions

View File

@@ -67,12 +67,12 @@ class _FullScreenState extends State<FullScreen> {
_scrollToBottom();
});
if (messageService.messages.isEmpty) {
// Future.delayed(const Duration(milliseconds: 500), () {
Future.delayed(const Duration(milliseconds: 500), () {
String text = Intl.getCurrentLocale().startsWith('zh')
? "您好,我是众众,请问有什么可以帮您?"
: "Hi, I'm Zhongzhong, may I help you ? ";
messageService.addMessage(text, false, MessageStatus.normal);
// });
});
}
return ChatBox(
scrollController: _scrollController,

View File

@@ -35,7 +35,59 @@ class ChatSseService {
print('初始化用户ID: $_cachedUserId');
}
String responseText = '';
String tempText = '';
StringBuffer buffer = StringBuffer();
// 英文分句符
final enEnders = RegExp(r'[.!?]');
// 中文分句符
final zhEnders = RegExp(r'[。!?]');
// 专有名词保护
String protectIDUNYX(String text) => text.replaceAll('ID.UNYX', 'ID_UNYX');
String restoreIDUNYX(String text) => text.replaceAll('ID_UNYX', 'ID.UNYX');
Future<void> processBuffer(bool isChinese) async {
String txt = buffer.toString();
// 先过滤 markdown 图片语法
int imgStart = txt.indexOf('![');
while (imgStart != -1) {
int imgEnd = txt.indexOf(')', imgStart);
if (imgEnd == -1) {
// 图片语法未闭合,缓存剩余部分
buffer.clear();
buffer.write(txt.substring(imgStart));
return;
}
// 移除图片语法
txt = txt.substring(0, imgStart) + txt.substring(imgEnd + 1);
imgStart = txt.indexOf('![');
}
// 彻底移除 markdown 有序/无序列表序号(如 1.、2.、-、*、+
txt = txt.replaceAll(RegExp(r'(^|\n)[ \t]*[0-9]+\.[ \t]*'), '\n');
txt = txt.replaceAll(RegExp(r'(^|\n)[ \t]*[-\*\+][ \t]+'), '\n');
// 分句符
RegExp enders = isChinese ? zhEnders : enEnders;
int lastEnd = 0;
// 本地缓存句子
List<String> sentences = [];
for (final match in enders.allMatches(txt)) {
int endIndex = match.end;
String sentence = txt.substring(lastEnd, endIndex).trim();
if (sentence.isNotEmpty) {
sentences.add(sentence);
}
lastEnd = endIndex;
}
// 只在达到完整句子时调用 TtsUtil.send
for (final s in sentences) {
String ttsStr=CommonUtil.cleanText(s, true);
// print("发送数据到TTS: $ttsStr");
TtsUtil.send(ttsStr);
}
// 缓存剩余不完整部分
String remain = txt.substring(lastEnd).trim();
buffer.clear();
if (remain.isNotEmpty) {
buffer.write(remain);
}
}
_currentClient = HttpClient();
try {
final chatUri = Uri.parse('http://143.64.185.20:18606/chat');
@@ -62,7 +114,7 @@ class ChatSseService {
if (line.startsWith('data:')) {
final jsonStr = line.substring(5).trim();
if (jsonStr == '[DONE]' || jsonStr.contains('message_end')) {
TtsUtil.complete();
await processBuffer(isChinese);
onStreamResponse(messageId, responseText, true);
break;
}
@@ -78,23 +130,8 @@ class ChatSseService {
if (_isAborted) {
break;
}
tempText += textChunk;
if (tempText.contains("ID.")) {
tempText = tempText.replaceAllMapped(
RegExp(r'ID\.', caseSensitive: false), (m) => 'ID ');
}
int endIndex = _getCompleteTextEndIndex(tempText);
String completeText = CommonUtil.cleanText(tempText.substring(0, endIndex).trim(), true);
if (completeText.isNotEmpty) {
if (_isFirstData && _startTtsFuture != null) {
await _startTtsFuture;
_isFirstData = false;
}
completeText += isChinese ? '' : ',';
print("----------------------Send text: " + completeText);
TtsUtil.send(completeText);
}
tempText = tempText.substring(endIndex).trim();
buffer.write(textChunk);
await processBuffer(isChinese);
responseText += textChunk;
onStreamResponse(messageId, responseText, false);
}
@@ -109,7 +146,6 @@ class ChatSseService {
} catch (e) {
// todo
} finally {
print("------------------------------finally");
resetRequest();
}
}