diff --git a/src/main/java/com/newyetai/ai/resume/controller/AiResumeController.java b/src/main/java/com/newyetai/ai/resume/controller/AiResumeController.java index 2d0eb61..4409bd7 100644 --- a/src/main/java/com/newyetai/ai/resume/controller/AiResumeController.java +++ b/src/main/java/com/newyetai/ai/resume/controller/AiResumeController.java @@ -3,6 +3,7 @@ package com.newyetai.ai.resume.controller; import com.newyetai.ai.resume.dto.AiResumeReqDTO; import com.newyetai.ai.resume.service.AiResumeService; import com.newyetai.common.AlitaResult; +import com.newyetai.sdk.xf.dto.TtsReqDTO; import com.newyetai.sdk.xf.util.XfUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -48,8 +49,8 @@ public class AiResumeController { @PostMapping("/tts") @ApiOperation("ai简历-语音合成") - public AlitaResult tts(@RequestParam("text") String text) { - return AlitaResult.success(XfUtils.tts(text)); + public AlitaResult tts(@RequestBody @Validated TtsReqDTO ttsReq) { + return AlitaResult.success(XfUtils.tts(ttsReq)); } @PostMapping("/analyseResume") diff --git a/src/main/java/com/newyetai/sdk/xf/dto/RtasrReqDTO.java b/src/main/java/com/newyetai/sdk/xf/dto/RtasrReqDTO.java index b5b1399..fbe0b8c 100644 --- a/src/main/java/com/newyetai/sdk/xf/dto/RtasrReqDTO.java +++ b/src/main/java/com/newyetai/sdk/xf/dto/RtasrReqDTO.java @@ -16,6 +16,10 @@ public class RtasrReqDTO implements Serializable { @NotBlank(message = "语音文件不能为空") private String fileCode; + /** + * 中文:cn + * 维语:cn_uyghur + */ @Default private String from = "cn_uyghur"; diff --git a/src/main/java/com/newyetai/sdk/xf/dto/TtsReqDTO.java b/src/main/java/com/newyetai/sdk/xf/dto/TtsReqDTO.java new file mode 100644 index 0000000..d591dfe --- /dev/null +++ b/src/main/java/com/newyetai/sdk/xf/dto/TtsReqDTO.java @@ -0,0 +1,40 @@ +package com.newyetai.sdk.xf.dto; + +import com.newyetai.sdk.xf.util.WebTtsWS; +import java.io.Serializable; +import javax.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Builder.Default; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class TtsReqDTO implements Serializable { + + @NotBlank(message = "合成文本不能为空") + private String text; + + /** + * 发音人 + * x4_yezi(中文)/x2_ugsp_dilare(维语) + */ + @Default + private String vcn = "xiaoyan"; + + /** + * 文本编码格式 + * GB2312 + * GBK + * BIG5 + * UNICODE(小语种) + * GB18030 + * UTF8(小语种推荐使用UTF8编码) + * */ + @Default + private String tte = WebTtsWS.TTL_UTF8; + +} \ No newline at end of file diff --git a/src/main/java/com/newyetai/sdk/xf/util/WebTtsWS.java b/src/main/java/com/newyetai/sdk/xf/util/WebTtsWS.java index 560c416..14639bf 100644 --- a/src/main/java/com/newyetai/sdk/xf/util/WebTtsWS.java +++ b/src/main/java/com/newyetai/sdk/xf/util/WebTtsWS.java @@ -5,6 +5,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.UnsupportedEncodingException; import java.net.URL; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -22,6 +23,7 @@ import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream; import com.google.gson.Gson; import com.newyetai.common.AlitaResult; import com.newyetai.sdk.xf.config.XfProperties; +import com.newyetai.sdk.xf.dto.TtsReqDTO; import lombok.extern.slf4j.Slf4j; import okhttp3.HttpUrl; @@ -44,6 +46,9 @@ public class WebTtsWS extends WebSocketListener { public static final int StatusFirstFrame = 0; public static final int StatusContinueFrame = 1; public static final int StatusLastFrame = 2; + public static final String TTL_UTF8 = "UTF8"; + public static final String TTL_UNICODE = "UNICODE"; + public static final Gson json = new Gson(); // mp3格式 private String encoding = "lame"; @@ -52,14 +57,16 @@ public class WebTtsWS extends WebSocketListener { private String text; private String appId; OutputStream outputStream; - // 合成文本编码格式 - public static final String TTE = "UTF8"; // 小语种必须使用UNICODE编码作为值 + // 小语种必须使用UNICODE编码作为值 + private String tte; // 发音人参数。到控制台-我的应用-语音合成-添加试用或购买发音人,添加后即显示该发音人参数值,若试用未添加的发音人会报错11200 - public static final String VCN = "xiaoyan"; + private String vcn; - WebTtsWS(String text, String appId, OutputStream outputStream){ + WebTtsWS(String text, String appId, String vcn, String tte, OutputStream outputStream){ this.text = text; this.appId = appId; + this.vcn = vcn; + this.tte = tte; this.outputStream = outputStream; } @@ -73,6 +80,16 @@ public class WebTtsWS extends WebSocketListener { super.onOpen(webSocket, response); log.info("ws建立连接成功..."); String requestJson;//请求参数json串 + String encodedText ; + if (WebTtsWS.TTL_UNICODE.equals(tte)) { + try { + encodedText = Base64.getEncoder().encodeToString(text.getBytes("UTF-16LE")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } else { + encodedText = Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8)); + } requestJson = "{\n" + " \"common\": {\n" + " \"app_id\": \"" + appId + "\"\n" + @@ -80,21 +97,19 @@ public class WebTtsWS extends WebSocketListener { " \"business\": {\n" + " \"aue\": \"lame\",\n" + " \"sfl\": 1,\n" + - " \"tte\": \"" + TTE + "\",\n" + + " \"tte\": \"" + tte + "\",\n" + " \"ent\": \"intp65\",\n" + - " \"vcn\": \"" + VCN + "\",\n" + + " \"vcn\": \"" + vcn + "\",\n" + " \"pitch\": 50,\n" + " \"speed\": 50\n" + " },\n" + " \"data\": {\n" + " \"status\": 2,\n" + " \"text\": \"" - + Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8)) + "\"\n" + - //" \"text\": \"" + Base64.getEncoder().encodeToString(TEXT.getBytes("UTF-16LE")) + "\"\n" + + + encodedText + "\"\n" + " }\n" + "}"; webSocket.send(requestJson); - } @Override @@ -145,7 +160,7 @@ public class WebTtsWS extends WebSocketListener { } } - public static AlitaResult execute(String text, XfProperties xfProperties, OutputStream outputStream) { + public static AlitaResult execute(TtsReqDTO ttsReq, XfProperties xfProperties, OutputStream outputStream) { try { // 构建鉴权url String authUrl = getAuthUrl(xfProperties.getTtsHostUrl(), xfProperties.getApiKey(), @@ -154,7 +169,7 @@ public class WebTtsWS extends WebSocketListener { //将url中的 schema http://和https://分别替换为ws:// 和 wss:// String url = authUrl.toString().replace("http://", "ws://").replace("https://", "wss://"); Request request = new Request.Builder().url(url).build(); - WebTtsWS webIATWS = new WebTtsWS(text, xfProperties.getAppId(), outputStream); + WebTtsWS webIATWS = new WebTtsWS(ttsReq.getText(), xfProperties.getAppId(), ttsReq.getVcn(), ttsReq.getTte(), outputStream); WebSocket webSocket = client.newWebSocket(request, webIATWS); while (webIATWS.result == null) { // 未拿到结果等待 @@ -165,7 +180,6 @@ public class WebTtsWS extends WebSocketListener { log.info("err ", e); return AlitaResult.error("语音合成失败:{}", e.getMessage()); } - } public static String getAuthUrl(String hostUrl, String apiKey, String apiSecret) diff --git a/src/main/java/com/newyetai/sdk/xf/util/XfUtils.java b/src/main/java/com/newyetai/sdk/xf/util/XfUtils.java index 801c3e1..95d0fff 100644 --- a/src/main/java/com/newyetai/sdk/xf/util/XfUtils.java +++ b/src/main/java/com/newyetai/sdk/xf/util/XfUtils.java @@ -22,6 +22,7 @@ import com.newyetai.component.file.util.FileUtils; import com.newyetai.config.service.ConfigService; import com.newyetai.sdk.xf.config.XfProperties; import com.newyetai.sdk.xf.dto.InterviewContentDTO; +import com.newyetai.sdk.xf.dto.TtsReqDTO; import com.newyetai.sdk.xf.model.ChatReq; import com.newyetai.sdk.xf.model.MessageDTO; import com.newyetai.sdk.xf.service.XfSparkProvider; @@ -123,15 +124,14 @@ public class XfUtils { } - /** * 语音合成 * @return * @author sr on 2025-01-09 */ - public static String tts(String text) { + public static String tts(TtsReqDTO ttsReq) { try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream()){ - AlitaResult result = WebTtsWS.execute(text, xfProperties, outputStream); + AlitaResult result = WebTtsWS.execute(ttsReq, xfProperties, outputStream); Assert.isTrue(result.getSuccess(), result.getMsg()); FileRecord fileRecord = fileService.upload("语音合成.mp3", outputStream.toByteArray()); return fileRecord.getFileCode();