diff --git a/vetti-admin/src/main/java/com/vetti/socket/ChatWebSocketMultipleHandler.java b/vetti-admin/src/main/java/com/vetti/socket/ChatWebSocketMultipleHandler.java index c8440b4..94d6f3d 100644 --- a/vetti-admin/src/main/java/com/vetti/socket/ChatWebSocketMultipleHandler.java +++ b/vetti-admin/src/main/java/com/vetti/socket/ChatWebSocketMultipleHandler.java @@ -276,6 +276,14 @@ public class ChatWebSocketMultipleHandler { ByteBuffer outByteBuffer = convertFileToByteBuffer(pathUrl); //发送文件流数据 session.getBasicRemote().sendBinary(outByteBuffer); + try { + Thread.sleep(200); + }catch (Exception e){} + //提示已经结束 + Map dataText = new HashMap<>(); + dataText.put("type","voiceEnd"); + dataText.put("content",""); + session.getBasicRemote().sendText(JSONUtil.toJsonStr(dataText)); // 发送响应确认 log.info("已经成功发送了语音流给前端:{}", DateUtil.now()); } catch (Exception e) { @@ -291,11 +299,10 @@ public class ChatWebSocketMultipleHandler { * @param session 客户端会话ID */ private void sendTTSBuffer(String clientId, String content, Session session) { - String resultFileName = clientId + "_" + System.currentTimeMillis() + ".wav"; - String resultPathUrl = RuoYiConfig.getProfile() + VOICE_STORAGE_RESULT_DIR + resultFileName; +// String resultFileName = clientId + "_" + System.currentTimeMillis() + ".wav"; +// String resultPathUrl = RuoYiConfig.getProfile() + VOICE_STORAGE_RESULT_DIR + resultFileName; ElevenLabsStreamClient elevenLabsClient = SpringUtils.getBean(ElevenLabsStreamClient.class); - elevenLabsClient.handleTextToVoice(content, resultPathUrl,session); - + elevenLabsClient.handleTextToVoice(content,session); //持续返回数据流给客户端 log.info("发送语音流成功啦!!!!!!!"); // sendVoiceBuffer(resultPathUrl, session); @@ -691,8 +698,13 @@ public class ChatWebSocketMultipleHandler { questionResult = questionResult + content; } cacheQuestionResult.put(session.getId(),questionResult); + sendTTSBuffer(clientId,content,session); + //上面语音发送完成了,开始发送问题文本啦 // 实时输出内容 try{ + try { + Thread.sleep(300); + }catch (Exception e){} //把文本也给前端返回去 Map dataText = new HashMap<>(); dataText.put("type","question"); @@ -701,7 +713,7 @@ public class ChatWebSocketMultipleHandler { }catch (Exception e){ e.printStackTrace(); } - sendTTSBuffer(clientId,content,session); + } } diff --git a/vetti-common/src/main/java/com/vetti/common/ai/elevenLabs/ElevenLabsStreamClient.java b/vetti-common/src/main/java/com/vetti/common/ai/elevenLabs/ElevenLabsStreamClient.java index a68c574..1ba4790 100644 --- a/vetti-common/src/main/java/com/vetti/common/ai/elevenLabs/ElevenLabsStreamClient.java +++ b/vetti-common/src/main/java/com/vetti/common/ai/elevenLabs/ElevenLabsStreamClient.java @@ -1,8 +1,10 @@ package com.vetti.common.ai.elevenLabs; +import cn.hutool.json.JSONUtil; import com.google.gson.Gson; import com.vetti.common.ai.elevenLabs.vo.VoiceSettings; import com.vetti.common.ai.elevenLabs.vo.VoicesResponse; +import com.vetti.common.config.RuoYiConfig; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; @@ -17,6 +19,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.websocket.Session; +import java.io.ByteArrayInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -46,11 +49,10 @@ public class ElevenLabsStreamClient { * * @param text 要转换的文本 * @param voiceId 语音ID (可从ElevenLabs网站获取) - * @param outputFilePath 输出文件路径 * @throws IOException 网络请求或文件操作异常 */ - private void textToSpeech(String text, String voiceId, String outputFilePath, CloseableHttpClient httpClient, Session session) throws IOException { - HttpPost httpPost = new HttpPost(BASE_URL + "/text-to-speech/" + voiceId+"/stream?output_format=pcm_16000&optimize_streaming_latency=1"); + private void textToSpeech(String text, String voiceId, CloseableHttpClient httpClient, Session session) throws IOException { + HttpPost httpPost = new HttpPost(BASE_URL + "/text-to-speech/" + voiceId+"/stream?output_format=mp3_24000_48&optimize_streaming_latency=2"); httpPost.setHeader("xi-api-key", apiKey); httpPost.setHeader("Content-Type", "application/json"); @@ -65,13 +67,30 @@ public class ElevenLabsStreamClient { HttpEntity responseEntity = response.getEntity(); if (responseEntity != null) { try (InputStream inputStream = responseEntity.getContent();) { +// byte[] allData = inputStream.readAllBytes(); +// InputStream stableStream = new ByteArrayInputStream(allData); +// sendAudioStream(session,stableStream); byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, 0, bytesRead); - session.getAsyncRemote().sendBinary(byteBuffer); - log.info("正常语音发送出去语音流啦!!!"); +// log.info("字符流的长度大小:{}", bytesRead); + if(bytesRead != 1 && bytesRead != 2){ + session.getAsyncRemote().sendBinary(byteBuffer); + try { + Thread.sleep(20); + }catch (Exception e){} +// log.info("正常语音发送出去语音流啦!!!"); + } } + //返回结束点 + try { + Thread.sleep(100); + }catch (Exception e){} + Map dataText = new HashMap<>(); + dataText.put("type","voiceEnd"); + dataText.put("content",""); + session.getBasicRemote().sendText(JSONUtil.toJsonStr(dataText)); } } } @@ -99,16 +118,15 @@ public class ElevenLabsStreamClient { /** * 处理文本转换成语音文件 * @param inputText - * @param outputFile * @return */ - public String handleTextToVoice(String inputText, String outputFile, Session session){ + public void handleTextToVoice(String inputText,Session session){ CloseableHttpClient httpClient = HttpClients.createDefault(); try { // 使用第一个可用语音进行文本转语音(澳洲本地女声) // String firstVoiceId = "56bWURjYFHyYyVf490Dp"; String firstVoiceId = "56bWURjYFHyYyVf490Dp"; - textToSpeech(inputText, firstVoiceId, outputFile,httpClient,session); + textToSpeech(inputText, firstVoiceId,httpClient,session); } catch (IOException e) { e.printStackTrace(); } finally { @@ -118,6 +136,41 @@ public class ElevenLabsStreamClient { e.printStackTrace(); } } - return outputFile; } + + public void sendAudioStream(Session session, InputStream inputStream) throws IOException { + byte[] buffer = new byte[2048]; // 分片大小,可调 + final int[] bytesReadHolder = new int[1]; + // 定义一个递归任务 + final Runnable[] sender = new Runnable[1]; + sender[0] = () -> { + try { + bytesReadHolder[0] = inputStream.read(buffer); + // 结束条件 + if (bytesReadHolder[0] == -1) { + log.info("音频流发送完毕"); + Map dataText = new HashMap<>(); + dataText.put("type","voiceEnd"); + dataText.put("content",""); + session.getBasicRemote().sendText(JSONUtil.toJsonStr(dataText)); + return; + } + ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, 0, bytesReadHolder[0]); + session.getAsyncRemote().sendBinary(byteBuffer, result -> { + if (result.isOK()) { + log.info("正常语音发送出去语音流啦!!! 大小={} 字节", bytesReadHolder[0]); + // 当前块发送成功,继续发送下一块 + sender[0].run(); + } else { + log.error("发送失败: {}", result.getException().getMessage()); + } + }); + } catch (IOException e) { + log.error("流读取错误: {}", e.getMessage()); + } + }; + // 启动递归链 + sender[0].run(); + } + } 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 1ec34f9..7e0ac1e 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 @@ -106,7 +106,7 @@ public class OpenAiStreamClient { // 检查是否为结束标记 if (data.equals("[DONE]")) { listener.onMessage(bufferStr.toString()); - bufferStr.replace(0, bufferStr.length(), ""); + bufferStr.setLength(0); listener.onComplete(); break; } @@ -125,7 +125,7 @@ public class OpenAiStreamClient { if(punctuationStr.contains(content.trim())){ //说明有标点啦,直接返回 listener.onMessage(bufferStr.toString()); - bufferStr.replace(0, bufferStr.length(), ""); + bufferStr.setLength(0); } } // listener.onMessage(content); diff --git a/vetti-hotakes/target/maven-archiver/pom.properties b/vetti-hotakes/target/maven-archiver/pom.properties index fea5a2e..da14a64 100644 --- a/vetti-hotakes/target/maven-archiver/pom.properties +++ b/vetti-hotakes/target/maven-archiver/pom.properties @@ -1,5 +1,5 @@ #Generated by Maven -#Thu Nov 06 20:32:30 CST 2025 +#Thu Nov 27 13:22:25 CST 2025 artifactId=vetti-hotakes groupId=com.vetti version=3.9.0 diff --git a/vetti-hotakes/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/vetti-hotakes/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst index bfa138f..e69de29 100644 --- a/vetti-hotakes/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +++ b/vetti-hotakes/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -1,21 +0,0 @@ -com/vetti/hotake/mapper/HotakeCvInfoMapper.class -com/vetti/hotake/service/impl/HotakeSysFileServiceImpl.class -com/vetti/hotake/domain/dto/HotakeSysFileDto.class -com/vetti/hotake/service/IHotakeSysNoticeService.class -com/vetti/hotake/domain/dto/HotakeSysNoticeDto.class -com/vetti/hotake/mapper/HotakeSysFileMapper.class -com/vetti/hotake/mapper/HotakeSysNoticeMapper.class -com/vetti/hotake/domain/HotakeCvInfo.class -com/vetti/hotake/service/impl/HotakeSysNoticeServiceImpl.class -com/vetti/hotake/domain/dto/HotakeSysNoticeTypeNameDto.class -com/vetti/hotake/domain/vo/HotakeSysFileVo.class -com/vetti/hotake/domain/HotakeSysFile.class -com/vetti/hotake/domain/dto/HotakeSysNoticeViewDto.class -com/vetti/hotake/domain/HotakeProblemBaseInfo.class -com/vetti/hotake/service/IHotakeProblemBaseInfoService.class -com/vetti/hotake/service/impl/HotakeProblemBaseInfoServiceImpl.class -com/vetti/hotake/domain/HotakeSysNotice.class -com/vetti/hotake/mapper/HotakeProblemBaseInfoMapper.class -com/vetti/hotake/service/IHotakeSysFileService.class -com/vetti/hotake/service/impl/HotakeCvInfoServiceImpl.class -com/vetti/hotake/service/IHotakeCvInfoService.class diff --git a/vetti-hotakes/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/vetti-hotakes/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst index 7947016..dd70b57 100644 --- a/vetti-hotakes/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +++ b/vetti-hotakes/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -1,23 +1,39 @@ +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/mapper/HotakeInterviewDetailMapper.java /Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/mapper/HotakeSysNoticeMapper.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeMeetingCalendarInfoServiceImpl.java /Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/mapper/HotakeSysFileMapper.java /Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeSysFileServiceImpl.java /Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/dto/HotakeSysNoticeTypeNameDto.java -/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/mapper/HotakeCvInfoMapper.java /Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/IHotakeSysFileService.java /Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/HotakeCvInfo.java -/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeProblemBaseInfoServiceImpl.java -/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/HotakeSysNotice.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/mapper/HotakeInterviewInfoMapper.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeMeetingCalendarDetailServiceImpl.java /Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/HotakeSysFile.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeInterviewDetailServiceImpl.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeInterviewInfoServiceImpl.java /Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeSysNoticeServiceImpl.java -/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/vo/HotakeSysFileVo.java /Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/mapper/HotakeProblemBaseInfoMapper.java -/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/dto/HotakeSysNoticeViewDto.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/HotakeMeetingCalendarDetail.java /Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/mapper/HotakeSysNoticeTypeMapper.java -/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/dto/HotakeSysNoticeDto.java /Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/HotakeSysNoticeType.java -/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/IHotakeSysNoticeService.java -/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/IHotakeCvInfoService.java /Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/HotakeProblemBaseInfo.java /Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeCvInfoServiceImpl.java /Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/IHotakeProblemBaseInfoService.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/HotakeMeetingCalendarInfo.java /Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/dto/HotakeSysFileDto.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/mapper/HotakeMeetingCalendarInfoMapper.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/IHotakeInterviewDetailService.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/mapper/HotakeCvInfoMapper.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeProblemBaseInfoServiceImpl.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/HotakeInterviewDetail.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/HotakeSysNotice.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/vo/HotakeSysFileVo.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/IHotakeInterviewInfoService.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/IHotakeMeetingCalendarInfoService.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/dto/HotakeSysNoticeViewDto.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/mapper/HotakeMeetingCalendarDetailMapper.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/dto/HotakeSysNoticeDto.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/domain/HotakeInterviewInfo.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/IHotakeSysNoticeService.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/IHotakeCvInfoService.java +/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/vetti-hotakes/src/main/java/com/vetti/hotake/service/IHotakeMeetingCalendarDetailService.java