mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03:53 +08:00
update:将本人的克隆音色自动添加模型配置中的音色选择列表
This commit is contained in:
@@ -146,6 +146,11 @@ public interface Constant {
|
||||
*/
|
||||
String MEMORY_NO_MEM = "Memory_nomem";
|
||||
|
||||
/**
|
||||
* 火山引擎双声道语音克隆
|
||||
*/
|
||||
String VOICE_CLONE_HUOSHAN_DOUBLE_STREAM = "huoshan_double_stream";
|
||||
|
||||
enum SysBaseParam {
|
||||
/**
|
||||
* ICP备案号
|
||||
@@ -171,6 +176,38 @@ public interface Constant {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 训练状态
|
||||
*/
|
||||
enum TrainStatus {
|
||||
/**
|
||||
* 未训练
|
||||
*/
|
||||
NOT_TRAINED(0),
|
||||
/**
|
||||
* 训练中
|
||||
*/
|
||||
TRAINING(1),
|
||||
/**
|
||||
* 已训练
|
||||
*/
|
||||
TRAINED(2),
|
||||
/**
|
||||
* 训练失败
|
||||
*/
|
||||
TRAIN_FAILED(3);
|
||||
|
||||
private final int code;
|
||||
|
||||
TrainStatus(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统短信
|
||||
*/
|
||||
|
||||
@@ -55,7 +55,7 @@ public interface ErrorCode {
|
||||
|
||||
int OTA_DEVICE_NOT_FOUND = 10041;
|
||||
int OTA_DEVICE_NEED_BIND = 10042;
|
||||
|
||||
|
||||
// 新增错误编码
|
||||
int DELETE_DATA_FAILED = 10043;
|
||||
int USER_NOT_LOGIN = 10044;
|
||||
@@ -66,7 +66,7 @@ public interface ErrorCode {
|
||||
int INVALID_LLM_TYPE = 10049;
|
||||
int TOKEN_GENERATE_ERROR = 10050;
|
||||
int RESOURCE_NOT_FOUND = 10051;
|
||||
|
||||
|
||||
// 新增错误编码
|
||||
int DEFAULT_AGENT_NOT_FOUND = 10052;
|
||||
int AGENT_NOT_FOUND = 10053;
|
||||
@@ -89,7 +89,7 @@ public interface ErrorCode {
|
||||
int LLM_NOT_EXIST = 10092; // 设置的LLM不存在
|
||||
int MODEL_REFERENCED_BY_AGENT = 10093; // 该模型配置已被智能体引用,无法删除
|
||||
int LLM_REFERENCED_BY_INTENT = 10094; // 该LLM模型已被意图识别配置引用,无法删除
|
||||
|
||||
|
||||
// 登录相关错误码
|
||||
int ADD_DATA_FAILED = 10065; // 新增数据失败
|
||||
int UPDATE_DATA_FAILED = 10066; // 修改数据失败
|
||||
@@ -102,17 +102,17 @@ public interface ErrorCode {
|
||||
int RETRIEVE_PASSWORD_DISABLED = 10073; // 未开启找回密码功能
|
||||
int PHONE_FORMAT_ERROR = 10074; // 手机号码格式不正确
|
||||
int SMS_CODE_ERROR = 10075; // 手机验证码错误
|
||||
|
||||
|
||||
// 字典类型相关错误码
|
||||
int DICT_TYPE_NOT_EXIST = 10076; // 字典类型不存在
|
||||
int DICT_TYPE_DUPLICATE = 10077; // 字典类型编码重复
|
||||
|
||||
|
||||
// 资源处理相关错误码
|
||||
int RESOURCE_READ_ERROR = 10078; // 读取资源失败
|
||||
|
||||
|
||||
// 智能体相关错误码
|
||||
int LLM_INTENT_PARAMS_MISMATCH = 10079; // LLM大模型和Intent意图识别,选择参数不匹配
|
||||
|
||||
|
||||
// 声纹相关错误码
|
||||
int VOICEPRINT_ALREADY_REGISTERED = 10080; // 此声音声纹已经注册
|
||||
int VOICEPRINT_DELETE_ERROR = 10081; // 删除声纹出现错误
|
||||
@@ -126,12 +126,12 @@ public interface ErrorCode {
|
||||
int VOICEPRINT_UNREGISTER_REQUEST_ERROR = 10089; // 声纹注销请求失败
|
||||
int VOICEPRINT_UNREGISTER_PROCESS_ERROR = 10090; // 声纹注销处理失败
|
||||
int VOICEPRINT_IDENTIFY_REQUEST_ERROR = 10091; // 声纹识别请求失败
|
||||
|
||||
|
||||
// 服务端管理相关错误码
|
||||
int INVALID_SERVER_ACTION = 10095; // 无效服务端操作
|
||||
int SERVER_WEBSOCKET_NOT_CONFIGURED = 10096; // 未配置服务端WebSocket地址
|
||||
int TARGET_WEBSOCKET_NOT_EXIST = 10097; // 目标WebSocket地址不存在
|
||||
|
||||
|
||||
// 参数验证相关错误码
|
||||
int WEBSOCKET_URLS_EMPTY = 10098; // WebSocket地址列表不能为空
|
||||
int WEBSOCKET_URL_LOCALHOST = 10099; // WebSocket地址不能使用localhost或127.0.0.1
|
||||
@@ -175,7 +175,7 @@ public interface ErrorCode {
|
||||
int DOWNLOAD_LINK_INVALID = 10137; // 下载链接无效
|
||||
int CHAT_ROLE_USER = 10138; // 用户角色
|
||||
int CHAT_ROLE_AGENT = 10139; // 智能体角色
|
||||
|
||||
|
||||
// 声音克隆相关错误码
|
||||
int VOICE_CLONE_AUDIO_EMPTY = 10140; // 音频文件不能为空
|
||||
int VOICE_CLONE_NOT_AUDIO_FILE = 10141; // 只支持音频文件
|
||||
@@ -197,4 +197,5 @@ public interface ErrorCode {
|
||||
int VOICE_CLONE_REQUEST_FAILED = 10157; // 请求失败
|
||||
int VOICE_CLONE_PREFIX = 10158; // 复刻音色前缀
|
||||
int VOICE_ID_ALREADY_EXISTS = 10159; // 音色ID已存在
|
||||
int VOICE_CLONE_HUOSHAN_VOICE_ID_ERROR = 10160; // 火山引擎音色ID格式错误
|
||||
}
|
||||
|
||||
+17
@@ -42,6 +42,7 @@ import xiaozhi.modules.agent.service.AgentTemplateService;
|
||||
import xiaozhi.modules.agent.vo.AgentInfoVO;
|
||||
import xiaozhi.modules.device.service.DeviceService;
|
||||
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
||||
import xiaozhi.modules.model.dto.VoiceDTO;
|
||||
import xiaozhi.modules.model.entity.ModelConfigEntity;
|
||||
import xiaozhi.modules.model.service.ModelConfigService;
|
||||
import xiaozhi.modules.model.service.ModelProviderService;
|
||||
@@ -373,6 +374,22 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
|
||||
entity.setLlmModelId(template.getLlmModelId());
|
||||
entity.setVllmModelId(template.getVllmModelId());
|
||||
entity.setTtsModelId(template.getTtsModelId());
|
||||
|
||||
if (template.getTtsVoiceId() == null && template.getTtsModelId() != null) {
|
||||
ModelConfigEntity ttsModel = modelConfigService.selectById(template.getTtsModelId());
|
||||
if (ttsModel != null && ttsModel.getConfigJson() != null) {
|
||||
Map<String, Object> config = ttsModel.getConfigJson();
|
||||
String voice = (String) config.get("voice");
|
||||
if (StringUtils.isBlank(voice)) {
|
||||
voice = (String) config.get("speaker");
|
||||
}
|
||||
VoiceDTO timbre = timbreModelService.getByVoiceCode(template.getTtsModelId(), voice);
|
||||
if (timbre != null) {
|
||||
template.setTtsVoiceId(timbre.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entity.setTtsVoiceId(template.getTtsVoiceId());
|
||||
entity.setMemModelId(template.getMemModelId());
|
||||
entity.setIntentModelId(template.getIntentModelId());
|
||||
|
||||
+9
-1
@@ -38,6 +38,8 @@ import xiaozhi.modules.sys.dto.SysParamsDTO;
|
||||
import xiaozhi.modules.sys.service.SysParamsService;
|
||||
import xiaozhi.modules.timbre.service.TimbreService;
|
||||
import xiaozhi.modules.timbre.vo.TimbreDetailsVO;
|
||||
import xiaozhi.modules.voiceclone.entity.VoiceCloneEntity;
|
||||
import xiaozhi.modules.voiceclone.service.VoiceCloneService;
|
||||
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
@@ -51,6 +53,7 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
private final TimbreService timbreService;
|
||||
private final AgentPluginMappingService agentPluginMappingService;
|
||||
private final AgentMcpAccessPointService agentMcpAccessPointService;
|
||||
private final VoiceCloneService cloneVoiceService;
|
||||
private final AgentVoicePrintDao agentVoicePrintDao;
|
||||
|
||||
@Override
|
||||
@@ -124,6 +127,11 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
voice = timbre.getTtsVoice();
|
||||
referenceAudio = timbre.getReferenceAudio();
|
||||
referenceText = timbre.getReferenceText();
|
||||
} else {
|
||||
VoiceCloneEntity voice_print = cloneVoiceService.selectById(agent.getTtsVoiceId());
|
||||
if (voice_print != null) {
|
||||
voice = voice_print.getVoiceId();
|
||||
}
|
||||
}
|
||||
// 构建返回数据
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
@@ -395,7 +403,7 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
|
||||
// 火山引擎声音克隆需要替换resource_id
|
||||
Map<String, Object> map = (Map<String, Object>) model.getConfigJson();
|
||||
if ("huoshan_double_stream".equals(map.get("type"))) {
|
||||
if (Constant.VOICE_CLONE_HUOSHAN_DOUBLE_STREAM.equals(map.get("type"))) {
|
||||
// 如果voice是”S_“开头的,使用seed-icl-1.0
|
||||
if (voice != null && voice.startsWith("S_")) {
|
||||
map.put("resource_id", "seed-icl-1.0");
|
||||
|
||||
@@ -64,12 +64,13 @@ public interface TimbreService extends BaseService<TimbreEntity> {
|
||||
* @return 音色名称
|
||||
*/
|
||||
String getTimbreNameById(String id);
|
||||
|
||||
|
||||
/**
|
||||
* 检查音色编码(ttsVoice/speaker_id)是否已存在
|
||||
* 根据音色编码获取音色信息
|
||||
*
|
||||
* @param ttsVoice 音色编码
|
||||
* @return 是否存在
|
||||
* @param ttsModelId 音色模型ID
|
||||
* @param voiceCode 音色编码
|
||||
* @return 音色信息
|
||||
*/
|
||||
boolean existsByTtsVoice(String ttsVoice);
|
||||
VoiceDTO getByVoiceCode(String ttsModelId, String voiceCode);
|
||||
}
|
||||
+44
-38
@@ -5,8 +5,7 @@ import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import xiaozhi.common.exception.ErrorCode;
|
||||
import xiaozhi.common.utils.MessageUtils;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -18,18 +17,23 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import xiaozhi.common.constant.Constant;
|
||||
import xiaozhi.common.exception.ErrorCode;
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.redis.RedisKeys;
|
||||
import xiaozhi.common.redis.RedisUtils;
|
||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||
import xiaozhi.common.utils.ConvertUtils;
|
||||
import xiaozhi.common.utils.MessageUtils;
|
||||
import xiaozhi.modules.model.dto.VoiceDTO;
|
||||
import xiaozhi.modules.security.user.SecurityUser;
|
||||
import xiaozhi.modules.timbre.dao.TimbreDao;
|
||||
import xiaozhi.modules.timbre.dto.TimbreDataDTO;
|
||||
import xiaozhi.modules.timbre.dto.TimbrePageDTO;
|
||||
import xiaozhi.modules.timbre.entity.TimbreEntity;
|
||||
import xiaozhi.modules.timbre.service.TimbreService;
|
||||
import xiaozhi.modules.timbre.vo.TimbreDetailsVO;
|
||||
import xiaozhi.modules.voiceclone.dao.VoiceCloneDao;
|
||||
import xiaozhi.modules.voiceclone.entity.VoiceCloneEntity;
|
||||
|
||||
/**
|
||||
* 音色的业务层的实现
|
||||
@@ -42,6 +46,7 @@ import xiaozhi.modules.timbre.vo.TimbreDetailsVO;
|
||||
public class TimbreServiceImpl extends BaseServiceImpl<TimbreDao, TimbreEntity> implements TimbreService {
|
||||
|
||||
private final TimbreDao timbreDao;
|
||||
private final VoiceCloneDao voiceCloneDao;
|
||||
private final RedisUtils redisUtils;
|
||||
|
||||
@Override
|
||||
@@ -123,39 +128,31 @@ public class TimbreServiceImpl extends BaseServiceImpl<TimbreDao, TimbreEntity>
|
||||
if (StringUtils.isNotBlank(voiceName)) {
|
||||
queryWrapper.like("name", voiceName);
|
||||
}
|
||||
|
||||
List<TimbreEntity> timbreEntities = timbreDao.selectList(queryWrapper);
|
||||
if (CollectionUtil.isEmpty(timbreEntities)) {
|
||||
return null;
|
||||
if (timbreEntities == null) {
|
||||
timbreEntities = new ArrayList<>();
|
||||
}
|
||||
List<VoiceDTO> voiceDTOs = timbreEntities.stream()
|
||||
.map(entity -> new VoiceDTO(entity.getId(), entity.getName()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 自定义排序:音色编码以S_开头的排在最前面,其他按sort字段升序排序
|
||||
timbreEntities.sort((t1, t2) -> {
|
||||
// 先判断是否以S_开头
|
||||
boolean isT1S_Start = StringUtils.isNotBlank(t1.getTtsVoice()) && t1.getTtsVoice().startsWith("S_");
|
||||
boolean isT2S_Start = StringUtils.isNotBlank(t2.getTtsVoice()) && t2.getTtsVoice().startsWith("S_");
|
||||
|
||||
if (isT1S_Start && !isT2S_Start) return -1; // t1以S_开头,排在前面
|
||||
if (!isT1S_Start && isT2S_Start) return 1; // t2以S_开头,排在前面
|
||||
|
||||
// 都以S_开头或都不以S_开头,则按sort字段升序排序
|
||||
return Long.compare(t1.getSort(), t2.getSort());
|
||||
});
|
||||
|
||||
// 排序完成后转换为DTO列表,并为复刻音色添加前缀
|
||||
List<VoiceDTO> voiceDTOs = new ArrayList<>();
|
||||
for (TimbreEntity entity : timbreEntities) {
|
||||
VoiceDTO dto = new VoiceDTO();
|
||||
dto.setId(entity.getId());
|
||||
// 对于音色编码以S_开头的复刻音色,在名称前加上"克隆:"前缀
|
||||
String name = entity.getName();
|
||||
if (StringUtils.isNotBlank(entity.getTtsVoice()) && entity.getTtsVoice().startsWith("S_") && !name.startsWith("克隆:")) {
|
||||
name = MessageUtils.getMessage(ErrorCode.VOICE_CLONE_PREFIX) + name;
|
||||
// 获取当前登录用户ID
|
||||
Long currentUserId = SecurityUser.getUser().getId();
|
||||
if (currentUserId != null) {
|
||||
// 查询用户的所有克隆音色记录
|
||||
List<VoiceDTO> cloneEntities = voiceCloneDao.getTrainSuccess(ttsModelId, currentUserId);
|
||||
for (VoiceDTO entity : cloneEntities) {
|
||||
// 只添加训练成功的克隆音色,且模型ID匹配
|
||||
VoiceDTO voiceDTO = new VoiceDTO();
|
||||
voiceDTO.setId(entity.getId());
|
||||
voiceDTO.setName(MessageUtils.getMessage(ErrorCode.VOICE_CLONE_PREFIX) + entity.getName());
|
||||
redisUtils.set(RedisKeys.getTimbreNameById(voiceDTO.getId()), voiceDTO.getName(),
|
||||
RedisUtils.NOT_EXPIRE);
|
||||
voiceDTOs.add(0, voiceDTO);
|
||||
}
|
||||
dto.setName(name);
|
||||
voiceDTOs.add(dto);
|
||||
}
|
||||
return voiceDTOs;
|
||||
|
||||
return CollectionUtil.isEmpty(voiceDTOs) ? null : voiceDTOs;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,21 +181,30 @@ public class TimbreServiceImpl extends BaseServiceImpl<TimbreDao, TimbreEntity>
|
||||
redisUtils.set(RedisKeys.getTimbreNameById(id), name);
|
||||
}
|
||||
return name;
|
||||
} else {
|
||||
VoiceCloneEntity cloneEntity = voiceCloneDao.selectById(id);
|
||||
if (cloneEntity != null) {
|
||||
String name = MessageUtils.getMessage(ErrorCode.VOICE_CLONE_PREFIX) + cloneEntity.getName();
|
||||
redisUtils.set(RedisKeys.getTimbreNameById(id), name);
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsByTtsVoice(String ttsVoice) {
|
||||
if (StringUtils.isBlank(ttsVoice)) {
|
||||
return false;
|
||||
public VoiceDTO getByVoiceCode(String ttsModelId, String voiceCode) {
|
||||
if (StringUtils.isBlank(voiceCode)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
QueryWrapper<TimbreEntity> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("tts_voice", ttsVoice);
|
||||
|
||||
// 检查数据库中是否存在相同的ttsVoice值
|
||||
return timbreDao.exists(queryWrapper);
|
||||
queryWrapper.eq("tts_model_id", ttsModelId);
|
||||
queryWrapper.eq("tts_voice", voiceCode);
|
||||
List<TimbreEntity> list = timbreDao.selectList(queryWrapper);
|
||||
if (list.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new VoiceDTO(list.get(0).getId(), list.get(0).getName());
|
||||
}
|
||||
}
|
||||
+1
@@ -116,6 +116,7 @@ public class VoiceCloneController {
|
||||
checkPermission(id);
|
||||
|
||||
voiceCloneService.updateName(id, name);
|
||||
redisUtils.delete(RedisKeys.getTimbreNameById(id));
|
||||
return new Result<String>();
|
||||
} catch (Exception e) {
|
||||
return new Result<String>().error(ErrorCode.UPDATE_DATA_FAILED, "更新失败: " + e.getMessage());
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package xiaozhi.modules.voiceclone.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import xiaozhi.modules.model.dto.VoiceDTO;
|
||||
import xiaozhi.modules.voiceclone.entity.VoiceCloneEntity;
|
||||
|
||||
/**
|
||||
@@ -11,5 +14,13 @@ import xiaozhi.modules.voiceclone.entity.VoiceCloneEntity;
|
||||
*/
|
||||
@Mapper
|
||||
public interface VoiceCloneDao extends BaseMapper<VoiceCloneEntity> {
|
||||
/**
|
||||
* 获取用户训练成功的音色列表
|
||||
*
|
||||
* @param modelId 模型ID
|
||||
* @param userId 用户ID
|
||||
* @return 训练成功的音色列表
|
||||
*/
|
||||
List<VoiceDTO> getTrainSuccess(String modelId, Long userId);
|
||||
|
||||
}
|
||||
|
||||
+3
@@ -33,6 +33,9 @@ public interface VoiceCloneService extends BaseService<VoiceCloneEntity> {
|
||||
|
||||
/**
|
||||
* 根据用户ID查询声音克隆列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 声音克隆列表
|
||||
*/
|
||||
List<VoiceCloneEntity> getByUserId(Long userId);
|
||||
|
||||
|
||||
+21
-24
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import xiaozhi.common.constant.Constant;
|
||||
import xiaozhi.common.exception.ErrorCode;
|
||||
import xiaozhi.common.exception.RenException;
|
||||
import xiaozhi.common.page.PageData;
|
||||
@@ -47,7 +48,6 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
|
||||
private final ModelConfigService modelConfigService;
|
||||
private final SysUserService sysUserService;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final xiaozhi.modules.timbre.service.TimbreService timbreService;
|
||||
|
||||
@Override
|
||||
public PageData<VoiceCloneEntity> page(Map<String, Object> params) {
|
||||
@@ -74,8 +74,27 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void save(VoiceCloneDTO dto) {
|
||||
ModelConfigEntity modelConfig = modelConfigService.getModelByIdFromCache(dto.getModelId());
|
||||
if (modelConfig == null || modelConfig.getConfigJson() == null) {
|
||||
throw new RenException(ErrorCode.VOICE_CLONE_MODEL_CONFIG_NOT_FOUND);
|
||||
}
|
||||
Map<String, Object> config = modelConfig.getConfigJson();
|
||||
String type = (String) config.get("type");
|
||||
if (StringUtils.isBlank(type)) {
|
||||
throw new RenException(ErrorCode.VOICE_CLONE_MODEL_TYPE_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 检查Voice ID是否已经被使用
|
||||
for (String voiceId : dto.getVoiceIds()) {
|
||||
if (StringUtils.isBlank(voiceId)) {
|
||||
continue;
|
||||
}
|
||||
if (Constant.VOICE_CLONE_HUOSHAN_DOUBLE_STREAM.equals(type)) {
|
||||
if (voiceId.indexOf("S_") == -1) {
|
||||
throw new RenException(ErrorCode.VOICE_CLONE_HUOSHAN_VOICE_ID_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
QueryWrapper<VoiceCloneEntity> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("voice_id", voiceId);
|
||||
wrapper.eq("model_id", dto.getModelId());
|
||||
@@ -254,7 +273,7 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
|
||||
if (StringUtils.isBlank(type)) {
|
||||
throw new RenException(ErrorCode.VOICE_CLONE_MODEL_TYPE_NOT_FOUND);
|
||||
}
|
||||
if (type.equals("huoshan_double_stream")) {
|
||||
if (Constant.VOICE_CLONE_HUOSHAN_DOUBLE_STREAM.equals(type)) {
|
||||
huoshanClone(config, entity);
|
||||
}
|
||||
} catch (RenException re) {
|
||||
@@ -337,28 +356,6 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
|
||||
entity.setVoiceId(speakerId);
|
||||
entity.setTrainError("");
|
||||
baseDao.updateById(entity);
|
||||
|
||||
try {
|
||||
// 检查speaker_id是否已经被使用
|
||||
if (timbreService.existsByTtsVoice(speakerId)) {
|
||||
log.info("音色编码speaker_id[{}]已存在,不重复写入数据库", speakerId);
|
||||
} else {
|
||||
xiaozhi.modules.timbre.dto.TimbreDataDTO timbreDataDTO = new xiaozhi.modules.timbre.dto.TimbreDataDTO();
|
||||
timbreDataDTO.setTtsModelId(entity.getModelId());
|
||||
timbreDataDTO.setName(entity.getName());
|
||||
timbreDataDTO.setTtsVoice(speakerId);
|
||||
timbreDataDTO.setSort(1);
|
||||
timbreDataDTO.setLanguages("zh-CN");
|
||||
timbreDataDTO.setRemark("复刻音色");
|
||||
|
||||
// 保存到音色表
|
||||
timbreService.save(timbreDataDTO);
|
||||
log.info("成功将复刻音色添加到音色表,speaker_id: {}", speakerId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 记录错误但不影响主流程
|
||||
log.error("将复刻音色添加到音色表失败: " + e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
// 失败时使用StatusMessage作为错误信息
|
||||
String errorMsg = StringUtils.isNotBlank(statusMessage) ? statusMessage : "训练失败";
|
||||
|
||||
+5
-1
@@ -11,5 +11,9 @@ CREATE TABLE `ai_voice_clone` (
|
||||
`train_error` VARCHAR(255) COMMENT '训练错误原因',
|
||||
`creator` BIGINT COMMENT '创建者 ID',
|
||||
`create_date` DATETIME COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`)
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX idx_ai_voice_clone_user_id_model_id_train_status (model_id,user_id, train_status),
|
||||
INDEX idx_ai_voice_clone_voice_id (voice_id),
|
||||
INDEX idx_ai_voice_clone_user_id (user_id),
|
||||
INDEX idx_ai_voice_clone_model_id_voice_id (model_id, voice_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='声音克隆表';
|
||||
@@ -0,0 +1,2 @@
|
||||
UPDATE `ai_model_config` SET model_code = 'XunfeiSparkLLM' WHERE id = 'LLM_XunfeiSparkLLM';
|
||||
UPDATE `ai_model_config` SET model_code = 'XunfeiStreamASR' WHERE id = 'ASR_XunfeiStream';
|
||||
@@ -389,9 +389,16 @@ databaseChangeLog:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202509220958.sql
|
||||
- changeSet:
|
||||
id: 202510071521
|
||||
id: 202510071522
|
||||
author: hrz
|
||||
changes:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202510071521.sql
|
||||
path: classpath:db/changelog/202510071522.sql
|
||||
- changeSet:
|
||||
id: 202510191042
|
||||
author: hrz
|
||||
changes:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202510191042.sql
|
||||
|
||||
@@ -164,4 +164,6 @@
|
||||
10155=\u706B\u5C71\u5F15\u64CE\u7F3A\u5C11\u914D\u7F6E
|
||||
10156=\u54CD\u5E94\u683C\u5F0F\u9519\u8BEF\uFF0C\u7F3A\u5C11BaseResp\u5B57\u6BB5
|
||||
10157=\u8BF7\u6C42\u5931\u8D25
|
||||
10158=\u8BF7\u6C42\u5931\u8D25\uFF0C\u72B6\u6001\u7801
|
||||
10158=\u8BF7\u6C42\u5931\u8D25\uFF0C\u72B6\u6001\u7801{0}
|
||||
10159=\u97F3\u8272ID\u5DF2\u5B58\u5728
|
||||
10160=\u706B\u5C71\u5F15\u64CE\u97F3\u8272ID\u683C\u5F0F\u9519\u8BEF\uFF0C\u5FC5\u987B\u4EE5S_\u5F00\u5934
|
||||
@@ -165,4 +165,5 @@
|
||||
10156=Response format error, missing BaseResp field
|
||||
10157=Request failed
|
||||
10158=Clone Voice:
|
||||
10159=Voice ID already exists
|
||||
10159=Voice ID already exists
|
||||
10160=Huoshan Engine voice ID format error, must start with S_
|
||||
@@ -164,5 +164,6 @@
|
||||
10155=\u706B\u5C71\u5F15\u64CE\u7F3A\u5C11\u914D\u7F6E
|
||||
10156=\u54CD\u5E94\u683C\u5F0F\u9519\u8BEF\uFF0C\u7F3A\u5C11BaseResp\u5B57\u6BB5
|
||||
10157=\u8BF7\u6C42\u5931\u8D25
|
||||
10158=\u514b\u9686\u8272\u97f3:
|
||||
10158=\u514B\u9686\u8272\u97F3:
|
||||
10159=\u97F3\u8272ID\u5DF2\u5B58\u5728
|
||||
10160=\u706B\u5C71\u5F15\u64CE\u97F3\u8272ID\u683C\u5F0F\u9519\u8BEF\uFF0C\u5FC5\u987B\u4EE5S_\u5F00\u5934
|
||||
@@ -164,5 +164,6 @@
|
||||
10155=\u706B\u5C71\u5F15\u64CE\u7F3A\u5C11appid\u6216access_token
|
||||
10156=\u97FF\u61C9\u683C\u5F0F\u932F\u8AA4\uFF0C\u7F3A\u5C11BaseResp\u5B57\u6BB5
|
||||
10157=\u8ACB\u6C42\u5931\u6557
|
||||
10158=\u514b\u9686\u8a9e\u97f3:
|
||||
10159=\u97F3\u8272ID\u5DF2\u5B58\u5728
|
||||
10158=\u514B\u9686\u8A9E\u97F3:
|
||||
10159=\u97F3\u8272ID\u5DF2\u5B58\u5728
|
||||
10160=\u706B\u5C71\u5F15\u64CE\u97F3\u8272ID\u683C\u5F0F\u932F\u8AA4\uFF0C\u5FC5\u9808\u4EE5S_\u958B\u982D
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="xiaozhi.modules.voiceclone.dao.VoiceCloneDao">
|
||||
<select id="getTrainSuccess" resultType="xiaozhi.modules.model.dto.VoiceDTO">
|
||||
select id, name
|
||||
from ai_voice_clone
|
||||
where model_id = #{modelId} and user_id = #{userId} and train_status = 2
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -876,6 +876,8 @@ export default {
|
||||
'ttsModel.operationClosed': 'Operation closed',
|
||||
'ttsModel.edit': 'Edit',
|
||||
'ttsModel.delete': 'Delete',
|
||||
'ttsModel.deleteVoiceSuccess': 'Successfully deleted {count} voices',
|
||||
'ttsModel.deleteFailed': 'Failed to delete voices',
|
||||
'ttsModel.save': 'Save',
|
||||
'ttsModel.deselectAll': 'Deselect All',
|
||||
'ttsModel.selectAll': 'Select All',
|
||||
|
||||
@@ -876,6 +876,8 @@ export default {
|
||||
'ttsModel.operationClosed': '操作已关闭',
|
||||
'ttsModel.edit': '编辑',
|
||||
'ttsModel.delete': '删除',
|
||||
'ttsModel.deleteVoiceSuccess': '成功删除{count}个音色',
|
||||
'ttsModel.deleteFailed': '删除音色失败',
|
||||
'ttsModel.save': '保存',
|
||||
'ttsModel.deselectAll': '取消全选',
|
||||
'ttsModel.selectAll': '全选',
|
||||
|
||||
@@ -874,6 +874,8 @@ export default {
|
||||
'ttsModel.operation': '操作',
|
||||
'ttsModel.edit': '編輯',
|
||||
'ttsModel.delete': '刪除',
|
||||
'ttsModel.deleteVoiceSuccess': '成功刪除{count}個音色',
|
||||
'ttsModel.deleteFailed': '刪除音色失敗',
|
||||
'ttsModel.save': '保存',
|
||||
'ttsModel.deselectAll': '取消全選',
|
||||
'ttsModel.selectAll': '全選',
|
||||
|
||||
Reference in New Issue
Block a user