AI 面试业务逻辑初始化
This commit is contained in:
@@ -4,61 +4,43 @@ import com.vetti.common.ai.elevenLabs.ElevenLabsClient;
|
|||||||
import com.vetti.common.ai.gpt.ChatGPTClient;
|
import com.vetti.common.ai.gpt.ChatGPTClient;
|
||||||
import com.vetti.common.ai.whisper.WhisperClient;
|
import com.vetti.common.ai.whisper.WhisperClient;
|
||||||
import com.vetti.common.config.RuoYiConfig;
|
import com.vetti.common.config.RuoYiConfig;
|
||||||
|
import com.vetti.common.utils.spring.SpringUtils;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.sound.sampled.AudioFileFormat;
|
|
||||||
import javax.sound.sampled.AudioFormat;
|
|
||||||
import javax.sound.sampled.AudioInputStream;
|
|
||||||
import javax.sound.sampled.AudioSystem;
|
|
||||||
import javax.websocket.*;
|
import javax.websocket.*;
|
||||||
import javax.websocket.server.PathParam;
|
import javax.websocket.server.PathParam;
|
||||||
import javax.websocket.server.ServerEndpoint;
|
import javax.websocket.server.ServerEndpoint;
|
||||||
import java.io.*;
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 语音面试 web处理器
|
* 语音面试 web处理器
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ServerEndpoint("/voice-websocket/{clientId}")
|
@ServerEndpoint("/voice-websocket/{clientId}")
|
||||||
@Component
|
@Component
|
||||||
public class ChatWebSocketHandler {
|
public class ChatWebSocketHandler {
|
||||||
|
|
||||||
// 音频参数配置 - 根据实际发送的音频流参数调整
|
|
||||||
private static final float SAMPLE_RATE = 16000.0f; // 采样率
|
|
||||||
private static final int SAMPLE_SIZE_IN_BITS = 16; // 采样位数
|
|
||||||
private static final int CHANNELS = 1; // 声道数 (1=单声道, 2=立体声)
|
|
||||||
private static final boolean SIGNED = true; // 是否有符号
|
|
||||||
private static final boolean BIG_ENDIAN = false; // 字节序
|
|
||||||
|
|
||||||
// 语音文件保存目录
|
// 语音文件保存目录
|
||||||
private static final String VOICE_STORAGE_DIR = "/voice_files/";
|
private static final String VOICE_STORAGE_DIR = "/voice_files/";
|
||||||
|
|
||||||
// 语音结果文件保存目录
|
// 语音结果文件保存目录
|
||||||
private static final String VOICE_STORAGE_RESULT_DIR = "/voice_result_files/";
|
private static final String VOICE_STORAGE_RESULT_DIR = "/voice_result_files/";
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ElevenLabsClient elevenLabsClient;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ChatGPTClient chatGPTClient;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private WhisperClient whisperClient;
|
|
||||||
|
|
||||||
|
|
||||||
public ChatWebSocketHandler() {
|
public ChatWebSocketHandler() {
|
||||||
// 初始化存储目录
|
// 初始化存储目录
|
||||||
File dir = new File(RuoYiConfig.getProfile()+VOICE_STORAGE_DIR);
|
File dir = new File(RuoYiConfig.getProfile() + VOICE_STORAGE_DIR);
|
||||||
if (!dir.exists()) {
|
if (!dir.exists()) {
|
||||||
dir.mkdirs();
|
dir.mkdirs();
|
||||||
}
|
}
|
||||||
|
|
||||||
File resultDir = new File(RuoYiConfig.getProfile()+VOICE_STORAGE_RESULT_DIR);
|
File resultDir = new File(RuoYiConfig.getProfile() + VOICE_STORAGE_RESULT_DIR);
|
||||||
if (!resultDir.exists()) {
|
if (!resultDir.exists()) {
|
||||||
resultDir.mkdirs();
|
resultDir.mkdirs();
|
||||||
}
|
}
|
||||||
@@ -67,7 +49,7 @@ public class ChatWebSocketHandler {
|
|||||||
// 连接建立时调用
|
// 连接建立时调用
|
||||||
@OnOpen
|
@OnOpen
|
||||||
public void onOpen(Session session, @PathParam("clientId") String clientId) {
|
public void onOpen(Session session, @PathParam("clientId") String clientId) {
|
||||||
log.info("WebSocket 链接已建立:{}",clientId);
|
log.info("WebSocket 链接已建立:{}", clientId);
|
||||||
//创建会话
|
//创建会话
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -88,37 +70,43 @@ public class ChatWebSocketHandler {
|
|||||||
// 接收二进制消息(流数据)
|
// 接收二进制消息(流数据)
|
||||||
@OnMessage
|
@OnMessage
|
||||||
public void onBinaryMessage(Session session, @PathParam("clientId") String clientId, ByteBuffer byteBuffer) {
|
public void onBinaryMessage(Session session, @PathParam("clientId") String clientId, ByteBuffer byteBuffer) {
|
||||||
try{
|
try {
|
||||||
log.info("客户端ID为:{}",clientId);
|
log.info("客户端ID为:{}", clientId);
|
||||||
// 处理二进制流数据
|
// 处理二进制流数据
|
||||||
byte[] data = new byte[byteBuffer.remaining()];
|
byte[] bytes = new byte[byteBuffer.remaining()];
|
||||||
|
//从缓冲区中读取数据并存储到指定的字节数组中
|
||||||
|
byteBuffer.get(bytes);
|
||||||
// 生成唯一文件名
|
// 生成唯一文件名
|
||||||
String fileName = clientId + "_" + System.currentTimeMillis() + ".mp3";
|
String fileName = clientId + "_" + System.currentTimeMillis() + ".webm";
|
||||||
String pathUrl = RuoYiConfig.getProfile()+VOICE_STORAGE_DIR + fileName;
|
String pathUrl = RuoYiConfig.getProfile()+VOICE_STORAGE_DIR + fileName;
|
||||||
log.info("文件路径为:{}",pathUrl);
|
// String pathUrl = "/Users/wangxiangshun/Desktop/0.8733346782733291.webm";
|
||||||
saveAsWebM(data,pathUrl);
|
log.info("文件路径为:{}", pathUrl);
|
||||||
// //拿到文件进行文字转换
|
saveAsWebM(bytes, pathUrl);
|
||||||
// String resultText = whisperClient.handleVoiceToText(pathUrl);
|
//拿到文件进行文字转换
|
||||||
// //把提问的文字发送给CPT
|
WhisperClient whisperClient = SpringUtils.getBean(WhisperClient.class);
|
||||||
// String resultMsg = chatGPTClient.handleAiChat(resultText);
|
String resultText = whisperClient.handleVoiceToText(pathUrl);
|
||||||
// //把结果文字转成语音文件
|
//把提问的文字发送给CPT
|
||||||
// //生成文件
|
ChatGPTClient chatGPTClient = SpringUtils.getBean(ChatGPTClient.class);
|
||||||
// // 生成唯一文件名
|
String resultMsg = chatGPTClient.handleAiChat(resultText);
|
||||||
// String resultFileName = clientId + "_" + System.currentTimeMillis() + ".webm";
|
//把结果文字转成语音文件
|
||||||
// String resultPathUrl = RuoYiConfig.getProfile()+VOICE_STORAGE_DIR + resultFileName;
|
//生成文件
|
||||||
// elevenLabsClient.handleTextToVoice(resultMsg,resultPathUrl);
|
// 生成唯一文件名
|
||||||
|
String resultFileName = clientId + "_" + System.currentTimeMillis() + ".webm";
|
||||||
|
String resultPathUrl = RuoYiConfig.getProfile() + VOICE_STORAGE_RESULT_DIR + resultFileName;
|
||||||
|
ElevenLabsClient elevenLabsClient = SpringUtils.getBean(ElevenLabsClient.class);
|
||||||
|
elevenLabsClient.handleTextToVoice(resultMsg, resultPathUrl);
|
||||||
//把语音文件转换成流,发送给前端
|
//把语音文件转换成流,发送给前端
|
||||||
System.out.println("接收到二进制数据,长度: " + data.length + " bytes");
|
System.out.println("接收到二进制数据,长度: " + bytes.length + " bytes");
|
||||||
try {
|
try {
|
||||||
//文件转换成文件流
|
//文件转换成文件流
|
||||||
// ByteBuffer outByteBuffer = convertFileToByteBuffer(resultPathUrl);
|
ByteBuffer outByteBuffer = convertFileToByteBuffer(resultPathUrl);
|
||||||
// session.getBasicRemote().sendBinary(outByteBuffer);
|
session.getBasicRemote().sendBinary(outByteBuffer);
|
||||||
// 发送响应确认
|
// 发送响应确认
|
||||||
session.getBasicRemote().sendText("已收到二进制数据,长度: " + data.length);
|
session.getBasicRemote().sendText("已收到二进制数据,长度: " + bytes.length);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}catch (Exception e){
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,6 +126,7 @@ public class ChatWebSocketHandler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 将字节数组保存为WebM文件
|
* 将字节数组保存为WebM文件
|
||||||
|
*
|
||||||
* @param byteData 包含WebM数据的字节数组
|
* @param byteData 包含WebM数据的字节数组
|
||||||
* @param filePath 目标文件路径
|
* @param filePath 目标文件路径
|
||||||
* @return 操作是否成功
|
* @return 操作是否成功
|
||||||
@@ -181,43 +170,13 @@ public class ChatWebSocketHandler {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 将接收的音频数据保存为WAV文件
|
|
||||||
*/
|
|
||||||
private void saveAudioToFile(byte[] audioData,String pathUrl) {
|
|
||||||
if (audioData == null || audioData.length == 0) {
|
|
||||||
System.out.println("没有接收到音频数据,不保存文件");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// 合并所有音频数据
|
|
||||||
// 创建音频格式
|
|
||||||
AudioFormat format = new AudioFormat(
|
|
||||||
SAMPLE_RATE,
|
|
||||||
SAMPLE_SIZE_IN_BITS,
|
|
||||||
CHANNELS,
|
|
||||||
SIGNED,
|
|
||||||
BIG_ENDIAN
|
|
||||||
);
|
|
||||||
// 创建音频输入流
|
|
||||||
ByteArrayInputStream bais = new ByteArrayInputStream(audioData);
|
|
||||||
AudioInputStream ais = new AudioInputStream(bais, format, audioData.length / format.getFrameSize());
|
|
||||||
// 保存为WAV文件
|
|
||||||
File outputFile = new File(pathUrl);
|
|
||||||
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, outputFile);
|
|
||||||
System.out.println("音频文件保存成功: " + outputFile.getAbsolutePath());
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.err.println("保存音频文件失败: " + e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File 转换成 ByteBuffer
|
* File 转换成 ByteBuffer
|
||||||
|
*
|
||||||
* @param fileUrl 文件路径
|
* @param fileUrl 文件路径
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private ByteBuffer convertFileToByteBuffer(String fileUrl){
|
private ByteBuffer convertFileToByteBuffer(String fileUrl) {
|
||||||
File file = new File(fileUrl);
|
File file = new File(fileUrl);
|
||||||
// 使用RandomAccessFile获取FileChannel
|
// 使用RandomAccessFile获取FileChannel
|
||||||
try (RandomAccessFile raf = new RandomAccessFile(file, "r");
|
try (RandomAccessFile raf = new RandomAccessFile(file, "r");
|
||||||
@@ -229,7 +188,7 @@ public class ChatWebSocketHandler {
|
|||||||
// 切换为读模式
|
// 切换为读模式
|
||||||
buffer.flip();
|
buffer.flip();
|
||||||
return buffer;
|
return buffer;
|
||||||
}catch (Exception e){
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ whisper:
|
|||||||
apiUrl: https://api.openai.com/v1/audio/transcriptions
|
apiUrl: https://api.openai.com/v1/audio/transcriptions
|
||||||
model: whisper-1
|
model: whisper-1
|
||||||
apiKey: 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
|
apiKey: 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
|
||||||
language: zh
|
language: en
|
||||||
|
|
||||||
# AI 聊天
|
# AI 聊天
|
||||||
chatGpt:
|
chatGpt:
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ whisper:
|
|||||||
apiUrl: https://api.openai.com/v1/audio/transcriptions
|
apiUrl: https://api.openai.com/v1/audio/transcriptions
|
||||||
model: whisper-1
|
model: whisper-1
|
||||||
apiKey: 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
|
apiKey: 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
|
||||||
language: zh
|
language: en
|
||||||
|
|
||||||
# AI 聊天
|
# AI 聊天
|
||||||
chatGpt:
|
chatGpt:
|
||||||
|
|||||||
Reference in New Issue
Block a user