Upload app.py
Browse files
app.py
CHANGED
|
@@ -8,6 +8,7 @@ import os
|
|
| 8 |
import json
|
| 9 |
import re
|
| 10 |
import tempfile
|
|
|
|
| 11 |
import subprocess
|
| 12 |
import shutil
|
| 13 |
import struct
|
|
@@ -32,6 +33,10 @@ except ImportError:
|
|
| 32 |
OMNI_MODEL = "qwen3.5-omni-plus"
|
| 33 |
DASHSCOPE_BASE_URL = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
LANGUAGES = ["Auto", "English", "Chinese", "Japanese", "Korean", "German",
|
| 36 |
"French", "Russian", "Portuguese", "Spanish", "Italian"]
|
| 37 |
|
|
@@ -120,7 +125,7 @@ def generate_custom_voice(text, language, speaker_label, instruction):
|
|
| 120 |
kwargs["instruct"] = instruction.strip()
|
| 121 |
print(f"[TTS] Custom: speaker={speaker}, lang={lang}")
|
| 122 |
wavs, sr = model.generate_custom_voice(**kwargs)
|
| 123 |
-
path = os.path.join(
|
| 124 |
sf.write(path, wavs[0], sr)
|
| 125 |
return path
|
| 126 |
|
|
@@ -135,7 +140,7 @@ def generate_voice_design(text, language, voice_description):
|
|
| 135 |
lang = language if language != "Auto" else "Auto"
|
| 136 |
print(f"[TTS] Design: lang={lang}, desc={voice_description[:60]}")
|
| 137 |
wavs, sr = model.generate_voice_design(text=text, language=lang, instruct=voice_description)
|
| 138 |
-
path = os.path.join(
|
| 139 |
sf.write(path, wavs[0], sr)
|
| 140 |
return path
|
| 141 |
|
|
@@ -155,7 +160,7 @@ def generate_voice_clone(text, language, ref_audio, ref_text):
|
|
| 155 |
kwargs["x_vector_only_mode"] = True
|
| 156 |
print(f"[TTS] Clone: lang={lang}")
|
| 157 |
wavs, sr = model.generate_voice_clone(**kwargs)
|
| 158 |
-
path = os.path.join(
|
| 159 |
sf.write(path, wavs[0], sr)
|
| 160 |
return path
|
| 161 |
|
|
@@ -299,7 +304,8 @@ def generate_multi_speaker_story(text_input, file_input, language, progress=gr.P
|
|
| 299 |
"Add it in Settings > Secrets."
|
| 300 |
)
|
| 301 |
|
| 302 |
-
tmp_dir =
|
|
|
|
| 303 |
|
| 304 |
# Step 1: Detect characters and emotions
|
| 305 |
progress(0.05, desc="Analyzing story characters and emotions...")
|
|
@@ -439,16 +445,7 @@ The old man was silent for a long time. "I already did," he finally whispered. "
|
|
| 439 |
|
| 440 |
DESCRIPTION = """
|
| 441 |
# Qwen3-TTS Demo
|
| 442 |
-
### Open-Source Text-to-Speech (1.7B) — Self-Hosted, No API Keys for TTS
|
| 443 |
-
|
| 444 |
-
| Mode | What it does |
|
| 445 |
-
|------|-------------|
|
| 446 |
-
| **Custom Voice** | Pick a preset voice + optional emotion instruction |
|
| 447 |
-
| **Voice Design** | Describe any voice in natural language |
|
| 448 |
-
| **Voice Clone** | Clone a voice from 3 seconds of audio |
|
| 449 |
-
| **Multi-Speaker Story** | Auto-detect characters, assign voices, add emotions — full audiobook |
|
| 450 |
|
| 451 |
-
10 languages supported. Running on ZeroGPU (free).
|
| 452 |
"""
|
| 453 |
|
| 454 |
with gr.Blocks(title="Qwen3-TTS Demo") as demo:
|
|
@@ -545,11 +542,9 @@ with gr.Blocks(title="Qwen3-TTS Demo") as demo:
|
|
| 545 |
|
| 546 |
gr.Markdown(
|
| 547 |
"---\n"
|
| 548 |
-
"**Models:** Qwen3-TTS-12Hz-1.7B (Apache 2.0) | "
|
| 549 |
"**Languages:** EN, ZH, JA, KO, DE, FR, RU, PT, ES, IT | "
|
| 550 |
-
"**TTS:** Self-hosted on ZeroGPU | "
|
| 551 |
"**Character Analysis:** Cloud AI (optional, for Multi-Speaker mode)"
|
| 552 |
)
|
| 553 |
|
| 554 |
if __name__ == "__main__":
|
| 555 |
-
demo.launch()
|
|
|
|
| 8 |
import json
|
| 9 |
import re
|
| 10 |
import tempfile
|
| 11 |
+
import time
|
| 12 |
import subprocess
|
| 13 |
import shutil
|
| 14 |
import struct
|
|
|
|
| 33 |
OMNI_MODEL = "qwen3.5-omni-plus"
|
| 34 |
DASHSCOPE_BASE_URL = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
|
| 35 |
|
| 36 |
+
# Persistent output directory (survives between requests)
|
| 37 |
+
OUTPUT_DIR = os.path.join(tempfile.gettempdir(), "qwen3_tts_outputs")
|
| 38 |
+
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
| 39 |
+
|
| 40 |
LANGUAGES = ["Auto", "English", "Chinese", "Japanese", "Korean", "German",
|
| 41 |
"French", "Russian", "Portuguese", "Spanish", "Italian"]
|
| 42 |
|
|
|
|
| 125 |
kwargs["instruct"] = instruction.strip()
|
| 126 |
print(f"[TTS] Custom: speaker={speaker}, lang={lang}")
|
| 127 |
wavs, sr = model.generate_custom_voice(**kwargs)
|
| 128 |
+
path = os.path.join(OUTPUT_DIR, f"custom_{int(time.time())}.wav")
|
| 129 |
sf.write(path, wavs[0], sr)
|
| 130 |
return path
|
| 131 |
|
|
|
|
| 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=text, language=lang, instruct=voice_description)
|
| 143 |
+
path = os.path.join(OUTPUT_DIR, f"design_{int(time.time())}.wav")
|
| 144 |
sf.write(path, wavs[0], sr)
|
| 145 |
return path
|
| 146 |
|
|
|
|
| 160 |
kwargs["x_vector_only_mode"] = True
|
| 161 |
print(f"[TTS] Clone: lang={lang}")
|
| 162 |
wavs, sr = model.generate_voice_clone(**kwargs)
|
| 163 |
+
path = os.path.join(OUTPUT_DIR, f"clone_{int(time.time())}.wav")
|
| 164 |
sf.write(path, wavs[0], sr)
|
| 165 |
return path
|
| 166 |
|
|
|
|
| 304 |
"Add it in Settings > Secrets."
|
| 305 |
)
|
| 306 |
|
| 307 |
+
tmp_dir = os.path.join(OUTPUT_DIR, f"ms_{int(time.time())}")
|
| 308 |
+
os.makedirs(tmp_dir, exist_ok=True)
|
| 309 |
|
| 310 |
# Step 1: Detect characters and emotions
|
| 311 |
progress(0.05, desc="Analyzing story characters and emotions...")
|
|
|
|
| 445 |
|
| 446 |
DESCRIPTION = """
|
| 447 |
# Qwen3-TTS Demo
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 448 |
|
|
|
|
| 449 |
"""
|
| 450 |
|
| 451 |
with gr.Blocks(title="Qwen3-TTS Demo") as demo:
|
|
|
|
| 542 |
|
| 543 |
gr.Markdown(
|
| 544 |
"---\n"
|
|
|
|
| 545 |
"**Languages:** EN, ZH, JA, KO, DE, FR, RU, PT, ES, IT | "
|
|
|
|
| 546 |
"**Character Analysis:** Cloud AI (optional, for Multi-Speaker mode)"
|
| 547 |
)
|
| 548 |
|
| 549 |
if __name__ == "__main__":
|
| 550 |
+
demo.launch(allowed_paths=[OUTPUT_DIR])
|