mirror of
https://github.com/RVC-Boss/GPT-SoVITS.git
synced 2026-07-22 01:47:57 +08:00
feat: 兼容 proplus-hc-dev 训练数据 + 新增「情绪/括注」字段
兼容 proplus-hc-dev 分支基于 6 列 .list 训练的模型,并按主分支自身流程
增加 emotion 字段(标注/推理 UI 单输入框「情绪/括注」,存 emotion 列,
保留 remark 列以兼容 proplus 6 列结构)。emotion/remark 不参与训练。
核心兼容性修复(消除 3 处硬编码拒绝 proplus 的 6 列 .list):
- 新增 tools/list_metadata.py: 6 列 .list 解析/格式化器 (parse/format_list_line)
- 1-get-text.py:129 line.split("|") 严格 4 列解包 -> parse_list_line
(proplus 同款修复,只取训练所需前 4 列,emotion/remark 静默忽略)
- subfix_webui b_load_list: 仅接受 len==4 -> 改用 parse_list_line (>=4 列)
- subfix_webui b_save_list: 写 4 列 -> 写 6 列 (保留 emotion/remark,向前向后兼容)
标注 UI (tools/subfix_webui.py):
- 每条音频新增「情绪/括注」Text 输入框 (g_emotion_list)
- b_change_index/reload_data/b_submit_change 贯通 emotion 读写
- 8 个分页事件输出列表统一追加 *g_emotion_list
推理 WebUI (inference_webui.py):
- _read_emotion_map_for_webui: 读 output/asr_opt/<exp>/*.list 的 emotion 列
- 训练样本下拉标签附加【emotion】(有则显示)
- 选中样本时同步刷新「情绪/括注」只读文本框; 应用样本一并填入
- 无 emotion 数据则文本框留空 (符合「无数据不显示」)
API (api_v2.py):
- GET /models/{name}/samples 响应增加 emotion 字段 (来自同源 .list)
向后兼容: 主分支原生 4 列 .list 全程正常; 训练 pipeline 不感知 emotion。
list_metadata/parse/emotion/round-trip 单元自测通过; py_compile 全通过。
This commit is contained in:
parent
7be8a59e52
commit
e1720e7e5a
@ -1245,6 +1245,34 @@ def _read_name2text_for_webui(logs_dir: Path) -> dict[str, dict[str, str]]:
|
||||
return output
|
||||
|
||||
|
||||
def _read_emotion_map_for_webui(model_name: str) -> dict[str, str]:
|
||||
"""读取 ASR 标注 .list 中的 emotion 列,返回 {audio_name|stem: emotion}。
|
||||
|
||||
扫描 output/asr_opt/<model_name>/ 下所有 *.list(4/5/6 列均兼容)。
|
||||
无 emotion 列或文件不存在时返回空 dict。
|
||||
"""
|
||||
from tools.list_metadata import parse_list_line
|
||||
|
||||
emotion_map: dict[str, str] = {}
|
||||
list_dir = Path("output") / "asr_opt" / model_name
|
||||
if not list_dir.exists():
|
||||
return emotion_map
|
||||
for list_path in sorted(list_dir.glob("*.list")):
|
||||
try:
|
||||
for line in list_path.read_text(encoding="utf-8", errors="ignore").splitlines():
|
||||
item = parse_list_line(line)
|
||||
if item is None or not item.emotion:
|
||||
continue
|
||||
name = os.path.basename(item.wav_path.strip("\"'"))
|
||||
if not name:
|
||||
continue
|
||||
emotion_map[name] = item.emotion
|
||||
emotion_map[Path(name).stem] = item.emotion
|
||||
except Exception:
|
||||
continue
|
||||
return emotion_map
|
||||
|
||||
|
||||
def on_model_name_change(model_name: str):
|
||||
"""模型名变化时,加载该模型的训练样本列表到下拉框与预览播放器。"""
|
||||
if not model_name:
|
||||
@ -1256,10 +1284,16 @@ def on_model_name_change(model_name: str):
|
||||
samples: list[tuple[str, str]] = []
|
||||
if wav_dir.exists():
|
||||
name2text = _read_name2text_for_webui(logs_dir)
|
||||
emotion_map = _read_emotion_map_for_webui(model_name)
|
||||
for f in sorted(wav_dir.iterdir()):
|
||||
if f.is_file() and f.suffix.lower() in (".wav", ".mp3", ".flac"):
|
||||
text = name2text.get(f.name, {}).get("text", "") or name2text.get(f.stem, {}).get("text", "")
|
||||
label = f"{f.name}" + (f" | {text[:30]}" if text else "")
|
||||
emotion = emotion_map.get(f.name, "") or emotion_map.get(f.stem, "")
|
||||
label = f"{f.name}"
|
||||
if text:
|
||||
label += f" | {text[:30]}"
|
||||
if emotion:
|
||||
label += f" | 【{emotion}】"
|
||||
samples.append((label, str(f)))
|
||||
choices = [s[0] for s in samples]
|
||||
value = samples[0][0] if samples else ""
|
||||
@ -1268,28 +1302,33 @@ def on_model_name_change(model_name: str):
|
||||
|
||||
|
||||
def on_ref_sample_change(sample_label: str, model_name: str):
|
||||
"""训练样本选择变化时,更新预览播放器。"""
|
||||
"""训练样本选择变化时,更新预览播放器与情绪/括注文本框。"""
|
||||
if not sample_label or not model_name:
|
||||
return gr.Audio(value=None)
|
||||
return gr.Audio(value=None), ""
|
||||
from config import exp_root
|
||||
|
||||
wav_name = sample_label.split(" | ")[0]
|
||||
audio_path = Path(exp_root) / model_name / "5-wav32k" / wav_name
|
||||
return gr.Audio(value=str(audio_path) if audio_path.exists() else None)
|
||||
emotion_map = _read_emotion_map_for_webui(model_name)
|
||||
emotion = emotion_map.get(wav_name, "") or emotion_map.get(Path(wav_name).stem, "")
|
||||
return gr.Audio(value=str(audio_path) if audio_path.exists() else None), emotion
|
||||
|
||||
|
||||
def on_apply_sample(sample_label: str, model_name: str):
|
||||
"""应用选中样本:写入参考音频路径和参考文本。"""
|
||||
"""应用选中样本:写入参考音频路径、参考文本、情绪/括注。"""
|
||||
if not sample_label or not model_name:
|
||||
return None, "", gr.Dropdown()
|
||||
return None, "", gr.Dropdown(), ""
|
||||
from config import exp_root
|
||||
|
||||
wav_name = sample_label.split(" | ")[0]
|
||||
audio_path = Path(exp_root) / model_name / "5-wav32k" / wav_name
|
||||
logs_dir = Path(exp_root) / model_name
|
||||
name2text = _read_name2text_for_webui(logs_dir)
|
||||
emotion_map = _read_emotion_map_for_webui(model_name)
|
||||
text = name2text.get(wav_name, {}).get("text", "") or name2text.get(Path(wav_name).stem, {}).get("text", "")
|
||||
return (str(audio_path) if audio_path.exists() else None), text, gr.Dropdown()
|
||||
emotion = emotion_map.get(wav_name, "") or emotion_map.get(Path(wav_name).stem, "")
|
||||
audio_out = str(audio_path) if audio_path.exists() else None
|
||||
return audio_out, text, gr.Dropdown(), emotion
|
||||
|
||||
|
||||
def _scan_model_weights_for_webui() -> dict[str, dict[str, list[str]]]:
|
||||
@ -1412,6 +1451,12 @@ with gr.Blocks(title="GPT-SoVITS WebUI", analytics_enabled=False, js=js, css=css
|
||||
)
|
||||
)
|
||||
prompt_text = gr.Textbox(label=i18n("参考音频的文本"), value="", lines=5, max_lines=5, scale=1)
|
||||
ref_emotion_text = gr.Textbox(
|
||||
label=i18n("情绪/括注(来自训练样本,无数据留空)"),
|
||||
value="",
|
||||
interactive=False,
|
||||
scale=1,
|
||||
)
|
||||
with gr.Column(scale=14):
|
||||
prompt_language = gr.Dropdown(
|
||||
label=i18n("参考音频的语种"),
|
||||
@ -1584,12 +1629,12 @@ with gr.Blocks(title="GPT-SoVITS WebUI", analytics_enabled=False, js=js, css=css
|
||||
ref_sample_dropdown.change(
|
||||
fn=on_ref_sample_change,
|
||||
inputs=[ref_sample_dropdown, model_name_dropdown],
|
||||
outputs=[ref_sample_player],
|
||||
outputs=[ref_sample_player, ref_emotion_text],
|
||||
)
|
||||
apply_sample_btn.click(
|
||||
fn=on_apply_sample,
|
||||
inputs=[ref_sample_dropdown, model_name_dropdown],
|
||||
outputs=[inp_ref, prompt_text, ref_sample_dropdown],
|
||||
outputs=[inp_ref, prompt_text, ref_sample_dropdown, ref_emotion_text],
|
||||
)
|
||||
auto_select_weights_btn.click(
|
||||
fn=on_auto_select_weights,
|
||||
|
||||
@ -20,6 +20,7 @@ import os.path
|
||||
from text.cleaner import clean_text
|
||||
from transformers import AutoModelForMaskedLM, AutoTokenizer
|
||||
from tools.my_utils import clean_path
|
||||
from tools.list_metadata import parse_list_line
|
||||
|
||||
# inp_text=sys.argv[1]
|
||||
# inp_wav_dir=sys.argv[2]
|
||||
@ -126,7 +127,12 @@ if os.path.exists(txt_path) == False:
|
||||
}
|
||||
for line in lines[int(i_part) :: int(all_parts)]:
|
||||
try:
|
||||
wav_name, spk_name, language, text = line.split("|")
|
||||
# 兼容 4/5/6 列 .list:parse_list_line 只取训练所需前 4 列,
|
||||
# 额外的 emotion/remark 列(proplus-hc-dev 分支)被静默忽略,不影响训练。
|
||||
item = parse_list_line(line)
|
||||
if item is None:
|
||||
raise ValueError("list 行列数不足,需至少 4 列:wav_path|speaker_name|language|text")
|
||||
wav_name, language, text = item.wav_path, item.language, item.text
|
||||
# todo.append([name,text,"zh"])
|
||||
if language in language_v1_to_language_v2.keys():
|
||||
todo.append([wav_name, text, language_v1_to_language_v2.get(language, language)])
|
||||
|
||||
33
api_v2.py
33
api_v2.py
@ -637,6 +637,36 @@ def _read_name2text(logs_dir: Path) -> dict[str, dict[str, str]]:
|
||||
return output
|
||||
|
||||
|
||||
def _read_emotion_map(model_name: str) -> dict[str, str]:
|
||||
"""读取 ASR 标注 .list 中的 emotion 列,返回 {audio_name|stem: emotion}。
|
||||
|
||||
扫描 output/asr_opt/<model_name>/ 下所有 *.list(兼容 4/5/6 列)。
|
||||
兼容 proplus-hc-dev 分支训练数据:emotion 为第 5 列,缺省为空串。
|
||||
无 .list 或无 emotion 列时返回空 dict。
|
||||
"""
|
||||
from tools.list_metadata import parse_list_line
|
||||
|
||||
emotion_map: dict[str, str] = {}
|
||||
list_dir = Path("output") / "asr_opt" / model_name
|
||||
if not list_dir.exists():
|
||||
return emotion_map
|
||||
for list_path in sorted(list_dir.glob("*.list")):
|
||||
try:
|
||||
for line in list_path.read_text(encoding="utf-8", errors="ignore").splitlines():
|
||||
item = parse_list_line(line)
|
||||
if item is None or not item.emotion:
|
||||
continue
|
||||
name = os.path.basename(item.wav_path.strip("\"'"))
|
||||
if not name:
|
||||
continue
|
||||
emotion_map[name] = item.emotion
|
||||
emotion_map[Path(name).stem] = item.emotion
|
||||
except Exception:
|
||||
continue
|
||||
return emotion_map
|
||||
|
||||
|
||||
|
||||
def _safe_logs_subpath(model_name: str, *parts: str) -> Path | None:
|
||||
"""解析 logs/<model_name>/<parts...> 路径并做目录穿越校验。
|
||||
|
||||
@ -706,6 +736,7 @@ async def list_model_samples(model_name: str):
|
||||
if logs_abs is None or not logs_abs.exists():
|
||||
return JSONResponse(status_code=404, content={"message": f"model '{model_name}' not found"})
|
||||
name2text = _read_name2text(logs_abs)
|
||||
emotion_map = _read_emotion_map(model_name)
|
||||
wav_dir = logs_abs / "5-wav32k"
|
||||
samples: list[dict] = []
|
||||
if wav_dir.exists():
|
||||
@ -718,11 +749,13 @@ async def list_model_samples(model_name: str):
|
||||
if entry is None:
|
||||
continue # 与 name2text 取交集,只返回有标注的样本
|
||||
rel = os.path.relpath(f, now_dir)
|
||||
emotion = emotion_map.get(f.name, "") or emotion_map.get(f.stem, "")
|
||||
samples.append({
|
||||
"audio_name": f.name,
|
||||
"audio_path": rel.replace(os.sep, "/"),
|
||||
"text": entry["text"],
|
||||
"lang": entry["lang"],
|
||||
"emotion": emotion,
|
||||
})
|
||||
return {"model_name": model_name, "samples": samples, "total": len(samples)}
|
||||
|
||||
|
||||
61
tools/list_metadata.py
Normal file
61
tools/list_metadata.py
Normal file
@ -0,0 +1,61 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ListLine:
|
||||
"""一条 `.list` 标注行(兼容 4/5/6 列)。
|
||||
|
||||
列定义: wav_path|speaker_name|language|text[|emotion|remark]
|
||||
训练只用前 4 列(training_fields);emotion / remark 为元数据,
|
||||
不参与模型训练,用于标注与参考音频展示。
|
||||
"""
|
||||
|
||||
wav_path: str
|
||||
speaker_name: str
|
||||
language: str
|
||||
text: str
|
||||
emotion: str = ""
|
||||
remark: str = ""
|
||||
|
||||
@property
|
||||
def training_fields(self):
|
||||
return [self.wav_path, self.speaker_name, self.language, self.text]
|
||||
|
||||
|
||||
def _clean(value):
|
||||
return str(value or "").strip()
|
||||
|
||||
|
||||
def parse_list_line(line):
|
||||
"""解析一行 `.list`。列数 < 4 返回 None;emotion/remark 缺省为空串。"""
|
||||
parts = str(line or "").rstrip("\r\n").split("|")
|
||||
if len(parts) < 4:
|
||||
return None
|
||||
return ListLine(
|
||||
wav_path=_clean(parts[0]),
|
||||
speaker_name=_clean(parts[1]),
|
||||
language=_clean(parts[2]),
|
||||
text=_clean(parts[3]),
|
||||
emotion=_clean(parts[4]) if len(parts) >= 5 else "",
|
||||
remark=_clean(parts[5]) if len(parts) >= 6 else "",
|
||||
)
|
||||
|
||||
|
||||
def format_list_line(item):
|
||||
"""把 ListLine 格式化为 6 列 `.list` 行(emotion/remark 为空也保留分隔符)。
|
||||
|
||||
注意:写出 6 列以保证与 proplus-hc-dev 分支互操作,主分支训练侧
|
||||
(1-get-text.py)已用 parse_list_line 只消费前 4 列,多余列不影响训练。
|
||||
"""
|
||||
return "|".join(
|
||||
[
|
||||
str(item.wav_path or ""),
|
||||
str(item.speaker_name or ""),
|
||||
str(item.language or ""),
|
||||
str(item.text or ""),
|
||||
str(item.emotion or ""),
|
||||
str(item.remark or ""),
|
||||
]
|
||||
)
|
||||
@ -32,6 +32,7 @@ g_batch = 10
|
||||
g_text_list = []
|
||||
g_audio_list = []
|
||||
g_checkbox_list = []
|
||||
g_emotion_list = []
|
||||
g_data_json = []
|
||||
|
||||
|
||||
@ -43,7 +44,13 @@ def reload_data(index, batch):
|
||||
datas = g_data_json[index : index + batch]
|
||||
output = []
|
||||
for d in datas:
|
||||
output.append({g_json_key_text: d[g_json_key_text], g_json_key_path: d[g_json_key_path]})
|
||||
output.append(
|
||||
{
|
||||
g_json_key_text: d[g_json_key_text],
|
||||
g_json_key_path: d[g_json_key_path],
|
||||
"emotion": d.get("emotion", ""),
|
||||
}
|
||||
)
|
||||
return output
|
||||
|
||||
|
||||
@ -52,26 +59,26 @@ def b_change_index(index, batch):
|
||||
g_index, g_batch = index, batch
|
||||
datas = reload_data(index, batch)
|
||||
output = []
|
||||
# 文本框组
|
||||
for i, _ in enumerate(datas):
|
||||
output.append(
|
||||
# gr.Textbox(
|
||||
# label=f"Text {i+index}",
|
||||
# value=_[g_json_key_text]#text
|
||||
# )
|
||||
{"__type__": "update", "label": f"Text {i + index}", "value": _[g_json_key_text]}
|
||||
)
|
||||
for _ in range(g_batch - len(datas)):
|
||||
output.append({"__type__": "update", "label": "Text", "value": ""})
|
||||
# 情绪/括注框组(与文本框一一对应)
|
||||
for i, _ in enumerate(datas):
|
||||
output.append(
|
||||
# gr.Textbox(
|
||||
# label=f"Text",
|
||||
# value=""
|
||||
# )
|
||||
{"__type__": "update", "label": "Text", "value": ""}
|
||||
{"__type__": "update", "label": f"情绪/括注 {i + index}", "value": _.get("emotion", "")}
|
||||
)
|
||||
for _ in range(g_batch - len(datas)):
|
||||
output.append({"__type__": "update", "label": "情绪/括注", "value": ""})
|
||||
# 音频组
|
||||
for _ in datas:
|
||||
output.append(_[g_json_key_path])
|
||||
for _ in range(g_batch - len(datas)):
|
||||
output.append(None)
|
||||
# 复选框组
|
||||
for _ in range(g_batch):
|
||||
output.append(False)
|
||||
return output
|
||||
@ -93,8 +100,12 @@ def b_previous_index(index, batch):
|
||||
return 0, *b_change_index(0, batch)
|
||||
|
||||
|
||||
def b_submit_change(*text_list):
|
||||
def b_submit_change(*args):
|
||||
# 输入约定:[text×batch] + [emotion×batch](由事件绑定顺序拼成)
|
||||
global g_data_json
|
||||
half = g_batch
|
||||
text_list = args[:half]
|
||||
emotion_list = args[half:]
|
||||
change = False
|
||||
for i, new_text in enumerate(text_list):
|
||||
if g_index + i <= g_max_json_index:
|
||||
@ -102,6 +113,12 @@ def b_submit_change(*text_list):
|
||||
if g_data_json[g_index + i][g_json_key_text] != new_text:
|
||||
g_data_json[g_index + i][g_json_key_text] = new_text
|
||||
change = True
|
||||
for i, new_emotion in enumerate(emotion_list):
|
||||
if g_index + i <= g_max_json_index:
|
||||
new_emotion = (new_emotion or "").strip()
|
||||
if g_data_json[g_index + i].get("emotion", "") != new_emotion:
|
||||
g_data_json[g_index + i]["emotion"] = new_emotion
|
||||
change = True
|
||||
if change:
|
||||
b_save_file()
|
||||
return g_index, *b_change_index(g_index, g_batch)
|
||||
@ -226,13 +243,25 @@ def b_save_json():
|
||||
|
||||
|
||||
def b_save_list():
|
||||
from tools.list_metadata import format_list_line, ListLine
|
||||
|
||||
with open(g_load_file, "w", encoding="utf-8") as file:
|
||||
for data in g_data_json:
|
||||
wav_path = data["wav_path"]
|
||||
speaker_name = data["speaker_name"]
|
||||
language = data["language"]
|
||||
text = data["text"]
|
||||
file.write(f"{wav_path}|{speaker_name}|{language}|{text}".strip() + "\n")
|
||||
# 写出 6 列以保留 emotion/remark(proplus-hc-dev 互操作);
|
||||
# 主分支训练侧(1-get-text.py)只消费前 4 列,多余列不影响训练。
|
||||
file.write(
|
||||
format_list_line(
|
||||
ListLine(
|
||||
wav_path=data.get("wav_path", ""),
|
||||
speaker_name=data.get("speaker_name", ""),
|
||||
language=data.get("language", ""),
|
||||
text=data.get("text", ""),
|
||||
emotion=data.get("emotion", ""),
|
||||
remark=data.get("remark", ""),
|
||||
)
|
||||
).strip()
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
|
||||
def b_load_json():
|
||||
@ -245,17 +274,25 @@ def b_load_json():
|
||||
|
||||
def b_load_list():
|
||||
global g_data_json, g_max_json_index
|
||||
from tools.list_metadata import parse_list_line
|
||||
|
||||
with open(g_load_file, "r", encoding="utf-8") as source:
|
||||
data_list = source.readlines()
|
||||
for _ in data_list:
|
||||
data = _.split("|")
|
||||
if len(data) == 4:
|
||||
wav_path, speaker_name, language, text = data
|
||||
g_data_json.append(
|
||||
{"wav_path": wav_path, "speaker_name": speaker_name, "language": language, "text": text.strip()}
|
||||
)
|
||||
else:
|
||||
print("error line:", data)
|
||||
item = parse_list_line(_)
|
||||
if item is None:
|
||||
print("error line:", _)
|
||||
continue
|
||||
g_data_json.append(
|
||||
{
|
||||
"wav_path": item.wav_path,
|
||||
"speaker_name": item.speaker_name,
|
||||
"language": item.language,
|
||||
"text": item.text,
|
||||
"emotion": item.emotion,
|
||||
"remark": item.remark,
|
||||
}
|
||||
)
|
||||
g_max_json_index = len(g_data_json) - 1
|
||||
|
||||
|
||||
@ -338,9 +375,16 @@ if __name__ == "__main__":
|
||||
text = gr.Textbox(label="Text", visible=True, scale=5)
|
||||
audio_output = gr.Audio(label="Output Audio", visible=True, scale=5)
|
||||
audio_check = gr.Checkbox(label="Yes", show_label=True, info="Choose Audio", scale=1)
|
||||
emotion = gr.Textbox(
|
||||
label="情绪/括注",
|
||||
visible=True,
|
||||
scale=3,
|
||||
placeholder="可选:台词对应的表演情绪或括注",
|
||||
)
|
||||
g_text_list.append(text)
|
||||
g_audio_list.append(audio_output)
|
||||
g_checkbox_list.append(audio_check)
|
||||
g_emotion_list.append(emotion)
|
||||
|
||||
with gr.Row():
|
||||
batchsize_slider = gr.Slider(
|
||||
@ -356,15 +400,16 @@ if __name__ == "__main__":
|
||||
index_slider,
|
||||
batchsize_slider,
|
||||
],
|
||||
outputs=[*g_text_list, *g_audio_list, *g_checkbox_list],
|
||||
outputs=[*g_text_list, *g_emotion_list, *g_audio_list, *g_checkbox_list],
|
||||
)
|
||||
|
||||
btn_submit_change.click(
|
||||
b_submit_change,
|
||||
inputs=[
|
||||
*g_text_list,
|
||||
*g_emotion_list,
|
||||
],
|
||||
outputs=[index_slider, *g_text_list, *g_audio_list, *g_checkbox_list],
|
||||
outputs=[index_slider, *g_text_list, *g_emotion_list, *g_audio_list, *g_checkbox_list],
|
||||
)
|
||||
|
||||
btn_previous_index.click(
|
||||
@ -373,7 +418,7 @@ if __name__ == "__main__":
|
||||
index_slider,
|
||||
batchsize_slider,
|
||||
],
|
||||
outputs=[index_slider, *g_text_list, *g_audio_list, *g_checkbox_list],
|
||||
outputs=[index_slider, *g_text_list, *g_emotion_list, *g_audio_list, *g_checkbox_list],
|
||||
)
|
||||
|
||||
btn_next_index.click(
|
||||
@ -382,25 +427,25 @@ if __name__ == "__main__":
|
||||
index_slider,
|
||||
batchsize_slider,
|
||||
],
|
||||
outputs=[index_slider, *g_text_list, *g_audio_list, *g_checkbox_list],
|
||||
outputs=[index_slider, *g_text_list, *g_emotion_list, *g_audio_list, *g_checkbox_list],
|
||||
)
|
||||
|
||||
btn_delete_audio.click(
|
||||
b_delete_audio,
|
||||
inputs=[*g_checkbox_list],
|
||||
outputs=[index_slider, *g_text_list, *g_audio_list, *g_checkbox_list],
|
||||
outputs=[index_slider, *g_text_list, *g_emotion_list, *g_audio_list, *g_checkbox_list],
|
||||
)
|
||||
|
||||
btn_merge_audio.click(
|
||||
b_merge_audio,
|
||||
inputs=[interval_slider, *g_checkbox_list],
|
||||
outputs=[index_slider, *g_text_list, *g_audio_list, *g_checkbox_list],
|
||||
outputs=[index_slider, *g_text_list, *g_emotion_list, *g_audio_list, *g_checkbox_list],
|
||||
)
|
||||
|
||||
btn_audio_split.click(
|
||||
b_audio_split,
|
||||
inputs=[splitpoint_slider, *g_checkbox_list],
|
||||
outputs=[index_slider, *g_text_list, *g_audio_list, *g_checkbox_list],
|
||||
outputs=[index_slider, *g_text_list, *g_emotion_list, *g_audio_list, *g_checkbox_list],
|
||||
)
|
||||
|
||||
btn_invert_selection.click(b_invert_selection, inputs=[*g_checkbox_list], outputs=[*g_checkbox_list])
|
||||
@ -413,7 +458,7 @@ if __name__ == "__main__":
|
||||
index_slider,
|
||||
batchsize_slider,
|
||||
],
|
||||
outputs=[*g_text_list, *g_audio_list, *g_checkbox_list],
|
||||
outputs=[*g_text_list, *g_emotion_list, *g_audio_list, *g_checkbox_list],
|
||||
)
|
||||
|
||||
demo.launch(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user