Spaces:
Running on Zero
Running on Zero
Enforce 300-char synthesis limit
Browse filesAdd a live n/300 counter (red when over) that disables Generate past the
limit, plus a server-side gr.Error guard so API/MCP callers can't bypass
it. Replaces the silent text_input[:300] truncation.
app.py
CHANGED
|
@@ -108,6 +108,7 @@ LANGUAGE_CONFIG = {
|
|
| 108 |
LANGUAGE_CHOICES = [('en', 'English'), ('ar', 'Arabic'), ('da', 'Danish'), ('de', 'German'), ('el', 'Greek'), ('es', 'Spanish'), ('fi', 'Finnish'), ('fr', 'French'), ('he', 'Hebrew'), ('hi', 'Hindi'), ('it', 'Italian'), ('ja', 'Japanese'), ('ko', 'Korean'), ('ms', 'Malay'), ('nl', 'Dutch'), ('no', 'Norwegian'), ('pl', 'Polish'), ('pt', 'Portuguese'), ('ru', 'Russian'), ('sv', 'Swedish'), ('sw', 'Swahili'), ('tr', 'Turkish'), ('zh', 'Chinese')]
|
| 109 |
DEFAULT_LANGUAGE = 'en'
|
| 110 |
FIXED_LANGUAGE_ID = None
|
|
|
|
| 111 |
|
| 112 |
EXAMPLES = [
|
| 113 |
['Last month, we reached a new milestone with two billion views on our YouTube channel.', 'https://storage.googleapis.com/chatterbox-demo-samples/mtl_prompts/en_f1.flac', 'en', 0.5, 0.8, 0, 0.5],
|
|
@@ -135,6 +136,20 @@ def on_language_change(language_id: str, current_ref_wav: str | None, current_te
|
|
| 135 |
return default_audio_for_ui(language_id), default_text_for_ui(language_id)
|
| 136 |
|
| 137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
def get_or_load_model():
|
| 139 |
global MODEL
|
| 140 |
if MODEL is None:
|
|
@@ -170,6 +185,8 @@ def generate_tts_audio(
|
|
| 170 |
cfgw_input: float = 0.5,
|
| 171 |
):
|
| 172 |
"""Generate speech from text with optional reference audio styling."""
|
|
|
|
|
|
|
| 173 |
current_model = get_or_load_model()
|
| 174 |
device = current_model.device
|
| 175 |
if seed_num_input != 0:
|
|
@@ -185,7 +202,7 @@ def generate_tts_audio(
|
|
| 185 |
}
|
| 186 |
if chosen_prompt:
|
| 187 |
generate_kwargs["audio_prompt_path"] = chosen_prompt
|
| 188 |
-
wav = current_model.generate(text_input
|
| 189 |
return (current_model.sr, wav.squeeze(0).cpu().numpy())
|
| 190 |
|
| 191 |
|
|
@@ -206,9 +223,10 @@ with gr.Blocks() as demo:
|
|
| 206 |
with gr.Column():
|
| 207 |
text = gr.Textbox(
|
| 208 |
value=default_text_for_ui(DEFAULT_LANGUAGE),
|
| 209 |
-
label="Text to synthesize (max
|
| 210 |
max_lines=5,
|
| 211 |
)
|
|
|
|
| 212 |
ref_wav = gr.Audio(
|
| 213 |
sources=["upload", "microphone"],
|
| 214 |
type="filepath",
|
|
@@ -237,6 +255,7 @@ with gr.Blocks() as demo:
|
|
| 237 |
|
| 238 |
inputs = [text, ref_wav, language, exaggeration, temp, seed_num, cfg_weight]
|
| 239 |
run_btn.click(fn=generate_tts_audio, inputs=inputs, outputs=[audio_output])
|
|
|
|
| 240 |
|
| 241 |
gr.Examples(
|
| 242 |
examples=EXAMPLES,
|
|
|
|
| 108 |
LANGUAGE_CHOICES = [('en', 'English'), ('ar', 'Arabic'), ('da', 'Danish'), ('de', 'German'), ('el', 'Greek'), ('es', 'Spanish'), ('fi', 'Finnish'), ('fr', 'French'), ('he', 'Hebrew'), ('hi', 'Hindi'), ('it', 'Italian'), ('ja', 'Japanese'), ('ko', 'Korean'), ('ms', 'Malay'), ('nl', 'Dutch'), ('no', 'Norwegian'), ('pl', 'Polish'), ('pt', 'Portuguese'), ('ru', 'Russian'), ('sv', 'Swedish'), ('sw', 'Swahili'), ('tr', 'Turkish'), ('zh', 'Chinese')]
|
| 109 |
DEFAULT_LANGUAGE = 'en'
|
| 110 |
FIXED_LANGUAGE_ID = None
|
| 111 |
+
MAX_CHARS = 300
|
| 112 |
|
| 113 |
EXAMPLES = [
|
| 114 |
['Last month, we reached a new milestone with two billion views on our YouTube channel.', 'https://storage.googleapis.com/chatterbox-demo-samples/mtl_prompts/en_f1.flac', 'en', 0.5, 0.8, 0, 0.5],
|
|
|
|
| 136 |
return default_audio_for_ui(language_id), default_text_for_ui(language_id)
|
| 137 |
|
| 138 |
|
| 139 |
+
def update_char_count(text: str):
|
| 140 |
+
"""Live character-count feedback for the synthesis box.
|
| 141 |
+
|
| 142 |
+
Returns two values wired to outputs=[char_count, run_btn]:
|
| 143 |
+
1. counter_md -> "n / MAX_CHARS", turned red once over the limit
|
| 144 |
+
2. button_update -> disables Generate while over the limit
|
| 145 |
+
(The server still rejects over-limit text; disabling is the visible cue.)
|
| 146 |
+
"""
|
| 147 |
+
n = len(text or "")
|
| 148 |
+
over = n > MAX_CHARS
|
| 149 |
+
counter_md = f"<span style='color:#e11'>{n} / {MAX_CHARS}</span>" if over else f"{n} / {MAX_CHARS}"
|
| 150 |
+
return counter_md, gr.update(interactive=not over)
|
| 151 |
+
|
| 152 |
+
|
| 153 |
def get_or_load_model():
|
| 154 |
global MODEL
|
| 155 |
if MODEL is None:
|
|
|
|
| 185 |
cfgw_input: float = 0.5,
|
| 186 |
):
|
| 187 |
"""Generate speech from text with optional reference audio styling."""
|
| 188 |
+
if len(text_input) > MAX_CHARS:
|
| 189 |
+
raise gr.Error(f"Text exceeds {MAX_CHARS} characters ({len(text_input)}). Please shorten it.")
|
| 190 |
current_model = get_or_load_model()
|
| 191 |
device = current_model.device
|
| 192 |
if seed_num_input != 0:
|
|
|
|
| 202 |
}
|
| 203 |
if chosen_prompt:
|
| 204 |
generate_kwargs["audio_prompt_path"] = chosen_prompt
|
| 205 |
+
wav = current_model.generate(text_input, **generate_kwargs)
|
| 206 |
return (current_model.sr, wav.squeeze(0).cpu().numpy())
|
| 207 |
|
| 208 |
|
|
|
|
| 223 |
with gr.Column():
|
| 224 |
text = gr.Textbox(
|
| 225 |
value=default_text_for_ui(DEFAULT_LANGUAGE),
|
| 226 |
+
label=f"Text to synthesize (max {MAX_CHARS} chars)",
|
| 227 |
max_lines=5,
|
| 228 |
)
|
| 229 |
+
char_count = gr.Markdown(f"{len(default_text_for_ui(DEFAULT_LANGUAGE))} / {MAX_CHARS}")
|
| 230 |
ref_wav = gr.Audio(
|
| 231 |
sources=["upload", "microphone"],
|
| 232 |
type="filepath",
|
|
|
|
| 255 |
|
| 256 |
inputs = [text, ref_wav, language, exaggeration, temp, seed_num, cfg_weight]
|
| 257 |
run_btn.click(fn=generate_tts_audio, inputs=inputs, outputs=[audio_output])
|
| 258 |
+
text.change(fn=update_char_count, inputs=[text], outputs=[char_count, run_btn])
|
| 259 |
|
| 260 |
gr.Examples(
|
| 261 |
examples=EXAMPLES,
|