CoolFace
Apppublic

ToluMichael/Chatterbox-Multilingual-TTS

sourceHugging Faceupdated 1y agoView on Hugging Face
1likes
Run.py189 linesDownload Raw Back to root
1import random
2import numpy as np
3import torch
4from src.chatterbox.mtl_tts import ChatterboxMultilingualTTS, SUPPORTED_LANGUAGES
5import gradio as gr
6import spaces
7
8DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
9print(f"🚀 Running on device: {DEVICE}")
10
11# --- Global Model Initialization ---
12MODEL = None
13
14LANGUAGE_CONFIG = {
15    "en": {
16        "audio": "https://storage.googleapis.com/chatterbox-demo-samples/mtl_prompts/en_f1.flac",
17        "text": "Last month, we reached a new milestone with two billion views on our YouTube channel."
18    },
19    "fr": {
20        "audio": "https://storage.googleapis.com/chatterbox-demo-samples/mtl_prompts/fr_f1.flac",
21        "text": "Le mois dernier, nous avons atteint un nouveau jalon avec deux milliards de vues sur notre chaîne YouTube."
22    },
23    # Add other languages as needed...
24}
25
26# --- UI Helpers ---
27def default_audio_for_ui(lang: str) -> str | None:
28    return LANGUAGE_CONFIG.get(lang, {}).get("audio")
29
30def default_text_for_ui(lang: str) -> str:
31    return LANGUAGE_CONFIG.get(lang, {}).get("text", "")
32
33def get_supported_languages_display() -> str:
34    language_items = []
35    for code, name in sorted(SUPPORTED_LANGUAGES.items()):
36        language_items.append(f"**{name}** (`{code}`)")
37    mid = len(language_items) // 2
38    line1 = " • ".join(language_items[:mid])
39    line2 = " • ".join(language_items[mid:])
40    return f"""
41### 🌍 Supported Languages ({len(SUPPORTED_LANGUAGES)} total)
42{line1}
43
44{line2}
45"""
46
47def get_or_load_model():
48    global MODEL
49    if MODEL is None:
50        print("Model not loaded, initializing...")
51        MODEL = ChatterboxMultilingualTTS.from_pretrained(DEVICE)
52        if hasattr(MODEL, 'to') and str(MODEL.device) != DEVICE:
53            MODEL.to(DEVICE)
54        print(f"Model loaded successfully. Internal device: {getattr(MODEL, 'device', 'N/A')}")
55    return MODEL
56
57try:
58    get_or_load_model()
59except Exception as e:
60    print(f"CRITICAL: Failed to load model. Error: {e}")
61
62def set_seed(seed: int):
63    torch.manual_seed(seed)
64    if DEVICE == "cuda":
65        torch.cuda.manual_seed(seed)
66        torch.cuda.manual_seed_all(seed)
67    random.seed(seed)
68    np.random.seed(seed)
69
70def chunk_text(text: str, max_len: int = 300):
71    """Split text into manageable chunks (~sentences or max_len)."""
72    import re
73    sentences = re.split(r'(?<=[.!?]) +', text)
74    chunks, current = [], ""
75    for sent in sentences:
76        if len(current) + len(sent) <= max_len:
77            current += " " + sent
78        else:
79            chunks.append(current.strip())
80            current = sent
81    if current:
82        chunks.append(current.strip())
83    return chunks
84
85def resolve_audio_prompt(language_id: str, provided_path: str | None) -> str | None:
86    if provided_path and str(provided_path).strip():
87        return provided_path
88    return LANGUAGE_CONFIG.get(language_id, {}).get("audio")
89
90@spaces.GPU
91def generate_tts_audio(
92    text_input: str,
93    language_id: str,
94    audio_prompt_path_input: str = None,
95    exaggeration_input: float = 0.5,
96    temperature_input: float = 0.8,
97    seed_num_input: int = 0,
98    cfgw_input: float = 0.5
99) -> tuple[int, np.ndarray]:
100
101    current_model = get_or_load_model()
102    if current_model is None:
103        raise RuntimeError("TTS model is not loaded.")
104
105    if seed_num_input != 0:
106        set_seed(int(seed_num_input))
107
108    chosen_prompt = audio_prompt_path_input or default_audio_for_ui(language_id)
109    generate_kwargs = {
110        "exaggeration": exaggeration_input,
111        "temperature": temperature_input,
112        "cfg_weight": cfgw_input,
113    }
114    if chosen_prompt:
115        generate_kwargs["audio_prompt_path"] = chosen_prompt
116
117    text_chunks = chunk_text(text_input, max_len=300)
118    print(f"Splitting text into {len(text_chunks)} chunks.")
119
120    audio_pieces = []
121    for idx, chunk in enumerate(text_chunks):
122        print(f"Generating chunk {idx+1}/{len(text_chunks)}: '{chunk[:50]}...' ")
123        wav = current_model.generate(chunk, language_id=language_id, **generate_kwargs)
124        audio_pieces.append(wav.squeeze(0).numpy())
125
126    final_audio = np.concatenate(audio_pieces)
127    print("Audio generation complete.")
128    return (current_model.sr, final_audio)
129
130with gr.Blocks() as demo:
131    gr.Markdown("""
132        # Chatterbox Multilingual Demo
133        Generate high-quality multilingual speech from text with reference audio styling, supporting 23 languages.
134        """)
135
136    gr.Markdown(get_supported_languages_display())
137
138    with gr.Row():
139        with gr.Column():
140            initial_lang = "fr"
141            text = gr.Textbox(
142                value=default_text_for_ui(initial_lang),
143                label="Text to synthesize (no character limit)",
144                max_lines=10
145            )
146
147            language_id = gr.Dropdown(
148                choices=list(ChatterboxMultilingualTTS.get_supported_languages().keys()),
149                value=initial_lang,
150                label="Language"
151            )
152
153            ref_wav = gr.Audio(
154                sources=["upload", "microphone"],
155                type="filepath",
156                label="Reference Audio File (Optional)",
157                value=default_audio_for_ui(initial_lang)
158            )
159
160            exaggeration = gr.Slider(0.25, 2, step=.05, label="Exaggeration", value=.5)
161            cfg_weight = gr.Slider(0.2, 1, step=.05, label="CFG/Pace", value=0.5)
162
163            with gr.Accordion("More options", open=False):
164                seed_num = gr.Number(value=0, label="Random seed (0 for random)")
165                temp = gr.Slider(0.05, 5, step=.05, label="Temperature", value=.8)
166
167            run_btn = gr.Button("Generate", variant="primary")
168
169        with gr.Column():
170            audio_output = gr.Audio(label="Output Audio")
171
172        def on_language_change(lang, current_ref, current_text):
173            return default_audio_for_ui(lang), default_text_for_ui(lang)
174
175        language_id.change(
176            fn=on_language_change,
177            inputs=[language_id, ref_wav, text],
178            outputs=[ref_wav, text],
179            show_progress=False
180        )
181
182    run_btn.click(
183        fn=generate_tts_audio,
184        inputs=[text, language_id, ref_wav, exaggeration, temp, seed_num, cfg_weight],
185        outputs=[audio_output],
186    )
187
188demo.launch(mcp_server=True, share=True)
189