From a3ef5d195969bfcb754d549b5fb2c5d5dad895ea Mon Sep 17 00:00:00 2001 From: wangxiangshun Date: Sun, 19 Oct 2025 17:01:13 +0800 Subject: [PATCH] =?UTF-8?q?TTS=20=E8=BF=94=E5=9B=9E=E8=AF=AD=E9=9F=B3?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vetti/socket/ChatWebSocketHandler.java | 44 ------------------- vetti-common/pom.xml | 1 + .../common/ai/gpt/OpenAiStreamClient.java | 26 ++++++++++- 3 files changed, 26 insertions(+), 45 deletions(-) diff --git a/vetti-admin/src/main/java/com/vetti/socket/ChatWebSocketHandler.java b/vetti-admin/src/main/java/com/vetti/socket/ChatWebSocketHandler.java index ecb57ca..00fa5cd 100644 --- a/vetti-admin/src/main/java/com/vetti/socket/ChatWebSocketHandler.java +++ b/vetti-admin/src/main/java/com/vetti/socket/ChatWebSocketHandler.java @@ -144,11 +144,6 @@ public class ChatWebSocketHandler { log.info("3、开始进行AI回答时间:{}",System.currentTimeMillis()/1000); //持续返回数据流给客户端 try { - File inputFile = new File(resultPathUrl); - File outputFile = new File(resultPathUrl); - // 设置去除尾部的秒数 - float removeSeconds = 0.25f; // 去除最后5秒 - trimEndByTime(inputFile, outputFile, removeSeconds); //文件转换成文件流 ByteBuffer outByteBuffer = convertFileToByteBuffer(resultPathUrl); //发送文件流数据 @@ -410,44 +405,5 @@ public class ChatWebSocketHandler { } - // 裁剪音频文件,去除最后多少秒 - public void trimEndByTime(File inputFile, File outputFile, float removeSeconds) { - try { - // 获取音频输入流 - AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(inputFile); - AudioFormat format = audioInputStream.getFormat(); - - // 获取音频文件的总帧数 - long totalFrames = audioInputStream.getFrameLength(); - - // 计算音频文件的总时长(秒) - float totalDuration = totalFrames / format.getSampleRate(); - - // 计算新的结束位置(去除指定的时间后) - long newEndFrame = (long) ((totalDuration - removeSeconds) * format.getSampleRate()); - - // 确保裁剪的结束帧在合理范围内 - if (newEndFrame < 0) { - System.out.println("去除的时间超过了音频的总时长"); - return; - } - - // 创建一个新的输入流,裁剪音频数据 - AudioInputStream trimmedStream = new AudioInputStream( - audioInputStream, - format, - newEndFrame - ); - - // 创建新的文件并保存裁剪后的音频 - AudioSystem.write(trimmedStream, AudioFileFormat.Type.WAVE, outputFile); - System.out.println("裁剪后的音频已保存到: " + outputFile.getAbsolutePath()); - - audioInputStream.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } diff --git a/vetti-common/pom.xml b/vetti-common/pom.xml index 9da9e72..3b213c1 100644 --- a/vetti-common/pom.xml +++ b/vetti-common/pom.xml @@ -169,6 +169,7 @@ org.java-websocket Java-WebSocket + org.apache.tomcat.embed tomcat-embed-websocket diff --git a/vetti-common/src/main/java/com/vetti/common/ai/gpt/OpenAiStreamClient.java b/vetti-common/src/main/java/com/vetti/common/ai/gpt/OpenAiStreamClient.java index 12eb503..82858ee 100644 --- a/vetti-common/src/main/java/com/vetti/common/ai/gpt/OpenAiStreamClient.java +++ b/vetti-common/src/main/java/com/vetti/common/ai/gpt/OpenAiStreamClient.java @@ -1,5 +1,6 @@ package com.vetti.common.ai.gpt; +import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.vetti.common.ai.gpt.service.OpenAiStreamListenerService; @@ -9,7 +10,9 @@ import org.springframework.stereotype.Component; import java.io.IOException; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; /** @@ -30,6 +33,12 @@ public class OpenAiStreamClient { @Value("${chatGpt.role}") private String role; + // 定义作为分割点的标点符号集合 + private final Set punctuationSet = new HashSet<>() {{ + add('。'); add('?'); add('!'); add(';'); // 中文标点 + add('.'); add('?'); add('!'); add(';'); // 英文标点 + }}; + /** * 发送流式请求 * @@ -42,6 +51,9 @@ public class OpenAiStreamClient { .readTimeout(60, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .build(); + // 文本缓冲区 + StringBuffer bufferStr = new StringBuffer(); + // 构建请求参数 Map requestBody = new HashMap<>(); requestBody.put("model", model); @@ -104,7 +116,19 @@ public class OpenAiStreamClient { .getStr("content"); if (content != null && !content.isEmpty()) { - listener.onMessage(content); + if(punctuationSet.contains(content)){ + //说明有标点啦,直接返回 + bufferStr.append(content); + listener.onMessage(bufferStr.toString()); + }else{ + //加入缓冲区 + if(StrUtil.isEmpty(bufferStr.toString())){ + bufferStr.append(content); + }else { + bufferStr.append(" ").append(content); + } + } + } } catch (Exception e) { listener.onError(new IOException("Parse error: " + e.getMessage()));