|
@@ -16,11 +16,17 @@ import com.fs.fastGpt.domain.FastgptEventLogTotal;
|
|
|
import com.fs.fastGpt.mapper.FastGptRoleMapper;
|
|
import com.fs.fastGpt.mapper.FastGptRoleMapper;
|
|
|
import com.fs.fastGpt.service.impl.FastgptEventLogTotalServiceImpl;
|
|
import com.fs.fastGpt.service.impl.FastgptEventLogTotalServiceImpl;
|
|
|
import com.fs.fastgptApi.vo.AudioVO;
|
|
import com.fs.fastgptApi.vo.AudioVO;
|
|
|
|
|
+import com.fs.his.domain.FsProject;
|
|
|
|
|
+import com.fs.his.mapper.FsProjectMapper;
|
|
|
|
|
+import com.fs.huifuPay.sdk.opps.core.utils.StringUtil;
|
|
|
import com.fs.qw.domain.QwUser;
|
|
import com.fs.qw.domain.QwUser;
|
|
|
import com.fs.system.oss.CloudStorageService;
|
|
import com.fs.system.oss.CloudStorageService;
|
|
|
import com.fs.system.oss.OSSFactory;
|
|
import com.fs.system.oss.OSSFactory;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import okhttp3.*;
|
|
import okhttp3.*;
|
|
|
|
|
+import org.jsoup.Jsoup;
|
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
@@ -44,6 +50,8 @@ import static com.fs.fastgptApi.util.AudioUtils.transferAudioSilk;
|
|
|
@Slf4j
|
|
@Slf4j
|
|
|
public class TtsServiceImpl implements TtsService {
|
|
public class TtsServiceImpl implements TtsService {
|
|
|
|
|
|
|
|
|
|
+ protected final Logger logger = LoggerFactory.getLogger(this.getClass());
|
|
|
|
|
+
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private TtsConfig ttsConfig;
|
|
private TtsConfig ttsConfig;
|
|
|
|
|
|
|
@@ -67,6 +75,9 @@ public class TtsServiceImpl implements TtsService {
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private FastgptEventLogTotalServiceImpl fastgptEventLogTotalServiceImpl;
|
|
private FastgptEventLogTotalServiceImpl fastgptEventLogTotalServiceImpl;
|
|
|
|
|
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private FsProjectMapper fsProjectMapper;
|
|
|
|
|
+
|
|
|
@Override
|
|
@Override
|
|
|
public AudioVO textToSpeech(TtsRequest request) {
|
|
public AudioVO textToSpeech(TtsRequest request) {
|
|
|
try {
|
|
try {
|
|
@@ -126,6 +137,65 @@ public class TtsServiceImpl implements TtsService {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public AudioVO textToSound(TtsRequest request) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 1. 参数校验
|
|
|
|
|
+ validateTtsHcRequest(request);
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 设置默认值
|
|
|
|
|
+ setTtsDefaultValues(request);
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 构建请求体
|
|
|
|
|
+ String requestBody = buildTtsRequestBody(request);
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 构建HTTP请求
|
|
|
|
|
+ Request httpRequest = buildHttpRequest(requestBody);
|
|
|
|
|
+
|
|
|
|
|
+ // 5. 发送请求(带重试)
|
|
|
|
|
+ byte[] bytes = executeTtsRequest(httpRequest);
|
|
|
|
|
+
|
|
|
|
|
+ // 6. 检查音频数据
|
|
|
|
|
+ if (bytes == null || bytes.length == 0) {
|
|
|
|
|
+ log.error("音频数据为空,VoiceType: {}", request.getVoiceType());
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 7. 自动保存音频文件
|
|
|
|
|
+ // 创建临时文件
|
|
|
|
|
+ File tempFile = File.createTempFile("tts_", ".wav");
|
|
|
|
|
+ try (FileOutputStream fos = new FileOutputStream(tempFile)) {
|
|
|
|
|
+ fos.write(bytes);
|
|
|
|
|
+ }
|
|
|
|
|
+ // 上传到OSS
|
|
|
|
|
+ try (FileInputStream fileInputStream = new FileInputStream(tempFile)) {
|
|
|
|
|
+ //直接转silk然后传桶,返回url 优化-需要wav格式
|
|
|
|
|
+ CloudStorageService storage = OSSFactory.build();
|
|
|
|
|
+ String wavUrl = storage.uploadSuffix(fileInputStream, ".wav");
|
|
|
|
|
+// AudioVO audioVO = AudioUtils.transferAudioSilkFromUrl(wavUrl, false);
|
|
|
|
|
+ Integer durations = getDurations(tempFile.getParent()+"\\"+tempFile.getName());
|
|
|
|
|
+ String silkUrl = transferAudioSilk(tempFile.getParent()+"\\", tempFile.getName(), false);
|
|
|
|
|
+ AudioVO audioVO = new AudioVO();
|
|
|
|
|
+ audioVO.setDuration(durations);
|
|
|
|
|
+ audioVO.setUrl(silkUrl);
|
|
|
|
|
+ audioVO.setWavUrl(wavUrl);
|
|
|
|
|
+ audioVO.setVoiceTxt(request.getText());
|
|
|
|
|
+ log.info("音频文件上传OSS成功: {}", audioVO.getUrl());
|
|
|
|
|
+ return audioVO;
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ // 删除临时文件
|
|
|
|
|
+ tempFile.delete();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("TTS合成失败,reqId: {}, 错误: {}",
|
|
|
|
|
+ request.getReqId(), e.getMessage());
|
|
|
|
|
+ throw e instanceof VoiceCloneException ?
|
|
|
|
|
+ (VoiceCloneException) e :
|
|
|
|
|
+ new VoiceCloneException("TTS合成失败", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
@Override
|
|
@Override
|
|
|
public void ttsChargeByCount(VcCompanyUser vcCompanyUser, AudioVO audioVO, QwUser user) {
|
|
public void ttsChargeByCount(VcCompanyUser vcCompanyUser, AudioVO audioVO, QwUser user) {
|
|
|
try {
|
|
try {
|
|
@@ -318,6 +388,25 @@ public class TtsServiceImpl implements TtsService {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private void validateTtsHcRequest(TtsRequest request) {
|
|
|
|
|
+ if (request == null) {
|
|
|
|
|
+ throw new VoiceCloneException(1001, "请求参数不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (request.getText() == null || request.getText().trim().isEmpty()) {
|
|
|
|
|
+ throw new VoiceCloneException(1001, "文本内容不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (request.getText().length() > ttsConfig.getMaxTextLength()) {
|
|
|
|
|
+ throw new VoiceCloneException(1001,
|
|
|
|
|
+ String.format("文本长度超过限制(%d字符)", ttsConfig.getMaxTextLength()));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (request.getReqId() == null || request.getReqId().trim().isEmpty()) {
|
|
|
|
|
+ request.setReqId(UUID.randomUUID().toString());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private void setDefaultValues(TtsRequest request) {
|
|
private void setDefaultValues(TtsRequest request) {
|
|
|
if (request.getAppId() == null || request.getAppId().equals("")) {
|
|
if (request.getAppId() == null || request.getAppId().equals("")) {
|
|
|
request.setAppId(voiceCloneConfig.getAppId());
|
|
request.setAppId(voiceCloneConfig.getAppId());
|
|
@@ -352,6 +441,41 @@ public class TtsServiceImpl implements TtsService {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private void setTtsDefaultValues(TtsRequest request) {
|
|
|
|
|
+ if (request.getAppId() == null || request.getAppId().equals("")) {
|
|
|
|
|
+ request.setAppId(voiceCloneConfig.getAppId());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (request.getToken() == null || request.getToken().equals("")) {
|
|
|
|
|
+ request.setToken(voiceCloneConfig.getAccessToken());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ request.setCluster(ttsConfig.getTtsCluster());
|
|
|
|
|
+
|
|
|
|
|
+ if (request.getFormat() == null) {
|
|
|
|
|
+ request.setFormat(ttsConfig.getDefaultFormat());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (request.getSampleRate() == null) {
|
|
|
|
|
+ request.setSampleRate(ttsConfig.getDefaultSampleRate());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (request.getSpeed() == null) {
|
|
|
|
|
+ request.setSpeed(ttsConfig.getDefaultSpeed());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (request.getVolume() == null) {
|
|
|
|
|
+ request.setVolume(ttsConfig.getDefaultVolume());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (request.getPitch() == null) {
|
|
|
|
|
+ request.setPitch(ttsConfig.getDefaultPitch());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (request.getVoiceType() == null) {
|
|
|
|
|
+ request.setVoiceType("zh_male_M392_conversation_wvae_bigtts");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private String buildRequestBody(TtsRequest request) throws IOException {
|
|
private String buildRequestBody(TtsRequest request) throws IOException {
|
|
|
Map<String, Object> requestBody = new HashMap<>();
|
|
Map<String, Object> requestBody = new HashMap<>();
|
|
|
|
|
|
|
@@ -430,6 +554,38 @@ public class TtsServiceImpl implements TtsService {
|
|
|
return objectMapper.writeValueAsString(requestBody);
|
|
return objectMapper.writeValueAsString(requestBody);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private String buildTtsRequestBody(TtsRequest request) throws IOException {
|
|
|
|
|
+ Map<String, Object> requestBody = new HashMap<>();
|
|
|
|
|
+
|
|
|
|
|
+ // 必填参数
|
|
|
|
|
+ HashMap<String, Object> app = new HashMap<String, Object>() {{
|
|
|
|
|
+ put("appid", request.getAppId());
|
|
|
|
|
+ put("token", request.getToken());
|
|
|
|
|
+ put("cluster", request.getCluster());
|
|
|
|
|
+ }};
|
|
|
|
|
+ HashMap<String, Object> user = new HashMap<String, Object>() {{
|
|
|
|
|
+ put("uid","001");
|
|
|
|
|
+ }};
|
|
|
|
|
+ HashMap<String, Object> audio = new HashMap<String, Object>() {{
|
|
|
|
|
+ put("voice_type", request.getVoiceType());
|
|
|
|
|
+ }};
|
|
|
|
|
+ if (request.getFormat() != null)audio.put("encoding", request.getFormat());
|
|
|
|
|
+ if (request.getSpeed() != null)audio.put("speed_ratio", request.getSpeed());
|
|
|
|
|
+
|
|
|
|
|
+ HashMap<String, Object> requestMap = new HashMap<String, Object>() {{
|
|
|
|
|
+ put("reqid", request.getReqId());
|
|
|
|
|
+ put("text", request.getText());
|
|
|
|
|
+ put("operation","query");
|
|
|
|
|
+ }};
|
|
|
|
|
+
|
|
|
|
|
+ requestBody.put("app",app);
|
|
|
|
|
+ requestBody.put("user", user);
|
|
|
|
|
+ requestBody.put("audio", audio);
|
|
|
|
|
+ requestBody.put("request", requestMap);
|
|
|
|
|
+
|
|
|
|
|
+ return objectMapper.writeValueAsString(requestBody);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private Request buildHttpRequest(String requestBody) {
|
|
private Request buildHttpRequest(String requestBody) {
|
|
|
RequestBody body = RequestBody.create(JSON,requestBody );
|
|
RequestBody body = RequestBody.create(JSON,requestBody );
|
|
|
|
|
|