File size: 7,254 Bytes
85bc603 e4ae2bb 85bc603 e4ae2bb 85bc603 e4ae2bb 85bc603 e4ae2bb 85bc603 e4ae2bb 85bc603 e4ae2bb 85bc603 e4ae2bb 85bc603 e4ae2bb 85bc603 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | #!/usr/bin/env python
"""Dynamic Accent: local Gradio demo (single GPU).
pip install -r requirements.txt # includes gradio
python app.py --cosyvoice_dir ../CosyVoice # then open http://127.0.0.1:7860
Text box -> optional auto-tag (editable preview of the tagged text) -> version + voice + rate -> Synthesize, or
Compare to hear Phase 3 and Flash on the same text, seed and voice. Loaded versions stay resident (Phase 2/3 share one
CosyVoice2 base; Flash has its own CosyVoice3 base), so a GPU with >= 24 GB is recommended. Flash needs CUDA.
"""
import argparse, json, os, sys, types
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
import infer # noqa: E402
from tagger import Tagger, parse_map, strip_tags, TAGGED, LANGS # noqa: E402
EXAMPLES = [
"Omo, I'm very excited to announce this new model, ọ na-eme m obi ụtọ, I no go lie give you",
"Ẹ káàbọ̀ sí ìpàdé wa òní. Today we will talk about the new school calendar.",
"Barka da yamma, yaya kuke? The weather report says it will rain tomorrow morning.",
"Kedu ka ị mere? Abeg no vex, I go call you for evening.",
]
class Engine:
def __init__(self, a):
self.a = a
self.models = {}
self.cosy = self.tok = None
self.taggers = {}
def get(self, version, voice):
if version not in self.models:
ns = types.SimpleNamespace(**vars(self.a))
ns.voice = voice
S = infer.build(version, ns, self.cosy, self.tok, copy_llm=self.cosy is not None)
if S.cosy is not None: # flash (CosyVoice3) keeps its own base
self.cosy, self.tok = S.cosy, S.tok
S._rate0 = S.rate
self.models[version] = S
S = self.models[version]
if getattr(S, "_voice_name", None) != voice:
S.set_voice(voice, self.a.voices); S._voice_name = voice
return S
def tag(self, text, lang_map, use_lid):
key = (lang_map, use_lid)
if key not in self.taggers:
self.taggers[key] = Tagger("glotlid" if use_lid else None, parse_map(lang_map))
return self.taggers[key].tag(strip_tags(text))
def say(self, text, version, voice, rate, seed):
S = self.get(version, voice)
S.rate = S._rate0 if rate == "default" else (infer.RATES.index(rate) if rate in infer.RATES else 0)
w = S.say(text, seed=int(seed))
return S.sr, w
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--cosyvoice_dir", default=os.environ.get("COSYVOICE_REPO", os.path.join(HERE, "CosyVoice")))
ap.add_argument("--model_dir", default=os.environ.get("COSYVOICE2_DIR", os.path.join(HERE, "pretrained", "CosyVoice2-0.5B")))
ap.add_argument("--host", default="127.0.0.1")
ap.add_argument("--port", type=int, default=7860)
ap.add_argument("--share", action="store_true")
a = ap.parse_args()
for k, v in dict(lm="", flow="", prompts=os.path.join(HERE, "prompts", "prompts.json"),
voices=os.path.join(HERE, "prompts", "voices.json"), dtype="auto", stitch=False, rate="default",
frontend="codeswitch", frontend_lang="eng", no_diacritics=False, experimental_translate=False,
cv3_dir=os.environ.get("CV3_DIR", os.path.join(HERE, "pretrained", "Fun-CosyVoice3-0.5B-2512"))).items():
setattr(a, k, v)
if not os.path.isdir(a.model_dir):
from huggingface_hub import snapshot_download
snapshot_download("FunAudioLLM/CosyVoice2-0.5B", local_dir=a.model_dir)
import gradio as gr
E = Engine(a)
S = infer.SETTINGS
voices = S.get("voices", ["anchor_male", "waxal_ibo_5_female", "waxal_yor_5_female"])
rec = S.get("recommended", {})
rates = ["default"] + S.get("rates", ["none", "very slow", "slow", "normal", "fast", "very fast"])
def prepare(text, auto, lang_map, use_lid, force):
if not text.strip():
return ""
if force and force != "off":
return f"<{force}>{strip_tags(text)}</{force}>"
if auto and not TAGGED.search(text):
return E.tag(text, lang_map or "", bool(use_lid))
return text
def hint(version, voice):
r = rec.get(version, [])
return "" if voice in r else f"Tip: {version} is recommended with {', '.join(r)}. {rec.get('note', '')}"
def synth(text, tagged, auto, lang_map, use_lid, force, version, voice, rate, seed):
t = tagged.strip() if tagged.strip() else prepare(text, auto, lang_map, use_lid, force)
sr, w = E.say(t, version, voice, rate, seed)
return t, (sr, w), hint(version, voice)
def compare(text, tagged, auto, lang_map, use_lid, force, voice, rate, seed):
t = tagged.strip() if tagged.strip() else prepare(text, auto, lang_map, use_lid, force)
a3 = E.say(t, "phase3", voice, rate, seed)
af = E.say(t, "flash", voice, rate, seed)
return t, a3, af
with gr.Blocks(title="Dynamic Accent: code-switching TTS") as demo:
gr.Markdown("## Dynamic Accent: code-switching TTS\nEnglish, Nigerian Pidgin, Igbo, Yoruba and Hausa, mixed in one "
"sentence. Tag spans with `<eng> <pcm> <ibo> <yor> <hau>`, or turn on auto-tag and edit its preview.")
text = gr.Textbox(label="Text", lines=3, value=EXAMPLES[0])
with gr.Row():
auto = gr.Checkbox(label="Auto-tag languages", value=True)
use_lid = gr.Checkbox(label="Use GlotLID (1.7 GB download, needs fasttext)", value=False)
force = gr.Dropdown(["off"] + LANGS, value="off", label="Force one language")
lang_map = gr.Textbox(label="Word overrides (e.g. omo=pcm,sha=pcm)", value="")
tag_btn = gr.Button("Preview tags")
tagged = gr.Textbox(label="Tagged text (edit before synthesis; leave empty to tag automatically)", lines=3)
with gr.Row():
version = gr.Dropdown(sorted(infer.VERSIONS), value=S.get("default_version", "flash"), label="Version")
voice = gr.Dropdown(voices, value=S.get("default_voice", "anchor_male"), label="Voice")
rate = gr.Dropdown(rates, value="default", label="Rate (phase3/flash)")
seed = gr.Number(value=0, precision=0, label="Seed")
tip = gr.Markdown()
with gr.Row():
go = gr.Button("Synthesize", variant="primary")
cmp_btn = gr.Button("Compare Phase 3 vs Flash")
out = gr.Audio(label="Output", type="numpy")
with gr.Row():
out2 = gr.Audio(label="Phase 3", type="numpy")
out3 = gr.Audio(label="Flash", type="numpy")
gr.Examples(EXAMPLES, inputs=text)
tag_btn.click(prepare, [text, auto, lang_map, use_lid, force], tagged)
version.change(hint, [version, voice], tip)
voice.change(hint, [version, voice], tip)
go.click(synth, [text, tagged, auto, lang_map, use_lid, force, version, voice, rate, seed], [tagged, out, tip])
cmp_btn.click(compare, [text, tagged, auto, lang_map, use_lid, force, voice, rate, seed], [tagged, out2, out3])
demo.queue(max_size=8).launch(server_name=a.host, server_port=a.port, share=a.share)
if __name__ == "__main__":
main()
|