mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 15:13:55 +08:00
feat: 新增音频路径與音频文本欄位,提供給本地tts選取音色
https://github.com/xinnan-tech/xiaozhi-esp32-server/issues/1503#issuecomment-2954952028
This commit is contained in:
+22
-15
@@ -67,6 +67,7 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
agent.getVadModelId(),
|
||||
agent.getAsrModelId(),
|
||||
null,
|
||||
@@ -103,11 +104,13 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
}
|
||||
// 获取音色信息
|
||||
String voice = null;
|
||||
String voiceRemark = null;
|
||||
String referenceAudio = null;
|
||||
String referenceText = null;
|
||||
TimbreDetailsVO timbre = timbreService.get(agent.getTtsVoiceId());
|
||||
if (timbre != null) {
|
||||
voice = timbre.getTtsVoice();
|
||||
voiceRemark = timbre.getRemark();
|
||||
referenceAudio = timbre.getReferenceAudio();
|
||||
referenceText = timbre.getReferenceText();
|
||||
}
|
||||
// 构建返回数据
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
@@ -141,7 +144,8 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
agent.getSystemPrompt(),
|
||||
agent.getSummaryMemory(),
|
||||
voice,
|
||||
voiceRemark,
|
||||
referenceAudio,
|
||||
referenceText,
|
||||
agent.getVadModelId(),
|
||||
agent.getAsrModelId(),
|
||||
agent.getLlmModelId(),
|
||||
@@ -229,23 +233,25 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
/**
|
||||
* 构建模块配置
|
||||
*
|
||||
* @param prompt 提示词
|
||||
* @param voice 音色
|
||||
* @param voiceRemark 備註
|
||||
* @param vadModelId VAD模型ID
|
||||
* @param asrModelId ASR模型ID
|
||||
* @param llmModelId LLM模型ID
|
||||
* @param ttsModelId TTS模型ID
|
||||
* @param memModelId 记忆模型ID
|
||||
* @param intentModelId 意图模型ID
|
||||
* @param result 结果Map
|
||||
* @param prompt 提示词
|
||||
* @param voice 音色
|
||||
* @param referenceAudio 参考音频路径
|
||||
* @param referenceText 参考文本
|
||||
* @param vadModelId VAD模型ID
|
||||
* @param asrModelId ASR模型ID
|
||||
* @param llmModelId LLM模型ID
|
||||
* @param ttsModelId TTS模型ID
|
||||
* @param memModelId 记忆模型ID
|
||||
* @param intentModelId 意图模型ID
|
||||
* @param result 结果Map
|
||||
*/
|
||||
private void buildModuleConfig(
|
||||
String assistantName,
|
||||
String prompt,
|
||||
String summaryMemory,
|
||||
String voice,
|
||||
String voiceRemark,
|
||||
String referenceAudio,
|
||||
String referenceText,
|
||||
String vadModelId,
|
||||
String asrModelId,
|
||||
String llmModelId,
|
||||
@@ -273,7 +279,8 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
// 如果是TTS类型,添加private_voice属性
|
||||
if ("TTS".equals(modelTypes[i])){
|
||||
if (voice != null) ((Map<String, Object>) model.getConfigJson()).put("private_voice", voice);
|
||||
if (voiceRemark != null) ((Map<String, Object>) model.getConfigJson()).put("voice_remark", voiceRemark);
|
||||
if (referenceAudio != null) ((Map<String, Object>) model.getConfigJson()).put("ref_audio", referenceAudio);
|
||||
if (referenceText != null) ((Map<String, Object>) model.getConfigJson()).put("ref_text", referenceText);
|
||||
}
|
||||
// 如果是Intent类型,且type=intent_llm,则给他添加附加模型
|
||||
if ("Intent".equals(modelTypes[i])) {
|
||||
|
||||
@@ -26,6 +26,12 @@ public class TimbreDataDTO {
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "参考音频路径")
|
||||
private String referenceAudio;
|
||||
|
||||
@Schema(description = "參考文本")
|
||||
private String referenceText;
|
||||
|
||||
@Schema(description = "排序")
|
||||
@Min(value = 0, message = "{sort.number}")
|
||||
private long sort;
|
||||
|
||||
@@ -34,6 +34,12 @@ public class TimbreEntity {
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "参考音频路径")
|
||||
private String referenceAudio;
|
||||
|
||||
@Schema(description = "參考文本")
|
||||
private String referenceText;
|
||||
|
||||
@Schema(description = "排序")
|
||||
private long sort;
|
||||
|
||||
|
||||
@@ -25,6 +25,12 @@ public class TimbreDetailsVO implements Serializable {
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "参考音频路径")
|
||||
private String referenceAudio;
|
||||
|
||||
@Schema(description = "參考文本")
|
||||
private String referenceText;
|
||||
|
||||
@Schema(description = "排序")
|
||||
private long sort;
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE `ai_tts_voice`
|
||||
ADD COLUMN `reference_audio` VARCHAR(500) DEFAULT NULL COMMENT '参考音频路径' AFTER `remark`,
|
||||
ADD COLUMN `reference_text` VARCHAR(500) DEFAULT NULL COMMENT '参考文本' AFTER `reference_audio`;
|
||||
@@ -204,4 +204,11 @@ databaseChangeLog:
|
||||
changes:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202506080955.sql
|
||||
path: classpath:db/changelog/202506080955.sql
|
||||
- changeSet:
|
||||
id: 202506091720
|
||||
author: shane0411
|
||||
changes:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202506091720.sql
|
||||
|
||||
@@ -34,6 +34,8 @@ export default {
|
||||
languages: params.languageType,
|
||||
name: params.voiceName,
|
||||
remark: params.remark,
|
||||
referenceAudio: params.referenceAudio,
|
||||
referenceText: params.referenceText,
|
||||
sort: params.sort,
|
||||
ttsModelId: params.ttsModelId,
|
||||
ttsVoice: params.voiceCode,
|
||||
@@ -75,6 +77,8 @@ export default {
|
||||
languages: params.languageType,
|
||||
name: params.voiceName,
|
||||
remark: params.remark,
|
||||
referenceAudio: params.referenceAudio,
|
||||
referenceText: params.referenceText,
|
||||
ttsModelId: params.ttsModelId,
|
||||
ttsVoice: params.voiceCode,
|
||||
voiceDemo: params.voiceDemo || ''
|
||||
|
||||
@@ -74,6 +74,18 @@
|
||||
<span v-else>{{ scope.row.remark }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="音频路径" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-if="scope.row.editing" v-model="scope.row.referenceAudio" placeholder="这里是参考音频路径"></el-input>
|
||||
<span v-else>{{ scope.row.referenceAudio }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="音频文本" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-if="scope.row.editing" v-model="scope.row.referenceText"></el-input>
|
||||
<span v-else>{{ scope.row.referenceText }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
<template v-if="!scope.row.editing">
|
||||
@@ -206,6 +218,8 @@ export default {
|
||||
voiceName: item.name || '未命名音色',
|
||||
languageType: item.languages || '',
|
||||
remark: item.remark || '',
|
||||
referenceAudio: item.referenceAudio || '',
|
||||
referenceText: item.referenceText || '',
|
||||
voiceDemo: item.voiceDemo || '',
|
||||
selected: false,
|
||||
editing: false,
|
||||
@@ -351,6 +365,8 @@ export default {
|
||||
voiceName: row.voiceName,
|
||||
languageType: row.languageType,
|
||||
remark: row.remark,
|
||||
referenceAudio: row.referenceAudio,
|
||||
referenceText: row.referenceText,
|
||||
ttsModelId: this.ttsModelId,
|
||||
voiceDemo: row.voiceDemo || '',
|
||||
sort: row.sort
|
||||
@@ -432,6 +448,8 @@ export default {
|
||||
languageType: '中文',
|
||||
voiceDemo: '',
|
||||
remark: '',
|
||||
referenceAudio: '',
|
||||
referenceText: '',
|
||||
selected: false,
|
||||
editing: true,
|
||||
sort: maxSort + 1
|
||||
|
||||
@@ -85,8 +85,12 @@ class TTSProvider(TTSProviderBase):
|
||||
self.reference_id = (
|
||||
None if not config.get("reference_id") else config.get("reference_id")
|
||||
)
|
||||
self.reference_audio = parse_string_to_list(config.get("reference_audio"))
|
||||
self.reference_text = parse_string_to_list(config.get("reference_text"))
|
||||
self.reference_audio = parse_string_to_list(
|
||||
config.get('ref_audio')if config.get('ref_audio') else config.get("reference_audio")
|
||||
)
|
||||
self.reference_text = parse_string_to_list(
|
||||
config.get('ref_text')if config.get('ref_text') else config.get("reference_text")
|
||||
)
|
||||
self.format = config.get("response_format", "wav")
|
||||
self.audio_file_type = config.get("response_format", "wav")
|
||||
self.api_key = config.get("api_key", "YOUR_API_KEY")
|
||||
@@ -129,13 +133,6 @@ class TTSProvider(TTSProviderBase):
|
||||
self.use_memory_cache = config.get("use_memory_cache", "on")
|
||||
self.seed = int(config.get("seed")) if config.get("seed") else None
|
||||
self.api_url = config.get("api_url", "http://127.0.0.1:8080/v1/tts")
|
||||
self.get_voice_data(config)
|
||||
|
||||
def get_voice_data(self, config: dict):
|
||||
if not config.get('private_voice', '') and not config.get('voice_remark', ''):
|
||||
return
|
||||
self.reference_audio = config.get('private_voice')
|
||||
self.reference_text = config.get('voice_remark')
|
||||
|
||||
async def text_to_speak(self, text, output_file):
|
||||
# Prepare reference data
|
||||
|
||||
@@ -12,8 +12,8 @@ class TTSProvider(TTSProviderBase):
|
||||
super().__init__(config, delete_audio_file)
|
||||
self.url = config.get("url")
|
||||
self.text_lang = config.get("text_lang", "zh")
|
||||
self.ref_audio_path = config.get("ref_audio_path")
|
||||
self.prompt_text = config.get("prompt_text")
|
||||
self.ref_audio_path = config.get('ref_audio') if config.get('ref_audio') else config.get("ref_audio_path")
|
||||
self.prompt_text = config.get('ref_text') if config.get('ref_text') else config.get("prompt_text")
|
||||
self.prompt_lang = config.get("prompt_lang", "zh")
|
||||
|
||||
# 处理空字符串的情况
|
||||
@@ -67,14 +67,6 @@ class TTSProvider(TTSProviderBase):
|
||||
)
|
||||
self.audio_file_type = config.get("format", "wav")
|
||||
|
||||
self.get_voice_data(config)
|
||||
|
||||
def get_voice_data(self, config: dict):
|
||||
if not config.get('private_voice', '') and not config.get('voice_remark', ''):
|
||||
return
|
||||
self.ref_audio_path = config.get('private_voice')
|
||||
self.prompt_text = config.get('voice_remark')
|
||||
|
||||
async def text_to_speak(self, text, output_file):
|
||||
request_json = {
|
||||
"text": text,
|
||||
|
||||
@@ -11,8 +11,8 @@ class TTSProvider(TTSProviderBase):
|
||||
def __init__(self, config, delete_audio_file):
|
||||
super().__init__(config, delete_audio_file)
|
||||
self.url = config.get("url")
|
||||
self.refer_wav_path = config.get("refer_wav_path")
|
||||
self.prompt_text = config.get("prompt_text")
|
||||
self.refer_wav_path = config.get('ref_audio')if config.get('ref_audio') else config.get("refer_wav_path")
|
||||
self.prompt_text = config.get('ref_text')if config.get('ref_text') else config.get("prompt_text")
|
||||
self.prompt_language = config.get("prompt_language")
|
||||
self.text_language = config.get("text_language", "audo")
|
||||
|
||||
@@ -34,14 +34,6 @@ class TTSProvider(TTSProviderBase):
|
||||
self.if_sr = str(config.get("if_sr", False)).lower() in ("true", "1", "yes")
|
||||
self.audio_file_type = config.get("format", "wav")
|
||||
|
||||
self.get_voice_data(config)
|
||||
|
||||
def get_voice_data(self, config: dict):
|
||||
if not config.get('private_voice', '') and not config.get('voice_remark', ''):
|
||||
return
|
||||
self.refer_wav_path = config.get('private_voice')
|
||||
self.prompt_text = config.get('voice_remark')
|
||||
|
||||
async def text_to_speak(self, text, output_file):
|
||||
request_params = {
|
||||
"refer_wav_path": self.refer_wav_path,
|
||||
|
||||
Reference in New Issue
Block a user