Luigi Claude Fable 5 commited on
Commit ·
9fc19a9
1
Parent(s): 462d381
Add PrimeTTS v2 (MB-iSTFT-VITS 34.7M, Xinran voice, 16 kHz) as default model
Browse filesSingle-ONNX end-to-end VITS served from Luigi/PrimeTTS v2_mbistft_16k/;
v1 pipeline kept for A/B. ORT-CPU parity vs torch verified (cos 1.0000).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X5N9x7k2c4iFcDzPKGcf2x
- .gitignore +3 -0
- README.md +2 -2
- app.py +48 -14
.gitignore
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__pycache__/
|
| 2 |
+
*.pyc
|
| 3 |
+
G2PWModel/
|
README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
emoji: 🗣️
|
| 4 |
colorFrom: indigo
|
| 5 |
colorTo: pink
|
|
@@ -7,7 +7,7 @@ sdk: docker
|
|
| 7 |
app_port: 7860
|
| 8 |
pinned: false
|
| 9 |
license: apache-2.0
|
| 10 |
-
short_description: '
|
| 11 |
models:
|
| 12 |
- owensong/Inflect-Nano-v1
|
| 13 |
- Luigi/PrimeTTS
|
|
|
|
| 1 |
---
|
| 2 |
+
title: PrimeTTS v2 — on-device zh-TW / English TTS
|
| 3 |
emoji: 🗣️
|
| 4 |
colorFrom: indigo
|
| 5 |
colorTo: pink
|
|
|
|
| 7 |
app_port: 7860
|
| 8 |
pinned: false
|
| 9 |
license: apache-2.0
|
| 10 |
+
short_description: 'PrimeTTS v2 (34.7M VITS, zh-TW+EN) vs v1 & baselines'
|
| 11 |
models:
|
| 12 |
- owensong/Inflect-Nano-v1
|
| 13 |
- Luigi/PrimeTTS
|
app.py
CHANGED
|
@@ -31,7 +31,8 @@ INFLECT = "Inflect-Nano-v1 · 4.63M · FastSpeech+Snake-HiFiGAN — English, 24
|
|
| 31 |
BREEZE2 = "Breeze2-VITS"
|
| 32 |
MELO = "MeloTTS"
|
| 33 |
MATCHA = "Matcha"
|
| 34 |
-
V1B = "PrimeTTS"
|
|
|
|
| 35 |
|
| 36 |
|
| 37 |
def host_regulate(cond, dur, pitch, abs_bins, max_frames):
|
|
@@ -109,6 +110,35 @@ def _v1b_engine(): # the flagship: 16 kHz acoustic (w/ frame_pitch_refiner) +
|
|
| 109 |
return _ENG[V1B]
|
| 110 |
|
| 111 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
def _sherpa_vits(repo, model_fn):
|
| 113 |
import sherpa_onnx
|
| 114 |
from huggingface_hub import snapshot_download
|
|
@@ -144,7 +174,7 @@ def _matcha_engine():
|
|
| 144 |
return _ENG[MATCHA]
|
| 145 |
|
| 146 |
|
| 147 |
-
ENGINES = {V1B: _v1b_engine} #
|
| 148 |
SHERPA = {BREEZE2: _breeze2_engine, MELO: _melo_engine, MATCHA: _matcha_engine} # sherpa-onnx end-to-end
|
| 149 |
|
| 150 |
|
|
@@ -207,7 +237,8 @@ def generate(model: str, text: str) -> str:
|
|
| 207 |
raise gr.Error("No audio produced — try Traditional-Chinese (and/or English) input for this model.")
|
| 208 |
else:
|
| 209 |
eng = ENGINES[model]()
|
| 210 |
-
wav =
|
|
|
|
| 211 |
peak = float(np.max(np.abs(wav))) # peak-normalize -> consistent, loud output across models
|
| 212 |
if peak > 1e-6:
|
| 213 |
wav = wav * (0.97 / peak)
|
|
@@ -218,12 +249,14 @@ def generate(model: str, text: str) -> str:
|
|
| 218 |
|
| 219 |
|
| 220 |
DESCRIPTION = """
|
| 221 |
-
**PrimeTTS** is
|
| 222 |
-
|
|
|
|
| 223 |
|
| 224 |
| Model | Params | Architecture | Rate | Languages |
|
| 225 |
|:--|:--|:--|:--|:--|
|
| 226 |
-
| **PrimeTTS** · *default* | **
|
|
|
|
| 227 |
| Breeze2-VITS | ~30M | VITS (end-to-end) | 22 kHz | zh-TW |
|
| 228 |
| MeloTTS | ~32M | VITS | 8 kHz | zh + English |
|
| 229 |
| Matcha | ~26M | Flow-matching + Vocos | 8 kHz | zh-TW + English |
|
|
@@ -231,8 +264,9 @@ Pick a model, type text, hit **Generate** — and hear how a tiny on-device mode
|
|
| 231 |
|
| 232 |
ARTICLE = """
|
| 233 |
---
|
| 234 |
-
**PrimeTTS
|
| 235 |
-
|
|
|
|
| 236 |
|
| 237 |
<sub>First use of each model downloads its weights — give it a minute. ·
|
| 238 |
[Luigi/PrimeTTS](https://huggingface.co/Luigi/PrimeTTS) ·
|
|
@@ -244,7 +278,7 @@ big VITS / Matcha systems. Measured on a Jetson Nano (ORT-CPU): **CER 0.117**, *
|
|
| 244 |
demo = gr.Interface(
|
| 245 |
fn=generate,
|
| 246 |
inputs=[
|
| 247 |
-
gr.Dropdown(choices=[V1B, BREEZE2, MELO, MATCHA], value=
|
| 248 |
gr.Textbox(label="Text", value="Anderson 先生您好,您 2024年3月15日 訂的 3 件商品總共 NT$1,299,���號 AB1234CD,發票已寄到 anderson.wang@gmail.com;有問題請撥 0912-345-678 轉分機 2580,或到台北市信義區松高路11號5樓櫃台,今天氣溫 28°C,降雨機率 70%,謝謝。", lines=3),
|
| 249 |
],
|
| 250 |
outputs=gr.Audio(label="Generated audio", type="filepath"),
|
|
@@ -254,15 +288,15 @@ demo = gr.Interface(
|
|
| 254 |
theme=gr.themes.Soft(),
|
| 255 |
examples=[
|
| 256 |
# pure English
|
| 257 |
-
[
|
| 258 |
# pure zh-TW
|
| 259 |
-
[
|
| 260 |
# zh-TW + English code-mix
|
| 261 |
-
[
|
| 262 |
# complex + representative: entity-rich customer-service (date/price/serial/email/phone)
|
| 263 |
-
[
|
| 264 |
# complex + representative: code-mix with date/time/count/address
|
| 265 |
-
[
|
| 266 |
],
|
| 267 |
cache_examples=False,
|
| 268 |
flagging_mode="never",
|
|
|
|
| 31 |
BREEZE2 = "Breeze2-VITS"
|
| 32 |
MELO = "MeloTTS"
|
| 33 |
MATCHA = "Matcha"
|
| 34 |
+
V1B = "PrimeTTS v1"
|
| 35 |
+
V2 = "PrimeTTS v2"
|
| 36 |
|
| 37 |
|
| 38 |
def host_regulate(cond, dur, pitch, abs_bins, max_frames):
|
|
|
|
| 110 |
return _ENG[V1B]
|
| 111 |
|
| 112 |
|
| 113 |
+
def _v2_engine(): # PrimeTTS v2: MB-iSTFT-VITS 34.7M end-to-end (Xinran voice), 16 kHz, single ONNX
|
| 114 |
+
if V2 in _ENG: return _ENG[V2]
|
| 115 |
+
from huggingface_hub import hf_hub_download
|
| 116 |
+
import frontend_bopomofo as F
|
| 117 |
+
p = hf_hub_download(PRIMETTS_REPO, "v2_mbistft_16k/primetts_v2_xinran.onnx")
|
| 118 |
+
F.text_to_ids("您好")
|
| 119 |
+
_ENG[V2] = dict(kind="mbvits", F=F, sr=16000, sess=_ort(p))
|
| 120 |
+
return _ENG[V2]
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def _synth_v2(eng, text):
|
| 124 |
+
"""Single-pass VITS: text ids (blank-interleaved, add_blank training) -> wav. Windowed
|
| 125 |
+
rel-pos attention has no absolute-position saturation, so no chunking is needed."""
|
| 126 |
+
o = eng["F"].text_to_ids(text)
|
| 127 |
+
if not o["phone_ids"]:
|
| 128 |
+
raise gr.Error("No pronounceable content found in the text.")
|
| 129 |
+
def blank(seq):
|
| 130 |
+
out = [0] * (2 * len(seq) + 1)
|
| 131 |
+
out[1::2] = seq
|
| 132 |
+
return np.array([out], np.int64)
|
| 133 |
+
x, tn, lg = blank(o["phone_ids"]), blank(o["tone_ids"]), blank(o["lang_ids"])
|
| 134 |
+
wav = eng["sess"].run(None, {
|
| 135 |
+
"x": x, "tone": tn, "lang": lg,
|
| 136 |
+
"x_lengths": np.array([x.shape[1]], np.int64),
|
| 137 |
+
"noise_scale": np.array([0.667], np.float32),
|
| 138 |
+
"length_scale": np.array([1.0], np.float32)})[0]
|
| 139 |
+
return wav.reshape(-1)
|
| 140 |
+
|
| 141 |
+
|
| 142 |
def _sherpa_vits(repo, model_fn):
|
| 143 |
import sherpa_onnx
|
| 144 |
from huggingface_hub import snapshot_download
|
|
|
|
| 174 |
return _ENG[MATCHA]
|
| 175 |
|
| 176 |
|
| 177 |
+
ENGINES = {V2: _v2_engine, V1B: _v1b_engine} # PrimeTTS v2 (single-ONNX VITS) + v1 (enc/dec/voc pipeline)
|
| 178 |
SHERPA = {BREEZE2: _breeze2_engine, MELO: _melo_engine, MATCHA: _matcha_engine} # sherpa-onnx end-to-end
|
| 179 |
|
| 180 |
|
|
|
|
| 237 |
raise gr.Error("No audio produced — try Traditional-Chinese (and/or English) input for this model.")
|
| 238 |
else:
|
| 239 |
eng = ENGINES[model]()
|
| 240 |
+
wav = _synth_v2(eng, text) if eng["kind"] == "mbvits" else _synth_inflect(eng, text)
|
| 241 |
+
sr = eng["sr"]
|
| 242 |
peak = float(np.max(np.abs(wav))) # peak-normalize -> consistent, loud output across models
|
| 243 |
if peak > 1e-6:
|
| 244 |
wav = wav * (0.97 / peak)
|
|
|
|
| 249 |
|
| 250 |
|
| 251 |
DESCRIPTION = """
|
| 252 |
+
**PrimeTTS** is an on-device zh-TW + English text-to-speech family built for the Jetson Nano.
|
| 253 |
+
**v2** (new) is a 34.7M end-to-end MB-iSTFT-VITS targeting the Nano's **GPU**; **v1** is the 5M
|
| 254 |
+
FastSpeech pipeline for pure-CPU. Pick a model, type text, hit **Generate**.
|
| 255 |
|
| 256 |
| Model | Params | Architecture | Rate | Languages |
|
| 257 |
|:--|:--|:--|:--|:--|
|
| 258 |
+
| **PrimeTTS v2** · *default* | **34.7M** | MB-iSTFT-VITS (end-to-end) | 16 kHz | zh-TW + English |
|
| 259 |
+
| PrimeTTS v1 | ~5.0M | FastSpeech + Snake-HiFiGAN (+ pitch refiner) | 16 kHz | zh-TW + English |
|
| 260 |
| Breeze2-VITS | ~30M | VITS (end-to-end) | 22 kHz | zh-TW |
|
| 261 |
| MeloTTS | ~32M | VITS | 8 kHz | zh + English |
|
| 262 |
| Matcha | ~26M | Flow-matching + Vocos | 8 kHz | zh-TW + English |
|
|
|
|
| 264 |
|
| 265 |
ARTICLE = """
|
| 266 |
---
|
| 267 |
+
**PrimeTTS v2** is a from-scratch MB-iSTFT-VITS (conv-only, no LSTM) distilled from a VibeVoice-Large
|
| 268 |
+
teacher; on our 36-sentence zh/mix/en eval its X-ASR CER is **0.027** (below the teacher's 0.043).
|
| 269 |
+
**v1** shows how far a ~5M pure-CPU model gets (Jetson Nano ORT-CPU: CER 0.117, RTF 0.347).
|
| 270 |
|
| 271 |
<sub>First use of each model downloads its weights — give it a minute. ·
|
| 272 |
[Luigi/PrimeTTS](https://huggingface.co/Luigi/PrimeTTS) ·
|
|
|
|
| 278 |
demo = gr.Interface(
|
| 279 |
fn=generate,
|
| 280 |
inputs=[
|
| 281 |
+
gr.Dropdown(choices=[V2, V1B, BREEZE2, MELO, MATCHA], value=V2, label="Model"),
|
| 282 |
gr.Textbox(label="Text", value="Anderson 先生您好,您 2024年3月15日 訂的 3 件商品總共 NT$1,299,���號 AB1234CD,發票已寄到 anderson.wang@gmail.com;有問題請撥 0912-345-678 轉分機 2580,或到台北市信義區松高路11號5樓櫃台,今天氣溫 28°C,降雨機率 70%,謝謝。", lines=3),
|
| 283 |
],
|
| 284 |
outputs=gr.Audio(label="Generated audio", type="filepath"),
|
|
|
|
| 288 |
theme=gr.themes.Soft(),
|
| 289 |
examples=[
|
| 290 |
# pure English
|
| 291 |
+
[V2, "Thank you for calling. How may I help you today?"],
|
| 292 |
# pure zh-TW
|
| 293 |
+
[V2, "您好,這裡是台灣電力公司客服中心,很高興為您服務。"],
|
| 294 |
# zh-TW + English code-mix
|
| 295 |
+
[V2, "幫您轉接給 Jason 王經理,他的分機是 2580。"],
|
| 296 |
# complex + representative: entity-rich customer-service (date/price/serial/email/phone)
|
| 297 |
+
[V2, "Anderson 先生您好,您 2024年3月15日 訂的 3 件商品總共 NT$1,299,序號 AB1234CD,發票已寄到 anderson.wang@gmail.com,有問題請撥 0912-345-678,謝謝。"],
|
| 298 |
# complex + representative: code-mix with date/time/count/address
|
| 299 |
+
[V2, "會議在 2024年3月15日下午三點,共 5 位 guest 出席,地點在台北市信義區松高路11號5樓。"],
|
| 300 |
],
|
| 301 |
cache_examples=False,
|
| 302 |
flagging_mode="never",
|