File size: 5,216 Bytes
a5b1b77
46e890d
af05b73
a5b1b77
 
 
c3c7ab2
 
 
 
 
a5b1b77
 
 
 
 
 
 
 
2b56895
c3c7ab2
91f5562
 
 
 
 
 
c3c7ab2
 
 
 
 
 
 
 
 
 
 
 
 
 
f7da1c2
c3c7ab2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91f5562
 
 
 
 
 
 
c3c7ab2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91f5562
 
c3c7ab2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2b56895
c3c7ab2
 
 
 
 
 
 
 
 
a5b1b77
c3c7ab2
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import os
os.environ.setdefault("CHATTERBOX_FLASH_ENGINE", "torch")
os.environ.setdefault("NUMBA_DISABLE_CUDA", "1")

import spaces
import torch
import numpy as np
import gradio as gr

import subprocess
import sys

subprocess.run(
    [sys.executable, "-m", "pip", "install", "--no-deps",
     "chatterbox-tts==0.1.7", "chatterbox-flash==0.1.0"],
    check=True,
)

from chatterbox_flash import ChatterboxFlashTTS

MODEL_ID = "ResembleAI/chatterbox-flash"
# Chatterbox-Flash has no built-in voice: `generate()` raises
# "Conditioning not prepared" unless conditionals exist. Ship a default
# reference clip so zero-shot generation works without a user upload.
DEFAULT_AUDIO_PROMPT = os.path.join(
    os.path.dirname(os.path.abspath(__file__)), "female_shadowheart4.flac"
)
_tts = None


def get_tts():
    global _tts
    if _tts is None:
        print(f"Loading Chatterbox-Flash from {MODEL_ID}...")
        _tts = ChatterboxFlashTTS.from_pretrained(
            MODEL_ID, device="cuda", dtype=torch.bfloat16,
        )
        print("Model loaded successfully.")
    return _tts


@spaces.GPU(duration=60)
def generate_tts(
    text_input: str,
    audio_prompt_path: str | None = None,
    exaggeration: float = 0.5,
    temperature: float = 0.6,
    cfg_scale: float = 1.0,
    num_steps: int = 10,
    seed_num: int = 0,
):
    """Generate speech from text using Chatterbox-Flash block-diffusion TTS."""
    tts = get_tts()

    if seed_num != 0:
        torch.manual_seed(int(seed_num))
        torch.cuda.manual_seed(int(seed_num))
        np.random.seed(int(seed_num))

    generate_kwargs = {
        "exaggeration": exaggeration,
        "temperature": temperature,
        "cfg_scale": cfg_scale,
        "num_steps": num_steps,
        "backend": "torch",
    }

    # Fall back to the bundled reference clip when the user supplies none, so
    # conditionals are always prepared before generation.
    prompt_path = audio_prompt_path or DEFAULT_AUDIO_PROMPT
    generate_kwargs["audio_prompt_path"] = prompt_path

    if tts.conds is None:
        tts.prepare_conditionals(prompt_path, exaggeration=exaggeration)

    wav = tts.generate(text_input[:300], **generate_kwargs)
    return (tts.sr, wav.squeeze(0).cpu().numpy())


CSS = """
#col-container { max-width: 1100px; margin: 0 auto; }
.dark .gradio-container { color: var(--body-text-color); }
"""

with gr.Blocks() as demo:
    gr.Markdown(
        """
        # Chatterbox-Flash TTS
        Prior-calibrated block-diffusion zero-shot TTS by Resemble AI.
        Provide a reference audio clip to clone a voice, or generate with the default voice.

        [Paper](https://huggingface.co/papers/2605.30748) · [Model](https://huggingface.co/ResembleAI/chatterbox-flash) · [GitHub](https://github.com/resemble-ai/chatterbox-flash)
        """
    )

    with gr.Row(elem_id="col-container"):
        with gr.Column(scale=3):
            text = gr.Textbox(
                value="Sometimes it's better to just let things slide, you know?",
                label="Text to synthesize (max 300 chars)",
                max_lines=5,
            )
            ref_wav = gr.Audio(
                sources=["upload", "microphone"],
                type="filepath",
                value=DEFAULT_AUDIO_PROMPT,
                label="Reference Audio (for voice cloning) — defaults to the bundled voice",
            )

            with gr.Accordion("Advanced settings", open=False):
                exaggeration = gr.Slider(
                    0.25, 2.0, step=0.05,
                    label="Exaggeration (0.5=neutral, higher=more expressive)",
                    value=0.5,
                )
                temperature = gr.Slider(
                    0.05, 2.0, step=0.05,
                    label="Temperature",
                    value=0.6,
                )
                cfg_scale = gr.Slider(
                    0.2, 1.0, step=0.05,
                    label="CFG Scale",
                    value=1.0,
                )
                num_steps = gr.Slider(
                    1, 30, step=1,
                    label="Denoising Steps",
                    value=10,
                )
                seed_num = gr.Number(
                    value=0, label="Seed (0=random)", precision=0,
                )

            run_btn = gr.Button("Generate", variant="primary")

        with gr.Column(scale=2):
            audio_output = gr.Audio(label="Output Audio")

    gr.Examples(
        examples=[
            ["Sometimes it's better to just let things slide, you know?"],
            ["The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs."],
            ["In the depths of winter, I finally learned that within me lay an invincible summer."],
        ],
        inputs=[text],
        outputs=[audio_output],
        fn=generate_tts,
        cache_examples=True,
        cache_mode="lazy",
    )

    run_btn.click(
        fn=generate_tts,
        inputs=[
            text, ref_wav, exaggeration,
            temperature, cfg_scale, num_steps, seed_num,
        ],
        outputs=[audio_output],
        api_name="generate",
    )

demo.launch(mcp_server=True, theme=gr.themes.Citrus(), css=CSS)