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