TTS 返回语音优化
This commit is contained in:
@@ -144,8 +144,10 @@ public class ChatWebSocketHandler {
|
|||||||
log.info("3、开始进行AI回答时间:{}",System.currentTimeMillis()/1000);
|
log.info("3、开始进行AI回答时间:{}",System.currentTimeMillis()/1000);
|
||||||
//持续返回数据流给客户端
|
//持续返回数据流给客户端
|
||||||
try {
|
try {
|
||||||
|
String resultOutPathUrl = RuoYiConfig.getProfile() + VOICE_STORAGE_RESULT_DIR + "110_"+resultFileName;
|
||||||
|
handleVoice(resultPathUrl,resultOutPathUrl);
|
||||||
//文件转换成文件流
|
//文件转换成文件流
|
||||||
ByteBuffer outByteBuffer = convertFileToByteBuffer(resultPathUrl);
|
ByteBuffer outByteBuffer = convertFileToByteBuffer(resultOutPathUrl);
|
||||||
//发送文件流数据
|
//发送文件流数据
|
||||||
session.getBasicRemote().sendBinary(outByteBuffer);
|
session.getBasicRemote().sendBinary(outByteBuffer);
|
||||||
// 发送响应确认
|
// 发送响应确认
|
||||||
@@ -404,6 +406,50 @@ public class ChatWebSocketHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleVoice(String inputPath,String outputPath){
|
||||||
|
double trimMs = 270; // 要去掉的尾部时长(毫秒)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 解析音频格式和总长度
|
||||||
|
AudioInputStream audioIn = AudioSystem.getAudioInputStream(new File(inputPath));
|
||||||
|
AudioFormat format = audioIn.getFormat();
|
||||||
|
long totalBytes = audioIn.getFrameLength() * format.getFrameSize(); // 总字节数
|
||||||
|
|
||||||
|
// 2. 计算300毫秒对应的字节数
|
||||||
|
float sampleRate = format.getSampleRate(); // 采样率(Hz)
|
||||||
|
int frameSize = format.getFrameSize(); // 每帧字节数(位深/8 * 声道数)
|
||||||
|
double trimSeconds = trimMs / 1000.0; // 转换为秒
|
||||||
|
long trimBytes = (long) (sampleRate * trimSeconds * frameSize); // 要去掉的字节数
|
||||||
|
|
||||||
|
// 3. 计算需要保留的字节数(避免负数)
|
||||||
|
long keepBytes = Math.max(0, totalBytes - trimBytes);
|
||||||
|
if (keepBytes == 0) {
|
||||||
|
System.out.println("音频长度小于300毫秒,无法截断");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 读取并保留前半部分(去掉最后300毫秒)
|
||||||
|
try (InputStream in = new FileInputStream(inputPath);
|
||||||
|
OutputStream out = new FileOutputStream(outputPath)) {
|
||||||
|
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
long totalRead = 0;
|
||||||
|
int bytesRead;
|
||||||
|
|
||||||
|
while (totalRead < keepBytes && (bytesRead = in.read(buffer)) != -1) {
|
||||||
|
long remaining = keepBytes - totalRead;
|
||||||
|
int writeBytes = (remaining < bytesRead) ? (int) remaining : bytesRead;
|
||||||
|
out.write(buffer, 0, writeBytes);
|
||||||
|
totalRead += writeBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("处理完成,去掉了最后" + trimMs + "毫秒,保留了" + totalRead + "字节");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (UnsupportedAudioFileException | IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class AiCommonController extends BaseController
|
|||||||
@GetMapping("/handleTextToVice")
|
@GetMapping("/handleTextToVice")
|
||||||
public AjaxResult handleTextToVice()
|
public AjaxResult handleTextToVice()
|
||||||
{
|
{
|
||||||
elevenLabsClient.handleTextToVoice("我只是测试的文本转换成语音","/Users/wangxiangshun/Desktop/临时文件/output1112.mp3");
|
elevenLabsClient.handleTextToVoice("Hello ! I can","/Users/wangxiangshun/Desktop/临时文件/output1112.wav");
|
||||||
return success();
|
return success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -145,7 +145,8 @@ verification:
|
|||||||
# 文本转语音
|
# 文本转语音
|
||||||
elevenLabs:
|
elevenLabs:
|
||||||
baseUrl: https://api.elevenlabs.io/v1
|
baseUrl: https://api.elevenlabs.io/v1
|
||||||
apiKey: sk_5240d8f56cb1eb5225fffcf903f62479884d1af5b3de6812
|
# apiKey: sk_5240d8f56cb1eb5225fffcf903f62479884d1af5b3de6812
|
||||||
|
apiKey: sk_88f5a560e1bbde0e5b8b6b6eb1812163a98bfb98554acbec
|
||||||
modelId: eleven_turbo_v2_5
|
modelId: eleven_turbo_v2_5
|
||||||
# 语音转文本
|
# 语音转文本
|
||||||
whisper:
|
whisper:
|
||||||
|
|||||||
@@ -115,21 +115,22 @@ public class OpenAiStreamClient {
|
|||||||
.getJSONObject("delta")
|
.getJSONObject("delta")
|
||||||
.getStr("content");
|
.getStr("content");
|
||||||
|
|
||||||
if (content != null && !content.isEmpty()) {
|
// if (content != null && !content.isEmpty()) {
|
||||||
if(punctuationSet.contains(content)){
|
// if(punctuationSet.contains(content)){
|
||||||
//说明有标点啦,直接返回
|
// //说明有标点啦,直接返回
|
||||||
bufferStr.append(content);
|
// bufferStr.append(content);
|
||||||
listener.onMessage(bufferStr.toString());
|
// listener.onMessage(bufferStr.toString());
|
||||||
}else{
|
// }else{
|
||||||
//加入缓冲区
|
// //加入缓冲区
|
||||||
if(StrUtil.isEmpty(bufferStr.toString())){
|
// if(StrUtil.isEmpty(bufferStr.toString())){
|
||||||
bufferStr.append(content);
|
// bufferStr.append(content);
|
||||||
}else {
|
// }else {
|
||||||
bufferStr.append(" ").append(content);
|
// bufferStr.append(" ").append(content);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
}
|
// }
|
||||||
|
listener.onMessage(content);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
listener.onError(new IOException("Parse error: " + e.getMessage()));
|
listener.onError(new IOException("Parse error: " + e.getMessage()));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user