|
|
@@ -5,8 +5,13 @@ import cn.iocoder.byzs.framework.common.pojo.PageResult;
|
|
|
import cn.iocoder.byzs.framework.common.util.object.BeanUtils;
|
|
|
import cn.iocoder.byzs.module.ai.controller.admin.tts.vo.AiTtsPageReqVO;
|
|
|
import cn.iocoder.byzs.module.ai.controller.admin.tts.vo.AiTtsSaveReqVO;
|
|
|
+import cn.iocoder.byzs.module.ai.dal.dataobject.model.AiChatRoleDO;
|
|
|
import cn.iocoder.byzs.module.ai.dal.dataobject.tts.AiTtsDO;
|
|
|
+import cn.iocoder.byzs.module.ai.dal.mysql.model.AiChatRoleMapper;
|
|
|
import cn.iocoder.byzs.module.ai.dal.mysql.tts.AiTtsMapper;
|
|
|
+import cn.iocoder.byzs.module.ai.util.tts.StreamTtsService;
|
|
|
+import cn.iocoder.byzs.module.infra.api.file.FileApi;
|
|
|
+import com.alibaba.nls.client.protocol.OutputFormatEnum;
|
|
|
import jakarta.annotation.Resource;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
|
@@ -28,6 +33,15 @@ public class AiTtsServiceImpl implements AiTtsService {
|
|
|
@Resource
|
|
|
private AiTtsMapper ttsMapper;
|
|
|
|
|
|
+ @Resource
|
|
|
+ private AiChatRoleMapper chatRoleMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private org.springframework.beans.factory.ObjectProvider<StreamTtsService> streamTtsServiceProvider;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private FileApi fileApi;
|
|
|
+
|
|
|
@Override
|
|
|
public Long createTts(AiTtsSaveReqVO createReqVO) {
|
|
|
// 插入
|
|
|
@@ -90,4 +104,57 @@ public class AiTtsServiceImpl implements AiTtsService {
|
|
|
return ttsMapper.getTtsSimpleListByStatus(status);
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public String convertTextToSpeech(Long roleId, String content) {
|
|
|
+ // 1. 根据角色id查询角色信息
|
|
|
+ AiChatRoleDO chatRole = chatRoleMapper.selectById(roleId);
|
|
|
+ if (chatRole == null) {
|
|
|
+ throw exception(TTS_NOT_EXISTS);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 根据角色的ttsId查询TTS配置
|
|
|
+ Long ttsId = chatRole.getTtsId();
|
|
|
+ if (ttsId == null) {
|
|
|
+ throw exception(TTS_NOT_EXISTS);
|
|
|
+ }
|
|
|
+ AiTtsDO aiTtsDO = ttsMapper.selectById(ttsId);
|
|
|
+ if (aiTtsDO == null) {
|
|
|
+ throw exception(TTS_NOT_EXISTS);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 使用StreamTtsService将文本转语音
|
|
|
+ StreamTtsService streamTtsService = streamTtsServiceProvider.getObject();
|
|
|
+ try {
|
|
|
+ // 创建音频数据缓冲区
|
|
|
+ java.io.ByteArrayOutputStream audioOutputStream = new java.io.ByteArrayOutputStream();
|
|
|
+ // 设置音频数据回调
|
|
|
+ streamTtsService.setAudioDataCallback(audioData -> {
|
|
|
+ try {
|
|
|
+ audioOutputStream.write(audioData);
|
|
|
+ } catch (java.io.IOException e) {
|
|
|
+ throw new RuntimeException("写入音频数据失败", e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 开始TTS语音合成
|
|
|
+ streamTtsService.startTts(aiTtsDO, OutputFormatEnum.MP3);
|
|
|
+ // 发送文本
|
|
|
+ streamTtsService.sendText(content);
|
|
|
+ // 停止TTS
|
|
|
+ streamTtsService.stopTts();
|
|
|
+
|
|
|
+ // 4. 存储语音文件并上传到服务器
|
|
|
+ byte[] mp3Data = audioOutputStream.toByteArray();
|
|
|
+ String filePath = fileApi.createFile(mp3Data);
|
|
|
+ return filePath;
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("文本转语音失败", e);
|
|
|
+ } finally {
|
|
|
+ // 确保资源被释放
|
|
|
+ if (streamTtsService != null) {
|
|
|
+ streamTtsService.stopTts();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
}
|