Upload app.py
Browse files
app.py
CHANGED
|
@@ -110,17 +110,46 @@ def generate_silence(dur, path):
|
|
| 110 |
capture_output=True, check=True)
|
| 111 |
|
| 112 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
# ==========================================
|
| 114 |
# SINGLE-SPEAKER MODES
|
| 115 |
# ==========================================
|
| 116 |
@spaces.GPU
|
| 117 |
-
def generate_custom_voice(text, language, speaker_label, instruction):
|
| 118 |
-
|
| 119 |
-
raise gr.Error("Please enter some text.")
|
| 120 |
model = get_model("custom")
|
| 121 |
speaker = speaker_label.split("--")[0].strip()
|
| 122 |
lang = language if language != "Auto" else "Auto"
|
| 123 |
-
kwargs = {"text":
|
| 124 |
if instruction and instruction.strip():
|
| 125 |
kwargs["instruct"] = instruction.strip()
|
| 126 |
print(f"[TTS] Custom: speaker={speaker}, lang={lang}")
|
|
@@ -131,15 +160,14 @@ def generate_custom_voice(text, language, speaker_label, instruction):
|
|
| 131 |
|
| 132 |
|
| 133 |
@spaces.GPU
|
| 134 |
-
def generate_voice_design(text, language, voice_description):
|
| 135 |
-
|
| 136 |
-
raise gr.Error("Please enter some text.")
|
| 137 |
if not voice_description.strip():
|
| 138 |
raise gr.Error("Please describe the voice you want.")
|
| 139 |
model = get_model("design")
|
| 140 |
lang = language if language != "Auto" else "Auto"
|
| 141 |
print(f"[TTS] Design: lang={lang}, desc={voice_description[:60]}")
|
| 142 |
-
wavs, sr = model.generate_voice_design(text=
|
| 143 |
path = os.path.join(OUTPUT_DIR, f"design_{int(time.time())}.wav")
|
| 144 |
sf.write(path, wavs[0], sr)
|
| 145 |
return path
|
|
@@ -244,14 +272,13 @@ def extract_pdf_sections(filepath):
|
|
| 244 |
|
| 245 |
|
| 246 |
@spaces.GPU
|
| 247 |
-
def generate_voice_clone(text, language, ref_audio, ref_text, add_emotions):
|
| 248 |
-
|
| 249 |
-
raise gr.Error("Please enter some text.")
|
| 250 |
if ref_audio is None:
|
| 251 |
raise gr.Error("Please upload a reference audio sample.")
|
| 252 |
|
| 253 |
# Enhance text with emotions if requested
|
| 254 |
-
final_text =
|
| 255 |
if add_emotions:
|
| 256 |
client = get_llm_client()
|
| 257 |
if client:
|
|
@@ -574,7 +601,9 @@ with gr.Blocks(title="Qwen3-TTS Demo") as demo:
|
|
| 574 |
gr.Markdown("Pick a preset speaker with optional emotion/style.")
|
| 575 |
with gr.Row():
|
| 576 |
with gr.Column():
|
| 577 |
-
cv_text = gr.Textbox(label="Text", lines=4, placeholder="Enter text...")
|
|
|
|
|
|
|
| 578 |
cv_lang = gr.Dropdown(choices=LANGUAGES, value="Auto", label="Language")
|
| 579 |
cv_speaker = gr.Dropdown(choices=SPEAKER_CHOICES,
|
| 580 |
value="Ryan -- Dynamic male, strong rhythmic drive (English)",
|
|
@@ -585,14 +614,16 @@ with gr.Blocks(title="Qwen3-TTS Demo") as demo:
|
|
| 585 |
with gr.Column():
|
| 586 |
cv_audio = gr.Audio(label="Generated Speech", type="filepath")
|
| 587 |
cv_btn.click(fn=generate_custom_voice,
|
| 588 |
-
inputs=[cv_text, cv_lang, cv_speaker, cv_instruct], outputs=cv_audio)
|
| 589 |
|
| 590 |
# ββ Tab 2: Voice Design ββ
|
| 591 |
with gr.Tab("Voice Design"):
|
| 592 |
gr.Markdown("Describe the voice you want β the AI creates it.")
|
| 593 |
with gr.Row():
|
| 594 |
with gr.Column():
|
| 595 |
-
vd_text = gr.Textbox(label="Text", lines=4, placeholder="Enter text...")
|
|
|
|
|
|
|
| 596 |
vd_lang = gr.Dropdown(choices=LANGUAGES, value="English", label="Language")
|
| 597 |
vd_desc = gr.Textbox(label="Voice Description", lines=3,
|
| 598 |
placeholder="e.g. Warm male storyteller with British accent...")
|
|
@@ -610,7 +641,7 @@ with gr.Blocks(title="Qwen3-TTS Demo") as demo:
|
|
| 610 |
with gr.Column():
|
| 611 |
vd_audio = gr.Audio(label="Generated Speech", type="filepath")
|
| 612 |
vd_btn.click(fn=generate_voice_design,
|
| 613 |
-
inputs=[vd_text, vd_lang, vd_desc], outputs=vd_audio)
|
| 614 |
|
| 615 |
# ββ Tab 3: Voice Clone ββ
|
| 616 |
with gr.Tab("Voice Clone"):
|
|
@@ -620,18 +651,20 @@ with gr.Blocks(title="Qwen3-TTS Demo") as demo:
|
|
| 620 |
)
|
| 621 |
with gr.Row():
|
| 622 |
with gr.Column():
|
| 623 |
-
vc_text = gr.Textbox(label="Text (in cloned voice)", lines=4, placeholder="
|
|
|
|
|
|
|
| 624 |
vc_lang = gr.Dropdown(choices=LANGUAGES, value="English", label="Language")
|
| 625 |
vc_ref = gr.Audio(label="Reference Audio (3+ seconds)", type="filepath")
|
| 626 |
vc_ref_text = gr.Textbox(label="Transcript (optional, improves quality)",
|
| 627 |
placeholder="What the person says in the reference...")
|
| 628 |
vc_emotions = gr.Checkbox(value=False, label="Enhance with emotions",
|
| 629 |
-
info="AI adds natural emotional cues
|
| 630 |
vc_btn = gr.Button("Clone & Generate", variant="primary")
|
| 631 |
with gr.Column():
|
| 632 |
vc_audio = gr.Audio(label="Cloned Voice Speech", type="filepath")
|
| 633 |
vc_btn.click(fn=generate_voice_clone,
|
| 634 |
-
inputs=[vc_text, vc_lang, vc_ref, vc_ref_text, vc_emotions], outputs=vc_audio)
|
| 635 |
|
| 636 |
# ββ Tab 4: Multi-Speaker Story ββ
|
| 637 |
with gr.Tab("Multi-Speaker Story"):
|
|
|
|
| 110 |
capture_output=True, check=True)
|
| 111 |
|
| 112 |
|
| 113 |
+
# ==========================================
|
| 114 |
+
# TEXT RESOLVER (shared across all modes)
|
| 115 |
+
# ==========================================
|
| 116 |
+
def resolve_text(text_input, file_input):
|
| 117 |
+
"""Get text from either the textbox or an uploaded file."""
|
| 118 |
+
if file_input is not None:
|
| 119 |
+
ext = os.path.splitext(file_input)[1].lower()
|
| 120 |
+
if ext == ".pdf":
|
| 121 |
+
try:
|
| 122 |
+
import pypdf
|
| 123 |
+
reader = pypdf.PdfReader(file_input)
|
| 124 |
+
return "\n\n".join(p.extract_text().strip() for p in reader.pages if p.extract_text())
|
| 125 |
+
except Exception as e:
|
| 126 |
+
raise gr.Error(f"PDF read failed: {e}")
|
| 127 |
+
elif ext == ".docx":
|
| 128 |
+
try:
|
| 129 |
+
import docx
|
| 130 |
+
doc = docx.Document(file_input)
|
| 131 |
+
return "\n\n".join(p.text.strip() for p in doc.paragraphs if p.text.strip())
|
| 132 |
+
except Exception as e:
|
| 133 |
+
raise gr.Error(f"DOCX read failed: {e}")
|
| 134 |
+
else:
|
| 135 |
+
with open(file_input, "r", encoding="utf-8", errors="replace") as f:
|
| 136 |
+
return f.read()
|
| 137 |
+
elif text_input and text_input.strip():
|
| 138 |
+
return text_input.strip()
|
| 139 |
+
else:
|
| 140 |
+
raise gr.Error("Please enter text or upload a file.")
|
| 141 |
+
|
| 142 |
+
|
| 143 |
# ==========================================
|
| 144 |
# SINGLE-SPEAKER MODES
|
| 145 |
# ==========================================
|
| 146 |
@spaces.GPU
|
| 147 |
+
def generate_custom_voice(text, file_input, language, speaker_label, instruction):
|
| 148 |
+
resolved_text = resolve_text(text, file_input)
|
|
|
|
| 149 |
model = get_model("custom")
|
| 150 |
speaker = speaker_label.split("--")[0].strip()
|
| 151 |
lang = language if language != "Auto" else "Auto"
|
| 152 |
+
kwargs = {"text": resolved_text, "language": lang, "speaker": speaker}
|
| 153 |
if instruction and instruction.strip():
|
| 154 |
kwargs["instruct"] = instruction.strip()
|
| 155 |
print(f"[TTS] Custom: speaker={speaker}, lang={lang}")
|
|
|
|
| 160 |
|
| 161 |
|
| 162 |
@spaces.GPU
|
| 163 |
+
def generate_voice_design(text, file_input, language, voice_description):
|
| 164 |
+
resolved_text = resolve_text(text, file_input)
|
|
|
|
| 165 |
if not voice_description.strip():
|
| 166 |
raise gr.Error("Please describe the voice you want.")
|
| 167 |
model = get_model("design")
|
| 168 |
lang = language if language != "Auto" else "Auto"
|
| 169 |
print(f"[TTS] Design: lang={lang}, desc={voice_description[:60]}")
|
| 170 |
+
wavs, sr = model.generate_voice_design(text=resolved_text, language=lang, instruct=voice_description)
|
| 171 |
path = os.path.join(OUTPUT_DIR, f"design_{int(time.time())}.wav")
|
| 172 |
sf.write(path, wavs[0], sr)
|
| 173 |
return path
|
|
|
|
| 272 |
|
| 273 |
|
| 274 |
@spaces.GPU
|
| 275 |
+
def generate_voice_clone(text, file_input, language, ref_audio, ref_text, add_emotions):
|
| 276 |
+
resolved_text = resolve_text(text, file_input)
|
|
|
|
| 277 |
if ref_audio is None:
|
| 278 |
raise gr.Error("Please upload a reference audio sample.")
|
| 279 |
|
| 280 |
# Enhance text with emotions if requested
|
| 281 |
+
final_text = resolved_text
|
| 282 |
if add_emotions:
|
| 283 |
client = get_llm_client()
|
| 284 |
if client:
|
|
|
|
| 601 |
gr.Markdown("Pick a preset speaker with optional emotion/style.")
|
| 602 |
with gr.Row():
|
| 603 |
with gr.Column():
|
| 604 |
+
cv_text = gr.Textbox(label="Text", lines=4, placeholder="Enter text or upload a file below...")
|
| 605 |
+
cv_file = gr.File(label="Or Upload (.txt, .pdf, .docx)",
|
| 606 |
+
file_types=[".txt", ".md", ".pdf", ".docx"], type="filepath")
|
| 607 |
cv_lang = gr.Dropdown(choices=LANGUAGES, value="Auto", label="Language")
|
| 608 |
cv_speaker = gr.Dropdown(choices=SPEAKER_CHOICES,
|
| 609 |
value="Ryan -- Dynamic male, strong rhythmic drive (English)",
|
|
|
|
| 614 |
with gr.Column():
|
| 615 |
cv_audio = gr.Audio(label="Generated Speech", type="filepath")
|
| 616 |
cv_btn.click(fn=generate_custom_voice,
|
| 617 |
+
inputs=[cv_text, cv_file, cv_lang, cv_speaker, cv_instruct], outputs=cv_audio)
|
| 618 |
|
| 619 |
# ββ Tab 2: Voice Design ββ
|
| 620 |
with gr.Tab("Voice Design"):
|
| 621 |
gr.Markdown("Describe the voice you want β the AI creates it.")
|
| 622 |
with gr.Row():
|
| 623 |
with gr.Column():
|
| 624 |
+
vd_text = gr.Textbox(label="Text", lines=4, placeholder="Enter text or upload a file below...")
|
| 625 |
+
vd_file = gr.File(label="Or Upload (.txt, .pdf, .docx)",
|
| 626 |
+
file_types=[".txt", ".md", ".pdf", ".docx"], type="filepath")
|
| 627 |
vd_lang = gr.Dropdown(choices=LANGUAGES, value="English", label="Language")
|
| 628 |
vd_desc = gr.Textbox(label="Voice Description", lines=3,
|
| 629 |
placeholder="e.g. Warm male storyteller with British accent...")
|
|
|
|
| 641 |
with gr.Column():
|
| 642 |
vd_audio = gr.Audio(label="Generated Speech", type="filepath")
|
| 643 |
vd_btn.click(fn=generate_voice_design,
|
| 644 |
+
inputs=[vd_text, vd_file, vd_lang, vd_desc], outputs=vd_audio)
|
| 645 |
|
| 646 |
# ββ Tab 3: Voice Clone ββ
|
| 647 |
with gr.Tab("Voice Clone"):
|
|
|
|
| 651 |
)
|
| 652 |
with gr.Row():
|
| 653 |
with gr.Column():
|
| 654 |
+
vc_text = gr.Textbox(label="Text (in cloned voice)", lines=4, placeholder="Enter text or upload a file below...")
|
| 655 |
+
vc_file = gr.File(label="Or Upload (.txt, .pdf, .docx)",
|
| 656 |
+
file_types=[".txt", ".md", ".pdf", ".docx"], type="filepath")
|
| 657 |
vc_lang = gr.Dropdown(choices=LANGUAGES, value="English", label="Language")
|
| 658 |
vc_ref = gr.Audio(label="Reference Audio (3+ seconds)", type="filepath")
|
| 659 |
vc_ref_text = gr.Textbox(label="Transcript (optional, improves quality)",
|
| 660 |
placeholder="What the person says in the reference...")
|
| 661 |
vc_emotions = gr.Checkbox(value=False, label="Enhance with emotions",
|
| 662 |
+
info="AI adds natural emotional cues. Requires DASHSCOPE_API_KEY.")
|
| 663 |
vc_btn = gr.Button("Clone & Generate", variant="primary")
|
| 664 |
with gr.Column():
|
| 665 |
vc_audio = gr.Audio(label="Cloned Voice Speech", type="filepath")
|
| 666 |
vc_btn.click(fn=generate_voice_clone,
|
| 667 |
+
inputs=[vc_text, vc_file, vc_lang, vc_ref, vc_ref_text, vc_emotions], outputs=vc_audio)
|
| 668 |
|
| 669 |
# ββ Tab 4: Multi-Speaker Story ββ
|
| 670 |
with gr.Tab("Multi-Speaker Story"):
|