M5-TTS-Space / app.py
ASLP-lab's picture
Restore original UI with defaults only
ef5080f verified
Raw History Blame Contribute Delete
5.12 kB
import os
from pathlib import Path
os.environ.setdefault("TOKENIZERS_PARALLELISM", "false")
os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
import spaces
import gradio as gr
import torch
from huggingface_hub import snapshot_download
from m5_infer import GenerationOptions, M5TTS
from tn_frontend import get_normalizer
ROOT = Path(__file__).resolve().parent
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
DEFAULT_VOICE = ROOT / "assets" / "aslp_default_voice.wav"
DEFAULT_PROMPT = "猪携带该疾病,然后通过蚊子传染给人类。"
DEFAULT_TARGET = (
"欢迎来到ASLP实验室。我们专注于语音、语言与智能交互技术研究,"
"期待用声音连接更广阔的世界。"
)
QWEN_ROOT = Path(snapshot_download(
repo_id="Qwen/Qwen3-TTS-12Hz-1.7B-Base",
revision="fd4b254389122332181a7c3db7f27e918eec64e3",
allow_patterns=["speech_tokenizer/*"],
))
get_normalizer("zh")
get_normalizer("en")
tts = M5TTS(
ROOT / "model" / "model.safetensors",
QWEN_ROOT / "speech_tokenizer",
device=DEVICE,
runtime_root=ROOT,
expected_sha256="6c1ac8578aafb07d444e284c76d16d6529e87e1971189c76ff1795bf1252d623",
)
def estimate_duration(_audio, _prompt, target, *_args, **_kwargs):
return min(120, max(30, 20 + len(target or "") // 2))
@spaces.GPU(size="large", duration=estimate_duration)
def synthesize(
reference_audio,
prompt_text,
target_text,
language,
accent,
temperature,
top_k,
seed,
):
if not reference_audio:
raise gr.Error("Please upload or record reference audio.")
prompt_text = (prompt_text or "").strip()
target_text = (target_text or "").strip()
if not prompt_text:
raise gr.Error("Reference transcript is required.")
if not target_text:
raise gr.Error("Target text is required.")
if len(target_text) > 250:
raise gr.Error("Target text is limited to 250 characters per request.")
accent = None if accent == "None" else accent
options = GenerationOptions(
seed=int(seed),
temperature=float(temperature),
top_k=int(top_k),
text_token_limit=96,
max_audio_ms=30_000,
max_seq_len=2048,
allow_truncated=False,
)
result = tts.synthesize(
target_text,
reference_audio,
prompt_text,
language=language,
reference_language=language,
accent=accent,
speaker=None,
caption=None,
utt="space",
tn="wetext",
options=options,
)
info = result["metadata"]
return (result["sample_rate"], result["audio"]), {
"sample_rate": result["sample_rate"],
"duration_seconds": round(info["duration"], 3),
"chunks": info["chunks"],
"target_tn": info["prepared"]["target"],
"reference_tn": info["prepared"]["reference"],
"conditioning": {
"language": info["language"],
"accent": info["accent"],
"speaker": info["speaker"],
},
}
with gr.Blocks(title="M5-TTS") as demo:
gr.Markdown(
"# M5-TTS\n"
"Upload a reference recording and its exact transcript, then synthesize "
"new speech with the same voice."
)
with gr.Row():
with gr.Column():
reference_audio = gr.Audio(
sources=["upload", "microphone"], type="filepath",
value=str(DEFAULT_VOICE), label="Reference audio",
)
prompt_text = gr.Textbox(
value=DEFAULT_PROMPT, label="Reference transcript", lines=3,
info="Update this transcript when uploading another voice.",
)
target_text = gr.Textbox(
value=DEFAULT_TARGET, label="Target text", lines=5, max_length=250,
placeholder="Text to synthesize (maximum 250 characters)",
)
with gr.Column():
language = gr.Dropdown(
[
("Chinese", "zh"), ("English", "en"), ("German", "de"),
("French", "fr"), ("Japanese", "ja"), ("Korean", "ko"),
],
value="zh", label="Language",
)
accent = gr.Dropdown(
["None", "yue", "chuan"], value="None", label="Accent",
)
temperature = gr.Slider(
0.1, 1.5, value=0.9, step=0.05, label="Temperature",
)
top_k = gr.Slider(1, 50, value=10, step=1, label="Top-k")
seed = gr.Number(value=42, precision=0, label="Seed")
generate_button = gr.Button("Generate", variant="primary")
output_audio = gr.Audio(label="Generated audio")
metadata = gr.JSON(label="Generation metadata")
generate_button.click(
fn=synthesize,
inputs=[
reference_audio, prompt_text, target_text, language, accent,
temperature, top_k, seed,
],
outputs=[output_audio, metadata],
api_name="synthesize",
concurrency_limit=1,
)
demo.queue(default_concurrency_limit=1).launch()