106 lines
3.7 KiB
Java
106 lines
3.7 KiB
Java
package com.realtime.service;
|
|
|
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import com.realtime.config.DoubaoVoiceConfig;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import okhttp3.*;
|
|
import okio.ByteString;
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.web.socket.BinaryMessage;
|
|
import org.springframework.web.socket.WebSocketSession;
|
|
|
|
import java.io.IOException;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.concurrent.TimeUnit;
|
|
@Slf4j
|
|
@Service
|
|
public class DoubaoVoiceService {
|
|
private final DoubaoVoiceConfig config;
|
|
private final OkHttpClient okHttpClient;
|
|
private WebSocket doubaoWebSocket; // 与豆包 API 的 WebSocket 连接
|
|
|
|
public DoubaoVoiceService(DoubaoVoiceConfig config) {
|
|
this.config = config;
|
|
this.okHttpClient = new OkHttpClient.Builder()
|
|
.readTimeout(0, TimeUnit.MILLISECONDS) // 实时流禁用超时
|
|
.build();
|
|
}
|
|
|
|
/**
|
|
* 建立与豆包实时语音接口的 WebSocket 连接
|
|
* @param clientSession 客户端的 WebSocket 会话(用于将结果返回给客户端)
|
|
*/
|
|
public void connect(WebSocketSession clientSession) {
|
|
|
|
Request request = new Request.Builder()
|
|
.addHeader("X-Api-App-ID",config.getAppId())
|
|
.addHeader("X-Api-Access-Key",config.getAccessToken())
|
|
.addHeader("X-Api-Resource-Id","volc.speech.dialog").addHeader("X-Api-App-Key",config.getApiKey())
|
|
.url("wss://openspeech.bytedance.com/api/v3/realtime/dialogue")
|
|
.build();
|
|
|
|
// 连接豆包 API 并处理回调
|
|
doubaoWebSocket = okHttpClient.newWebSocket(request, new WebSocketListener() {
|
|
@Override
|
|
public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) {
|
|
// 连接成功:可发送初始化参数(如语音类型、采样率)
|
|
sendInitParams(webSocket);
|
|
}
|
|
|
|
@Override
|
|
public void onMessage(@NotNull WebSocket webSocket, ByteString bytes) {
|
|
// 接收豆包返回的实时结果(如识别文本、合成语音)
|
|
try {
|
|
// 将结果转发给客户端
|
|
clientSession.sendMessage(new BinaryMessage(bytes.toByteArray()));
|
|
} catch (IOException e) {
|
|
log.error("转发结果到客户端失败", e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
|
|
System.out.println(t.getMessage());
|
|
log.error("豆包 WebSocket 连接失败", t);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 向豆包 API 发送音频数据(实时流)
|
|
*/
|
|
public void sendAudio(byte[] audioData) {
|
|
if (doubaoWebSocket != null) {
|
|
doubaoWebSocket.send(ByteString.of(audioData));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 发送初始化参数(根据豆包 API 要求)
|
|
*/
|
|
private void sendInitParams(WebSocket webSocket) {
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.put("action", "init");
|
|
params.put("format", "pcm"); // 音频格式
|
|
params.put("sample_rate", 16000); // 采样率
|
|
params.put("mode", "realtime"); // 实时模式
|
|
try {
|
|
webSocket.send(new ObjectMapper().writeValueAsString(params));
|
|
} catch (JsonProcessingException e) {
|
|
log.error("发送初始化参数失败", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 关闭连接
|
|
*/
|
|
public void close() {
|
|
if (doubaoWebSocket != null) {
|
|
doubaoWebSocket.close(1000, "正常关闭");
|
|
}
|
|
}
|
|
}
|