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 files

Single-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

Files changed (3) hide show
  1. .gitignore +3 -0
  2. README.md +2 -2
  3. 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: Inflect-Nano-v1 vs PrimeTTS-8k A/B TTS Demo
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: 'A/B: original Inflect-Nano-v1 (EN) vs PrimeTTS-8k (zh-TW+EN)'
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} # latest PrimeTTS only (encoder/decoder/vocoder pipeline)
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 = _synth_inflect(eng, text); sr = eng["sr"]
 
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 a ~4M-parameter zh-TW + English text-to-speech that runs entirely on **CPU** (built for the Jetson Nano).
222
- Pick a model, type text, hit **Generate** and hear how a tiny on-device model compares to systems 6–8× larger.
 
223
 
224
  | Model | Params | Architecture | Rate | Languages |
225
  |:--|:--|:--|:--|:--|
226
- | **PrimeTTS** · *default* | **~5.0M** | FastSpeech + Snake-HiFiGAN (+ pitch refiner) | 16 kHz | zh-TW + English |
 
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 is ~6–8× smaller** than the others the point of this A/B is how close a tiny on-device CPU model gets to the
235
- big VITS / Matcha systems. Measured on a Jetson Nano (ORT-CPU): **CER 0.117**, **RTF 0.347**.
 
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=V1B, label="Model"),
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
- [V1B, "Thank you for calling. How may I help you today?"],
258
  # pure zh-TW
259
- [V1B, "您好,這裡是台灣電力公司客服中心,很高興為您服務。"],
260
  # zh-TW + English code-mix
261
- [V1B, "幫您轉接給 Jason 王經理,他的分機是 2580。"],
262
  # complex + representative: entity-rich customer-service (date/price/serial/email/phone)
263
- [V1B, "Anderson 先生您好,您 2024年3月15日 訂的 3 件商品總共 NT$1,299,序號 AB1234CD,發票已寄到 anderson.wang@gmail.com,有問題請撥 0912-345-678,謝謝。"],
264
  # complex + representative: code-mix with date/time/count/address
265
- [V1B, "會議在 2024年3月15日下午三點,共 5 位 guest 出席,地點在台北市信義區松高路11號5樓。"],
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",