lenML/ChatTTS-Forge
301
1import gradio as gr2import torch3from modules.normalization import text_normalize4from modules.webui.webui_utils import (5 get_speakers,6 get_styles,7 split_long_text,8)9from modules.hf import spaces10 11 12# NOTE: 因为 text_normalize 需要使用 tokenizer13@torch.inference_mode()14@spaces.GPU15def merge_dataframe_to_ssml(dataframe, spk, style, seed):16 if style == "*auto":17 style = None18 if spk == "-1" or spk == -1:19 spk = None20 if seed == -1 or seed == "-1":21 seed = None22 23 ssml = ""24 indent = " " * 225 26 for i, row in dataframe.iterrows():27 ssml += f"{indent}<voice"28 if spk:29 ssml += f' spk="{spk}"'30 if style:31 ssml += f' style="{style}"'32 if seed:33 ssml += f' seed="{seed}"'34 ssml += ">\n"35 ssml += f"{indent}{indent}{text_normalize(row.iloc[1])}\n"36 ssml += f"{indent}</voice>\n"37 return f"<speak version='0.1'>\n{ssml}</speak>"38 39 40# 长文本处理41# 可以输入长文本,并选择切割方法,切割之后可以将拼接的SSML发送到SSML tab42# 根据 。 句号切割,切割之后显示到 data table43def create_spliter_tab(ssml_input, tabs):44 speakers = get_speakers()45 46 def get_speaker_show_name(spk):47 if spk.gender == "*" or spk.gender == "":48 return spk.name49 return f"{spk.gender} : {spk.name}"50 51 speaker_names = ["*random"] + [52 get_speaker_show_name(speaker) for speaker in speakers53 ]54 55 styles = ["*auto"] + [s.get("name") for s in get_styles()]56 57 with gr.Row():58 with gr.Column(scale=1):59 # 选择说话人 选择风格 选择seed60 with gr.Group():61 gr.Markdown("🗣️Speaker")62 spk_input_text = gr.Textbox(63 label="Speaker (Text or Seed)",64 value="female2",65 show_label=False,66 )67 spk_input_dropdown = gr.Dropdown(68 choices=speaker_names,69 interactive=True,70 value="female : female2",71 show_label=False,72 )73 spk_rand_button = gr.Button(74 value="🎲",75 variant="secondary",76 )77 with gr.Group():78 gr.Markdown("🎭Style")79 style_input_dropdown = gr.Dropdown(80 choices=styles,81 interactive=True,82 show_label=False,83 value="*auto",84 )85 with gr.Group():86 gr.Markdown("🗣️Seed")87 infer_seed_input = gr.Number(88 value=42,89 label="Inference Seed",90 show_label=False,91 minimum=-1,92 maximum=2**32 - 1,93 )94 infer_seed_rand_button = gr.Button(95 value="🎲",96 variant="secondary",97 )98 99 send_btn = gr.Button("📩Send to SSML", variant="primary")100 101 with gr.Column(scale=3):102 with gr.Group():103 gr.Markdown("📝Long Text Input")104 gr.Markdown("- 此页面用于处理超长文本")105 gr.Markdown("- 切割后,可以选择说话人、风格、seed,然后发送到SSML")106 long_text_input = gr.Textbox(107 label="Long Text Input",108 lines=10,109 placeholder="输入长文本",110 elem_id="long-text-input",111 show_label=False,112 )113 long_text_split_button = gr.Button("🔪Split Text")114 115 with gr.Row():116 with gr.Column(scale=3):117 with gr.Group():118 gr.Markdown("🎨Output")119 long_text_output = gr.DataFrame(120 headers=["index", "text", "length"],121 datatype=["number", "str", "number"],122 elem_id="long-text-output",123 interactive=False,124 wrap=True,125 value=[],126 )127 128 spk_input_dropdown.change(129 fn=lambda x: x.startswith("*") and "-1" or x.split(":")[-1].strip(),130 inputs=[spk_input_dropdown],131 outputs=[spk_input_text],132 )133 spk_rand_button.click(134 lambda x: int(torch.randint(0, 2**32 - 1, (1,)).item()),135 inputs=[spk_input_text],136 outputs=[spk_input_text],137 )138 infer_seed_rand_button.click(139 lambda x: int(torch.randint(0, 2**32 - 1, (1,)).item()),140 inputs=[infer_seed_input],141 outputs=[infer_seed_input],142 )143 long_text_split_button.click(144 split_long_text,145 inputs=[long_text_input],146 outputs=[long_text_output],147 )148 149 infer_seed_rand_button.click(150 lambda x: int(torch.randint(0, 2**32 - 1, (1,)).item()),151 inputs=[infer_seed_input],152 outputs=[infer_seed_input],153 )154 155 send_btn.click(156 merge_dataframe_to_ssml,157 inputs=[158 long_text_output,159 spk_input_text,160 style_input_dropdown,161 infer_seed_input,162 ],163 outputs=[ssml_input],164 )165 166 def change_tab():167 return gr.Tabs(selected="ssml")168 169 send_btn.click(change_tab, inputs=[], outputs=[tabs])170 