multimodalart HF Staff commited on
Commit
fb7fd60
·
verified ·
1 Parent(s): 3bb26a1

Upload folder using huggingface_hub

Browse files
README.md CHANGED
@@ -1,13 +1,37 @@
1
  ---
2
- title: Moss Tts Pny
3
- emoji:
4
- colorFrom: yellow
5
- colorTo: pink
6
  sdk: gradio
7
- sdk_version: 6.19.0
8
- python_version: '3.12'
9
  app_file: app.py
10
- pinned: false
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: MOSS-TTS-PNY
3
+ emoji: 🎙️
4
+ colorFrom: gray
5
+ colorTo: yellow
6
  sdk: gradio
7
+ sdk_version: 6.15.1
 
8
  app_file: app.py
9
+ short_description: Speaker-conditioned TTS with emotion & energy control
10
+ python_version: "3.12"
11
+ startup_duration_timeout: 1h
12
  ---
13
 
14
+ # MOSS-TTS-PNY
15
+
16
+ Speaker-conditioned text-to-speech demo using the [ZDisket/MOSS-TTS-PNY](https://huggingface.co/ZDisket/MOSS-TTS-PNY) model — a fine-tuned MOSS-TTS checkpoint with character voice clones from My Little Pony: Friendship is Magic and Team Fortress 2.
17
+
18
+ ## Features
19
+
20
+ - **43 speakers** — MLP:FiM characters (Twilight, Pinkie, Rainbow, etc.) and TF2 characters (Heavy, Scout, Soldier, etc.)
21
+ - **12 emotions** — Neutral, Happy, Sad, Angry, Annoyed, Fearful, Surprised, Calm, Disgusted, Whispering, Nonverbal, Shouting
22
+ - **Continuous energy control** — adjust the energy/intensity of the generated speech (0.0 – 1.0)
23
+ - **48 kHz output** via iSTFTNet2 TorchScript CUDA vocoder
24
+
25
+ ## How it works
26
+
27
+ 1. Enter the text you want to synthesize.
28
+ 2. Pick a speaker from the dropdown.
29
+ 3. Choose an emotion and set the energy level.
30
+ 4. Click **Generate** to produce audio.
31
+
32
+ ## Notes
33
+
34
+ - Uses `trust_remote_code=True` to load the custom MOSS-TTS model code.
35
+ - `transformers==4.55.0` is pinned to prevent gibberish outputs.
36
+ - The model downloads ~12 GB of weights at startup.
37
+ - Max 30 seconds of audio per generation.
app.py ADDED
@@ -0,0 +1,484 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Gradio demo for ZDisket/MOSS-TTS-PNY — a speaker-conditioned TTS model.
2
+
3
+ This Space loads the MOSS-TTS-PNY checkpoint (fine-tuned on MLP:FiM and TF2
4
+ voices) and provides a Gradio interface for text-to-speech synthesis with
5
+ speaker selection, emotion control, and energy adjustment.
6
+
7
+ Model: https://huggingface.co/ZDisket/MOSS-TTS-PNY
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import csv
13
+ import json
14
+ import math
15
+ import os
16
+ import random
17
+ import tempfile
18
+ import time
19
+ import unicodedata
20
+ import uuid
21
+ from pathlib import Path
22
+ from typing import Any
23
+
24
+ # ── ZeroGPU: import spaces before any CUDA-touching import ──────────────
25
+ import spaces # noqa: E402
26
+
27
+ import torch # noqa: E402
28
+ import gradio as gr # noqa: E402
29
+ from huggingface_hub import snapshot_download # noqa: E402
30
+
31
+ # ── Path setup ───────────────────────────────────────────────────────────
32
+ BUILD_ROOT = Path(__file__).resolve().parent
33
+ MODEL_REPO = "ZDisket/MOSS-TTS-PNY"
34
+
35
+ # Skip the os.execv re-exec inside portable_tts_runtime
36
+ os.environ.setdefault("MOSS_TTS_NVIDIA_LD_LIBRARY_PATH_READY", "1")
37
+ os.environ.setdefault("HF_HUB_ENABLE_HF_TRANSFER", "1")
38
+
39
+ # ── Download model weights at startup ─────────────────────────────────────
40
+ print("Downloading model weights from ZDisket/MOSS-TTS-PNY …", flush=True)
41
+ _download_start = time.perf_counter()
42
+ _model_root = snapshot_download(
43
+ repo_id=MODEL_REPO,
44
+ repo_type="model",
45
+ allow_patterns=[
46
+ "moss_tts_local_clipper_checkpoint/**",
47
+ "moss_audio_tokenizer/**",
48
+ "istftnet2_decoder4_50hz/**",
49
+ ],
50
+ )
51
+ _download_elapsed = time.perf_counter() - _download_start
52
+ print(f"Model weights downloaded in {_download_elapsed:.1f}s -> {_model_root}", flush=True)
53
+
54
+ CHECKPOINT_PATH = str(Path(_model_root) / "moss_tts_local_clipper_checkpoint")
55
+ CODEC_PATH = str(Path(_model_root) / "moss_audio_tokenizer")
56
+ DECODER_DIR = str(Path(_model_root) / "istftnet2_decoder4_50hz")
57
+
58
+ # ── Constants ─────────────────────────────────────────────────────────────
59
+ CODEC_FRAMES_PER_SECOND = 12.5
60
+ MAX_OUTPUT_SECONDS = 30
61
+ MAX_OUTPUT_FRAMES = int(CODEC_FRAMES_PER_SECOND * MAX_OUTPUT_SECONDS)
62
+ MAX_TEXT_LINES = 12
63
+ MAX_LANGUAGE_CHARS = 16
64
+ MAX_TEXT_CHARS = 500
65
+
66
+ EMOTION_CHOICES = [
67
+ ("Neutral", 0),
68
+ ("Happy", 1),
69
+ ("Sad", 2),
70
+ ("Angry", 3),
71
+ ("Annoyed", 4),
72
+ ("Fearful", 5),
73
+ ("Surprised", 6),
74
+ ("Calm", 7),
75
+ ("Disgusted", 8),
76
+ ("Whispering", 9),
77
+ ("Nonverbal", 10),
78
+ ("Shouting", 11),
79
+ ]
80
+
81
+ RANDOM_TEXTS = [
82
+ "I know this sounds sudden, but I really missed hearing your voice today.",
83
+ "Wait, you actually finished the whole thing already?",
84
+ "I'm trying to stay calm, but that was way closer than I expected.",
85
+ "Honestly, I'm proud of us for getting through that without giving up.",
86
+ "No, no, it's fine. I'm only a little dramatically offended.",
87
+ "I cannot believe you kept that surprise hidden for so long.",
88
+ "Please tell me you saw that too, because I am not ready to explain it.",
89
+ "That was kind of beautiful, in a messy and completely unexpected way.",
90
+ "I'm sorry, I should have listened before jumping to conclusions.",
91
+ "Okay, deep breath. We can fix this if we take it one step at a time.",
92
+ "You have no idea how happy that made me.",
93
+ "I was nervous at first, but now I'm actually excited to try again.",
94
+ ]
95
+
96
+ # ── Load speaker map & tiers ──────────────────────────────────────────────
97
+
98
+
99
+ def load_speaker_tiers(csv_path: str) -> dict[int, int]:
100
+ path = Path(csv_path)
101
+ if not path.exists():
102
+ return {}
103
+ tiers: dict[int, int] = {}
104
+ with path.open("r", encoding="utf-8", newline="") as handle:
105
+ for row in csv.DictReader(handle):
106
+ tiers[int(row["speaker_id"])] = int(row["tier"])
107
+ return tiers
108
+
109
+
110
+ def load_speaker_choices(csv_path: str, tiers: dict[int, int]) -> tuple[list[str], dict[str, int]]:
111
+ path = Path(csv_path)
112
+ if not path.exists():
113
+ choices = [f"Speaker {i} ({i})" for i in range(43)]
114
+ return choices, {c: i for i, c in enumerate(choices)}
115
+ choice_rows: list[tuple[int, str, str, int]] = []
116
+ with path.open("r", encoding="utf-8", newline="") as handle:
117
+ for row in csv.DictReader(handle):
118
+ speaker_id = int(row["speaker_id"])
119
+ label = row.get("label") or f"{row.get('display_name') or row.get('name')} ({speaker_id})"
120
+ tier = tiers.get(speaker_id, 9)
121
+ if tier != 9:
122
+ label = f"T{tier} - {label}"
123
+ choice_rows.append((tier, label.lower(), label, speaker_id))
124
+ choices: list[str] = []
125
+ speaker_ids: dict[str, int] = {}
126
+ for _tier, _sort_label, label, speaker_id in sorted(choice_rows):
127
+ choices.append(label)
128
+ speaker_ids[label] = speaker_id
129
+ return choices, speaker_ids
130
+
131
+
132
+ SPEAKER_TIERS = load_speaker_tiers(str(BUILD_ROOT / "speaker_hours_tiers.csv"))
133
+ SPEAKER_CHOICES, SPEAKER_IDS = load_speaker_choices(
134
+ str(BUILD_ROOT / "speaker_id_map.csv"), SPEAKER_TIERS
135
+ )
136
+ DEFAULT_SPEAKER = next(
137
+ (c for c in SPEAKER_CHOICES if "Twilight (31)" in c),
138
+ SPEAKER_CHOICES[0] if SPEAKER_CHOICES else "Speaker 31 (31)",
139
+ )
140
+
141
+ # ── Load model at module scope (ZeroGPU rule 2) ───────────────────────────
142
+ from portable_tts_runtime import ( # noqa: E402
143
+ OptimizedTTSConfig,
144
+ OptimizedTTSRunner,
145
+ save_wav_pcm16,
146
+ )
147
+
148
+ # Use fixed-full torch opt mode (avoids the ~4-minute Triton compile of the
149
+ # packed-local path; still gets torch.compile on the global transformer).
150
+ RUNNER = OptimizedTTSRunner(
151
+ OptimizedTTSConfig(
152
+ checkpoint=CHECKPOINT_PATH,
153
+ codec_path=CODEC_PATH,
154
+ decoder_dir=DECODER_DIR,
155
+ decoder_runtime="torchscript_cuda",
156
+ decoder4_features_runtime="torch_fp16",
157
+ dtype="fp16",
158
+ attn_implementation="sdpa",
159
+ torch_opt_mode="fixed-full",
160
+ cache_implementation="static",
161
+ compile_global_transformer=True,
162
+ global_compile_mode="default",
163
+ fast_prepare_inputs=True,
164
+ triton_top_p=True,
165
+ packed_local_qkv=False,
166
+ packed_local_mlp=False,
167
+ static_packed_weights=False,
168
+ tensorize_rmsnorm_eps=True,
169
+ feedback_lookup=True,
170
+ vocoder_bucket_frames=64,
171
+ vocoder_prewarm_buckets="64,128,192,256,320,384,448,512,576,640",
172
+ )
173
+ )
174
+ SAMPLE_RATE = int(RUNNER.sample_rate)
175
+ print(f"Model loaded. sample_rate={SAMPLE_RATE}", flush=True)
176
+
177
+ # Prewarm the model with a short dummy generation so the first user request
178
+ # does not pay the torch.compile cost.
179
+ print("Prewarming model …", flush=True)
180
+ _prewarm_start = time.perf_counter()
181
+ try:
182
+ _pw = RUNNER.synthesize(
183
+ text="The optimized demo is ready.",
184
+ language="en",
185
+ speaker_id=31,
186
+ max_new_tokens=64,
187
+ text_temperature=0.0,
188
+ text_top_p=1.0,
189
+ text_top_k=None,
190
+ audio_temperature=0.8,
191
+ audio_top_p=0.92,
192
+ audio_top_k=None,
193
+ audio_repetition_penalty=1.05,
194
+ n_vq_for_inference=32,
195
+ style_text="The optimized demo is ready.",
196
+ style_emotion_id=0,
197
+ style_energy=0.5,
198
+ )
199
+ print(f"Prewarm complete in {time.perf_counter() - _prewarm_start:.1f}s", flush=True)
200
+ except Exception as exc:
201
+ print(f"Prewarm failed ({exc!r}) — continuing anyway", flush=True)
202
+
203
+
204
+ # ── Helpers ────────────────────────────────────────────────────────────────
205
+
206
+
207
+ def energy_label(energy: float) -> str:
208
+ if energy < 0.33:
209
+ return "low energy"
210
+ if energy < 0.66:
211
+ return "medium energy"
212
+ return "high energy"
213
+
214
+
215
+ def normalize_client_text(value: str, *, max_chars: int) -> str:
216
+ if not isinstance(value, str):
217
+ raise gr.Error("Text must be a string.")
218
+ text = unicodedata.normalize("NFKC", value).replace("\r\n", "\n").replace("\r", "\n").strip()
219
+ if not text:
220
+ raise gr.Error("Enter some text first.")
221
+ if max_chars > 0 and len(text) > max_chars:
222
+ raise gr.Error(f"Text is too long. Keep it under {max_chars} characters.")
223
+ if text.count("\n") + 1 > MAX_TEXT_LINES:
224
+ raise gr.Error(f"Text has too many lines. Keep it under {MAX_TEXT_LINES} lines.")
225
+ for char in text:
226
+ cat = unicodedata.category(char)
227
+ if cat.startswith("C") and char not in {"\n", "\t"}:
228
+ raise gr.Error("Text contains unsupported control characters.")
229
+ if not any(c.isalnum() for c in text):
230
+ raise gr.Error("Text must contain at least one letter or number.")
231
+ if "\ufffd" in text:
232
+ raise gr.Error("Text contains malformed replacement characters.")
233
+ return text
234
+
235
+
236
+ def normalize_language(value: str) -> str:
237
+ language = unicodedata.normalize("NFKC", value or "en").strip().lower()
238
+ if not language:
239
+ return "en"
240
+ if len(language) > MAX_LANGUAGE_CHARS:
241
+ raise gr.Error("Language code is too long.")
242
+ allowed = set("abcdefghijklmnopqrstuvwxyz-")
243
+ if any(c not in allowed for c in language):
244
+ raise gr.Error("Language must be a simple code like 'en'.")
245
+ return language
246
+
247
+
248
+ def validate_float(name: str, value: float, minimum: float, maximum: float) -> float:
249
+ value = float(value)
250
+ if not math.isfinite(value) or value < minimum or value > maximum:
251
+ raise gr.Error(f"{name} must be between {minimum} and {maximum}.")
252
+ return value
253
+
254
+
255
+ def validate_int(name: str, value: int, minimum: int, maximum: int) -> int:
256
+ value = int(value)
257
+ if value < minimum or value > maximum:
258
+ raise gr.Error(f"{name} must be between {minimum} and {maximum}.")
259
+ return value
260
+
261
+
262
+ def _random_text() -> str:
263
+ return random.choice(RANDOM_TEXTS)
264
+
265
+
266
+ # ── Inference (decorated for ZeroGPU) ─────────────────────────────────────
267
+
268
+
269
+ @spaces.GPU(duration=120)
270
+ def synthesize(
271
+ text: str,
272
+ style_text: str,
273
+ language: str,
274
+ speaker_label: str,
275
+ emotion_label: str,
276
+ style_energy: float,
277
+ audio_temperature: float,
278
+ audio_top_p: float,
279
+ audio_top_k: int,
280
+ audio_repetition_penalty: float,
281
+ ) -> tuple[str, str]:
282
+ """Generate speech audio from text using MOSS-TTS-PNY.
283
+
284
+ Args:
285
+ text: The text to synthesize.
286
+ style_text: Optional style reference text (falls back to ``text``).
287
+ language: Language code (e.g. ``"en"``).
288
+ speaker_label: Speaker name from the dropdown.
289
+ emotion_label: Emotion name from the dropdown.
290
+ style_energy: Energy level (0.0 – 1.0).
291
+ audio_temperature: Sampling temperature for audio tokens.
292
+ audio_top_p: Top-p value for audio token sampling.
293
+ audio_top_k: Top-k value (0 = disabled).
294
+ audio_repetition_penalty: Repetition penalty for audio tokens.
295
+
296
+ Returns:
297
+ A tuple of (wav_filepath, stats_json).
298
+ """
299
+ clean_text = normalize_client_text(text, max_chars=MAX_TEXT_CHARS)
300
+ clean_style_text = (
301
+ normalize_client_text(style_text, max_chars=MAX_TEXT_CHARS)
302
+ if style_text and style_text.strip()
303
+ else ""
304
+ )
305
+ clean_language = normalize_language(language)
306
+ if speaker_label not in SPEAKER_IDS:
307
+ raise gr.Error("Choose a valid speaker.")
308
+ emotion_lookup = {label: eid for label, eid in EMOTION_CHOICES}
309
+ if emotion_label not in emotion_lookup:
310
+ raise gr.Error("Choose a valid emotion.")
311
+ clean_energy = validate_float("Energy", style_energy, 0.0, 1.0)
312
+ clean_temperature = validate_float("Audio temperature", audio_temperature, 0.0, 1.5)
313
+ clean_top_p = validate_float("Audio top-p", audio_top_p, 0.05, 1.0)
314
+ clean_top_k = validate_int("Audio top-k", audio_top_k, 0, 200)
315
+ clean_rep_pen = validate_float(
316
+ "Audio repetition penalty", audio_repetition_penalty, 0.0, 2.0
317
+ )
318
+
319
+ start = time.perf_counter()
320
+ result = RUNNER.synthesize(
321
+ text=clean_text,
322
+ language=clean_language,
323
+ speaker_id=SPEAKER_IDS[speaker_label],
324
+ max_new_tokens=MAX_OUTPUT_FRAMES,
325
+ text_temperature=0.0,
326
+ text_top_p=1.0,
327
+ text_top_k=None,
328
+ audio_temperature=clean_temperature,
329
+ audio_top_p=clean_top_p,
330
+ audio_top_k=clean_top_k if clean_top_k > 0 else None,
331
+ audio_repetition_penalty=clean_rep_pen,
332
+ n_vq_for_inference=32,
333
+ style_text=clean_style_text or clean_text,
334
+ style_emotion_id=emotion_lookup[emotion_label],
335
+ style_energy=clean_energy,
336
+ )
337
+ elapsed = time.perf_counter() - start
338
+
339
+ # Write to a temp file (concurrency-safe — no fixed output paths).
340
+ tmp_wav = tempfile.NamedTemporaryFile(
341
+ suffix=".wav", delete=False, dir=str(BUILD_ROOT / "outputs")
342
+ )
343
+ tmp_wav.close()
344
+ output_path = Path(tmp_wav.name)
345
+ save_wav_pcm16(output_path, result["audio"], SAMPLE_RATE)
346
+
347
+ stats = {
348
+ "speaker": speaker_label,
349
+ "speaker_id": SPEAKER_IDS[speaker_label],
350
+ "emotion": emotion_label,
351
+ "style_energy": clean_energy,
352
+ "energy_label": energy_label(clean_energy),
353
+ "language": clean_language,
354
+ "text_chars": len(clean_text),
355
+ "audio_sec": round(result["audio_sec"], 3),
356
+ "prompt_sec": round(result["prompt_sec"], 3),
357
+ "generate_sec": round(result["generate_sec"], 3),
358
+ "decode_sec": round(result["decode_sec"], 3),
359
+ "total_sec": round(elapsed, 3),
360
+ "generate_x_realtime": round(result["generate_x_realtime"], 2),
361
+ "generated_tokens": result.get("generated_tokens", 0),
362
+ "prompt_tokens": result.get("prompt_tokens", 0),
363
+ "sample_rate": SAMPLE_RATE,
364
+ }
365
+ return str(output_path), json.dumps(stats, indent=2)
366
+
367
+
368
+ # ── UI ────────────────────────────────────────────────────────────────────
369
+
370
+ CSS = """
371
+ #col-container { max-width: 1100px; margin: 0 auto; }
372
+ .dark .gradio-container { color: var(--body-text-color); }
373
+ """
374
+
375
+ with gr.Blocks(
376
+ title="MOSS-TTS-PNY",
377
+ theme=gr.themes.Citrus(),
378
+ css=CSS,
379
+ ) as demo:
380
+ gr.Markdown(
381
+ "# 🎙️ MOSS-TTS-PNY\n"
382
+ "Speaker-conditioned text-to-speech with emotion and energy control. "
383
+ "Fine-tuned MOSS-TTS checkpoint featuring character voices.\n\n"
384
+ "Model: [ZDisket/MOSS-TTS-PNY](https://huggingface.co/ZDisket/MOSS-TTS-PNY)"
385
+ )
386
+
387
+ with gr.Row():
388
+ with gr.Column(scale=3):
389
+ text = gr.Textbox(
390
+ label="Text",
391
+ value="I wasn't expecting that to work, but now I'm kind of excited.",
392
+ lines=4,
393
+ max_length=MAX_TEXT_CHARS,
394
+ )
395
+ style_text = gr.Textbox(
396
+ label="Style text (optional)",
397
+ placeholder="Leave empty to use the same text as input.",
398
+ lines=2,
399
+ max_length=MAX_TEXT_CHARS,
400
+ )
401
+ with gr.Row():
402
+ randomize = gr.Button("Random Text")
403
+ generate = gr.Button("Generate", variant="primary")
404
+
405
+ with gr.Column(scale=2):
406
+ language = gr.Textbox(label="Language", value="en")
407
+ speaker = gr.Dropdown(
408
+ label="Speaker",
409
+ choices=SPEAKER_CHOICES,
410
+ value=DEFAULT_SPEAKER,
411
+ )
412
+ emotion = gr.Dropdown(
413
+ label="Emotion",
414
+ choices=[label for label, _ in EMOTION_CHOICES],
415
+ value="Neutral",
416
+ )
417
+ style_energy = gr.Slider(
418
+ label="Energy",
419
+ minimum=0.0,
420
+ maximum=1.0,
421
+ value=0.5,
422
+ step=0.05,
423
+ )
424
+ gr.Markdown(f"Max {MAX_OUTPUT_SECONDS}s of audio output")
425
+
426
+ with gr.Accordion("Advanced settings", open=False):
427
+ gr.Markdown("If you don't know what you're doing, leave these untouched.")
428
+ with gr.Row():
429
+ audio_temperature = gr.Slider(
430
+ label="Audio temperature", minimum=0.0, maximum=1.5, value=0.8, step=0.05
431
+ )
432
+ audio_top_p = gr.Slider(
433
+ label="Audio top-p", minimum=0.05, maximum=1.0, value=0.92, step=0.05
434
+ )
435
+ audio_top_k = gr.Slider(
436
+ label="Audio top-k", minimum=0, maximum=200, value=0, step=1
437
+ )
438
+ audio_repetition_penalty = gr.Slider(
439
+ label="Audio repetition penalty",
440
+ minimum=0.0,
441
+ maximum=2.0,
442
+ value=1.05,
443
+ step=0.05,
444
+ )
445
+
446
+ with gr.Row():
447
+ audio_output = gr.Audio(label="Generated audio", type="filepath", autoplay=True)
448
+ stats = gr.Code(label="Stats", language="json")
449
+
450
+ gr.Examples(
451
+ examples=[
452
+ ["I wasn't expecting that to work, but now I'm kind of excited.", "", "en",
453
+ DEFAULT_SPEAKER, "Neutral", 0.5, 0.8, 0.92, 0, 1.05],
454
+ ["You have no idea how happy that made me.", "", "en",
455
+ DEFAULT_SPEAKER, "Happy", 0.8, 0.8, 0.92, 0, 1.05],
456
+ ["I'm trying to stay calm, but that was way closer than I expected.", "", "en",
457
+ DEFAULT_SPEAKER, "Fearful", 0.3, 0.8, 0.92, 0, 1.05],
458
+ ["Okay, deep breath. We can fix this if we take it one step at a time.", "", "en",
459
+ DEFAULT_SPEAKER, "Calm", 0.4, 0.8, 0.92, 0, 1.05],
460
+ ],
461
+ inputs=[
462
+ text, style_text, language, speaker, emotion, style_energy,
463
+ audio_temperature, audio_top_p, audio_top_k, audio_repetition_penalty,
464
+ ],
465
+ outputs=[audio_output, stats],
466
+ fn=synthesize,
467
+ cache_examples=True,
468
+ cache_mode="lazy",
469
+ )
470
+
471
+ randomize.click(_random_text, inputs=[], outputs=[text], queue=False)
472
+ generate.click(
473
+ synthesize,
474
+ inputs=[
475
+ text, style_text, language, speaker, emotion, style_energy,
476
+ audio_temperature, audio_top_p, audio_top_k, audio_repetition_penalty,
477
+ ],
478
+ outputs=[audio_output, stats],
479
+ api_name="generate",
480
+ )
481
+
482
+ if __name__ == "__main__":
483
+ demo.queue(default_concurrency_limit=1, max_size=20)
484
+ demo.launch(mcp_server=True, show_error=False)
decoder4_features_torch.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import torch
4
+ import torch.nn as nn
5
+
6
+
7
+ class Decoder4FeatureExtractor(nn.Module):
8
+ """MOSS audio-tokenizer codebook decode plus decoder blocks 0..4."""
9
+
10
+ def __init__(
11
+ self,
12
+ audio_tokenizer: nn.Module,
13
+ num_quantizers: int = 32,
14
+ output_dtype: torch.dtype = torch.float16,
15
+ ) -> None:
16
+ super().__init__()
17
+ quantizer = getattr(audio_tokenizer, "quantizer")
18
+ self.quantizers = quantizer.quantizers
19
+ self.output_proj = quantizer.output_proj
20
+ self.decoder_prefix = nn.ModuleList(list(audio_tokenizer.decoder[:5]))
21
+ self.rvq_dim = int(quantizer.rvq_dim)
22
+ self.num_quantizers = int(num_quantizers)
23
+ self.output_dtype = output_dtype
24
+
25
+ def forward(self, codes: torch.Tensor, lengths: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
26
+ _, batch, frames = codes.shape
27
+ emb = torch.zeros(batch, self.rvq_dim, frames, device=codes.device, dtype=self.output_dtype)
28
+ for index, quantizer in enumerate(self.quantizers[: self.num_quantizers]):
29
+ if self.output_dtype == torch.float16:
30
+ z_q = quantizer.embed_code(codes[index]).transpose(1, 2).to(self.output_dtype)
31
+ z_q = quantizer.out_proj(z_q)
32
+ else:
33
+ z_q = quantizer.decode_code(codes[index])
34
+ emb = emb + z_q.to(self.output_dtype)
35
+ features = self.output_proj(emb)
36
+ feature_lengths = lengths
37
+ for module in self.decoder_prefix:
38
+ features, feature_lengths = module(features, feature_lengths)
39
+ return features.to(self.output_dtype), feature_lengths
portable_tts_runtime.py ADDED
@@ -0,0 +1,800 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ from __future__ import annotations
3
+
4
+ import gc
5
+ import importlib.util
6
+ import json
7
+ import os
8
+ import site
9
+ import sys
10
+ import time
11
+ import wave
12
+ from dataclasses import dataclass
13
+ from pathlib import Path
14
+ from types import SimpleNamespace
15
+ from typing import Any
16
+
17
+ import numpy as np
18
+ import torch
19
+ from transformers import AutoConfig, AutoModel, AutoTokenizer, TorchAoConfig
20
+
21
+ from decoder4_features_torch import Decoder4FeatureExtractor
22
+
23
+
24
+ BUNDLE_ROOT = Path(__file__).resolve().parent
25
+
26
+
27
+ def _candidate_roots() -> list[Path]:
28
+ roots = [
29
+ BUNDLE_ROOT,
30
+ BUNDLE_ROOT.parent,
31
+ Path.cwd(),
32
+ Path.cwd() / "moss_tts_clipper_istftnet2_release",
33
+ BUNDLE_ROOT.parent / "moss_tts_clipper_istftnet2_release",
34
+ ]
35
+ seen: set[Path] = set()
36
+ unique: list[Path] = []
37
+ for root in roots:
38
+ resolved = root.resolve()
39
+ if resolved not in seen:
40
+ seen.add(resolved)
41
+ unique.append(resolved)
42
+ return unique
43
+
44
+
45
+ def resolve_asset(value: str | Path) -> Path:
46
+ path = Path(value)
47
+ if path.exists():
48
+ return path.resolve()
49
+ for root in _candidate_roots():
50
+ candidate = root / path
51
+ if candidate.exists():
52
+ return candidate.resolve()
53
+ return path
54
+
55
+
56
+ def add_release_root_to_syspath(checkpoint: str | Path) -> Path:
57
+ checkpoint_path = resolve_asset(checkpoint)
58
+ release_root = checkpoint_path.parent
59
+ release_root_str = str(release_root)
60
+ if release_root_str not in sys.path:
61
+ sys.path.insert(0, release_root_str)
62
+ return checkpoint_path
63
+
64
+
65
+ def ensure_nvidia_library_path() -> None:
66
+ if os.environ.get("MOSS_TTS_NVIDIA_LD_LIBRARY_PATH_READY") == "1":
67
+ return
68
+ lib_dirs: list[str] = []
69
+ site_roots = list(dict.fromkeys(site.getsitepackages() + [site.getusersitepackages()]))
70
+ for site_root in site_roots:
71
+ nvidia_root = Path(site_root) / "nvidia"
72
+ if not nvidia_root.exists():
73
+ continue
74
+ for lib_dir in nvidia_root.glob("*/lib"):
75
+ if lib_dir.is_dir():
76
+ lib_dirs.append(str(lib_dir))
77
+ if not lib_dirs:
78
+ return
79
+ current = os.environ.get("LD_LIBRARY_PATH", "")
80
+ current_parts = [part for part in current.split(":") if part]
81
+ wanted = [part for part in lib_dirs if part not in current_parts]
82
+ if not wanted:
83
+ os.environ["MOSS_TTS_NVIDIA_LD_LIBRARY_PATH_READY"] = "1"
84
+ return
85
+ os.environ["LD_LIBRARY_PATH"] = ":".join(wanted + current_parts)
86
+ os.environ["MOSS_TTS_NVIDIA_LD_LIBRARY_PATH_READY"] = "1"
87
+ os.execv(sys.executable, [sys.executable, *sys.argv])
88
+
89
+
90
+ def choose_providers(requested: str, available: list[str]) -> list[str]:
91
+ return [requested] if requested in available else ["CPUExecutionProvider"]
92
+
93
+
94
+ def select_dtype(device: torch.device, dtype_arg: str) -> torch.dtype:
95
+ if device.type != "cuda":
96
+ return torch.float32
97
+ if dtype_arg == "fp16":
98
+ return torch.float16
99
+ if dtype_arg == "bf16":
100
+ return torch.bfloat16
101
+ if dtype_arg == "fp32":
102
+ return torch.float32
103
+ major, _minor = torch.cuda.get_device_capability(device)
104
+ return torch.bfloat16 if major >= 8 else torch.float16
105
+
106
+
107
+ def save_wav_pcm16(path: Path, audio: torch.Tensor, sample_rate: int) -> None:
108
+ pcm = (audio.clamp(-1.0, 1.0) * 32767.0).to(torch.int16).cpu().numpy()
109
+ path.parent.mkdir(parents=True, exist_ok=True)
110
+ with wave.open(str(path), "wb") as handle:
111
+ handle.setnchannels(1)
112
+ handle.setsampwidth(2)
113
+ handle.setframerate(sample_rate)
114
+ handle.writeframes(pcm.tobytes())
115
+
116
+
117
+ def summarize(values: list[float]) -> dict[str, float]:
118
+ if not values:
119
+ return {"mean": 0.0, "min": 0.0, "max": 0.0}
120
+ return {
121
+ "mean": sum(values) / len(values),
122
+ "min": min(values),
123
+ "max": max(values),
124
+ }
125
+
126
+
127
+ def build_processor_without_audio_model(checkpoint: str | Path):
128
+ bundled_release_root = BUNDLE_ROOT.parent
129
+ if (bundled_release_root / "moss_tts_local_clipper_checkpoint").exists():
130
+ bundled_release_root_str = str(bundled_release_root)
131
+ if bundled_release_root_str not in sys.path:
132
+ sys.path.insert(0, bundled_release_root_str)
133
+ try:
134
+ from moss_tts_local_clipper_checkpoint.processing_moss_tts import MossTTSDelayProcessor
135
+ except ImportError:
136
+ processing_file = Path(checkpoint) / "processing_moss_tts.py"
137
+ if not processing_file.exists():
138
+ raise
139
+ spec = importlib.util.spec_from_file_location("moss_tts_checkpoint_processing", processing_file)
140
+ if spec is None or spec.loader is None:
141
+ raise ImportError(f"Could not load processor module from {processing_file}.")
142
+ module = importlib.util.module_from_spec(spec)
143
+ sys.modules[spec.name] = module
144
+ spec.loader.exec_module(module)
145
+ MossTTSDelayProcessor = module.MossTTSDelayProcessor
146
+
147
+ config = AutoConfig.from_pretrained(checkpoint, trust_remote_code=True)
148
+ tokenizer = AutoTokenizer.from_pretrained(checkpoint, trust_remote_code=True)
149
+ return MossTTSDelayProcessor(tokenizer=tokenizer, model_config=config)
150
+
151
+
152
+ def load_style_extractor_class(checkpoint: str | Path):
153
+ try:
154
+ from moss_tts_local.style_features import BertStyleFeatureExtractor
155
+
156
+ return BertStyleFeatureExtractor
157
+ except ImportError:
158
+ pass
159
+
160
+ style_file = Path(checkpoint) / "style_features.py"
161
+ if style_file.exists():
162
+ spec = importlib.util.spec_from_file_location("moss_tts_checkpoint_style_features", style_file)
163
+ if spec is None or spec.loader is None:
164
+ raise ImportError(f"Could not load style feature module from {style_file}.")
165
+ module = importlib.util.module_from_spec(spec)
166
+ sys.modules[spec.name] = module
167
+ spec.loader.exec_module(module)
168
+ return module.BertStyleFeatureExtractor
169
+
170
+ from moss_tts_local_clipper_checkpoint.style_features import BertStyleFeatureExtractor
171
+
172
+ return BertStyleFeatureExtractor
173
+
174
+
175
+ class TorchDecoder4FeatureRuntime:
176
+ def __init__(self, codec_path: str | Path, device: torch.device, dtype: torch.dtype) -> None:
177
+ if device.type != "cuda" or dtype != torch.float16:
178
+ raise RuntimeError("torch_fp16 decoder4 runtime requires CUDA fp16.")
179
+ audio_tokenizer = AutoModel.from_pretrained(codec_path, trust_remote_code=True).to(
180
+ device=device,
181
+ dtype=dtype,
182
+ )
183
+ audio_tokenizer.eval()
184
+ num_quantizers = int(audio_tokenizer.config.quantizer_kwargs.get("num_quantizers", 32))
185
+ self.extractor = Decoder4FeatureExtractor(
186
+ audio_tokenizer,
187
+ num_quantizers=num_quantizers,
188
+ output_dtype=dtype,
189
+ ).to(device=device, dtype=dtype)
190
+ self.extractor.eval()
191
+ self.device = device
192
+ self.inputs = [SimpleNamespace(name="codes"), SimpleNamespace(name="lengths")]
193
+ del audio_tokenizer
194
+ gc.collect()
195
+ torch.cuda.empty_cache()
196
+
197
+ def get_inputs(self) -> list[Any]:
198
+ return self.inputs
199
+
200
+ def get_providers(self) -> list[str]:
201
+ return ["torch_fp16_cuda"]
202
+
203
+ def run(self, output_names: Any, feeds: dict[str, np.ndarray]) -> list[np.ndarray]:
204
+ del output_names
205
+ codes = torch.from_numpy(feeds["codes"]).to(device=self.device, dtype=torch.long)
206
+ lengths = torch.from_numpy(feeds["lengths"]).to(device=self.device, dtype=torch.long)
207
+ with torch.inference_mode():
208
+ features, feature_lengths = self.extractor(codes, lengths)
209
+ if self.device.type == "cuda":
210
+ torch.cuda.synchronize()
211
+ return [
212
+ features.detach().float().cpu().numpy(),
213
+ feature_lengths.detach().cpu().numpy(),
214
+ ]
215
+
216
+
217
+ def run_vocoder_onnx(session: Any, features: np.ndarray, feature_lengths: np.ndarray) -> torch.Tensor:
218
+ input_name = session.get_inputs()[0].name
219
+ audio_np = session.run(None, {input_name: features.astype(np.float32, copy=False)})[0]
220
+ samples = int(feature_lengths[0]) * 960
221
+ audio = torch.from_numpy(audio_np[0, 0, :samples]).float().cpu()
222
+ return torch.nan_to_num(audio).reshape(-1).clamp(-1.0, 1.0)
223
+
224
+
225
+ class TorchScriptVocoderRuntime:
226
+ def __init__(
227
+ self,
228
+ decoder_dir: str | Path,
229
+ device: torch.device,
230
+ *,
231
+ use_cudagraph: bool = False,
232
+ bucket_frames: int = 0,
233
+ ) -> None:
234
+ artifact = Path(decoder_dir) / (
235
+ "istftnet2_decoder_cuda.ts" if device.type == "cuda" else "istftnet2_decoder_cpu.ts"
236
+ )
237
+ if not artifact.exists():
238
+ raise FileNotFoundError(f"Missing TorchScript vocoder artifact: {artifact}")
239
+ self.device = device
240
+ self.module = torch.jit.load(str(artifact), map_location=device).eval()
241
+ self.use_cudagraph = bool(use_cudagraph and device.type == "cuda")
242
+ self.bucket_frames = max(0, int(bucket_frames))
243
+ self.graphs: dict[tuple[Any, ...], tuple[torch.cuda.CUDAGraph, torch.Tensor, torch.Tensor]] = {}
244
+
245
+ def get_inputs(self) -> list[Any]:
246
+ return [SimpleNamespace(name="features")]
247
+
248
+ def get_providers(self) -> list[str]:
249
+ suffix = "_cudagraph" if self.use_cudagraph else ""
250
+ return [f"torchscript_{self.device.type}{suffix}"]
251
+
252
+ def prewarm_buckets(self, frame_lengths: list[int]) -> dict[str, Any]:
253
+ requested = [int(length) for length in frame_lengths if int(length) > 0]
254
+ if not requested:
255
+ return {"requested": [], "elapsed_sec": 0.0}
256
+ if self.device.type == "cuda":
257
+ torch.cuda.synchronize(self.device)
258
+ start = time.perf_counter()
259
+ warmed: list[int] = []
260
+ with torch.inference_mode():
261
+ for frames in requested:
262
+ features = torch.randn(1, 768, frames, device=self.device, dtype=torch.float32)
263
+ self.run_tensor(features)
264
+ warmed.append(frames)
265
+ if self.device.type == "cuda":
266
+ torch.cuda.synchronize(self.device)
267
+ return {"requested": requested, "warmed": warmed, "elapsed_sec": time.perf_counter() - start}
268
+
269
+ def run_tensor(self, features_tensor: torch.Tensor) -> torch.Tensor:
270
+ features_tensor = features_tensor.to(device=self.device, dtype=torch.float32).contiguous()
271
+ if self.bucket_frames > 0 and features_tensor.ndim == 3:
272
+ frames = int(features_tensor.shape[-1])
273
+ bucketed = ((frames + self.bucket_frames - 1) // self.bucket_frames) * self.bucket_frames
274
+ if bucketed > frames:
275
+ padded = torch.zeros(
276
+ *features_tensor.shape[:-1],
277
+ bucketed,
278
+ device=features_tensor.device,
279
+ dtype=features_tensor.dtype,
280
+ )
281
+ padded[..., :frames] = features_tensor
282
+ features_tensor = padded
283
+ if not self.use_cudagraph:
284
+ with torch.inference_mode():
285
+ return self.module(features_tensor)
286
+ key = (
287
+ features_tensor.device.index,
288
+ features_tensor.dtype,
289
+ tuple(features_tensor.shape),
290
+ )
291
+ entry = self.graphs.get(key)
292
+ if entry is None:
293
+ try:
294
+ static_features = torch.empty_like(features_tensor)
295
+ static_features.copy_(features_tensor)
296
+ warmup_stream = torch.cuda.Stream(device=features_tensor.device)
297
+ warmup_stream.wait_stream(torch.cuda.current_stream(features_tensor.device))
298
+ with torch.cuda.stream(warmup_stream), torch.inference_mode():
299
+ for _ in range(3):
300
+ static_audio = self.module(static_features)
301
+ torch.cuda.current_stream(features_tensor.device).wait_stream(warmup_stream)
302
+
303
+ graph = torch.cuda.CUDAGraph()
304
+ with torch.cuda.graph(graph), torch.inference_mode():
305
+ static_audio = self.module(static_features)
306
+ entry = (graph, static_features, static_audio)
307
+ self.graphs[key] = entry
308
+ except Exception:
309
+ self.use_cudagraph = False
310
+ self.graphs.clear()
311
+ try:
312
+ torch.cuda.synchronize(features_tensor.device)
313
+ except Exception:
314
+ pass
315
+ with torch.inference_mode():
316
+ return self.module(features_tensor)
317
+
318
+ graph, static_features, static_audio = entry
319
+ static_features.copy_(features_tensor)
320
+ graph.replay()
321
+ return static_audio
322
+
323
+
324
+ def run_vocoder(
325
+ session: Any,
326
+ features: np.ndarray | torch.Tensor,
327
+ feature_lengths: np.ndarray | torch.Tensor,
328
+ ) -> torch.Tensor:
329
+ if isinstance(session, TorchScriptVocoderRuntime):
330
+ if isinstance(features, np.ndarray):
331
+ features_tensor = torch.from_numpy(features).to(device=session.device, dtype=torch.float32)
332
+ else:
333
+ features_tensor = features.to(device=session.device, dtype=torch.float32)
334
+ with torch.inference_mode():
335
+ audio_tensor = session.run_tensor(features_tensor)
336
+ if session.device.type == "cuda":
337
+ torch.cuda.synchronize()
338
+ if isinstance(feature_lengths, torch.Tensor):
339
+ samples = int(feature_lengths[0].item()) * 960
340
+ else:
341
+ samples = int(feature_lengths[0]) * 960
342
+ audio = audio_tensor[0, 0, :samples].detach().float().cpu()
343
+ return torch.nan_to_num(audio).reshape(-1).clamp(-1.0, 1.0)
344
+
345
+ if isinstance(features, torch.Tensor):
346
+ features = features.detach().cpu().numpy()
347
+ if isinstance(feature_lengths, torch.Tensor):
348
+ feature_lengths = feature_lengths.detach().cpu().numpy()
349
+ return run_vocoder_onnx(session, features, feature_lengths)
350
+
351
+
352
+ def parse_int_csv(value: str) -> list[int]:
353
+ values: list[int] = []
354
+ for part in value.split(","):
355
+ part = part.strip()
356
+ if not part:
357
+ continue
358
+ values.append(int(part))
359
+ return values
360
+
361
+
362
+ def enable_static_cache_for_global_model(model: Any) -> None:
363
+ type(model)._can_compile_fullgraph = True
364
+ language_config = model.config.language_config
365
+ for field in (
366
+ "max_position_embeddings",
367
+ "hidden_size",
368
+ "num_attention_heads",
369
+ "head_dim",
370
+ "num_key_value_heads",
371
+ "sliding_window",
372
+ "layer_types",
373
+ "num_hidden_layers",
374
+ ):
375
+ if hasattr(language_config, field):
376
+ setattr(model.config, field, getattr(language_config, field))
377
+
378
+
379
+ def configure_torch_compile() -> None:
380
+ import importlib
381
+ import torch._inductor.config as inductor_config
382
+
383
+ dynamo_config = importlib.import_module("torch._dynamo.config")
384
+ dynamo_config.cache_size_limit = 256
385
+ dynamo_config.capture_scalar_outputs = True
386
+ inductor_config.triton.cudagraphs = False
387
+ inductor_config.triton.cudagraph_trees = False
388
+ inductor_config.triton.cudagraph_skip_dynamic_graphs = True
389
+
390
+
391
+ @dataclass
392
+ class OptimizedTTSConfig:
393
+ checkpoint: str | Path = "moss_tts_local_clipper_checkpoint"
394
+ codec_path: str | Path = "moss_audio_tokenizer"
395
+ decoder_dir: str | Path = "istftnet2_decoder4_50hz"
396
+ decoder4_features_onnx: str | Path = "ort_sessions/decoder4_features_fp32/model.onnx"
397
+ decoder_runtime: str = "torchscript_cuda"
398
+ vocoder_cudagraph: bool = False
399
+ vocoder_bucket_frames: int = 0
400
+ vocoder_prewarm_buckets: str = ""
401
+ decoder4_features_runtime: str = "torch_fp16"
402
+ decoder4_provider: str = "CUDAExecutionProvider"
403
+ dtype: str = "fp16"
404
+ tts_quantization: str = "none"
405
+ torch_opt_mode: str = "static-local-cache-triton-compile-cudagraph"
406
+ compile_mode: str = "max-autotune-no-cudagraphs"
407
+ cache_implementation: str = "static"
408
+ compile_global_transformer: bool = True
409
+ global_compile_mode: str = "default"
410
+ attn_implementation: str = "sdpa"
411
+ fast_prepare_inputs: bool = True
412
+ tensorrt_local: bool = False
413
+ triton_top_p: bool = False
414
+ triton_fused_lm_head: bool = False
415
+ triton_qkv_cache: bool = False
416
+ packed_local_qkv: bool = False
417
+ packed_local_mlp: bool = False
418
+ packed_adapter_mlp: bool = False
419
+ packed_adapter_mlp_scope: str = "heads"
420
+ static_packed_weights: bool = False
421
+ triton_rmsnorm: bool = False
422
+ tensorize_rmsnorm_eps: bool = True
423
+ fast_control_head: bool = False
424
+ feedback_lookup: bool = True
425
+ local_compile_fullgraph: bool = False
426
+ style_bert_model: str = "cirimus/modernbert-base-go-emotions"
427
+ style_bert_layer: int = 19
428
+ style_bert_max_length: int = 512
429
+
430
+
431
+ class OptimizedTTSRunner:
432
+ def __init__(self, config: OptimizedTTSConfig) -> None:
433
+ ensure_nvidia_library_path()
434
+ torch.backends.cudnn.benchmark = False
435
+ try:
436
+ torch.backends.cuda.enable_cudnn_sdp(False)
437
+ torch.backends.cuda.enable_flash_sdp(True)
438
+ torch.backends.cuda.enable_mem_efficient_sdp(True)
439
+ torch.backends.cuda.enable_math_sdp(True)
440
+ except Exception:
441
+ pass
442
+
443
+ checkpoint = add_release_root_to_syspath(config.checkpoint)
444
+ codec_path = resolve_asset(config.codec_path)
445
+ decoder_dir = resolve_asset(config.decoder_dir)
446
+ decoder4_features_onnx = resolve_asset(config.decoder4_features_onnx)
447
+
448
+ from torch_hf_optimizations import install_torch_frame_sampler, tensorize_rmsnorm_eps
449
+
450
+ import onnxruntime as ort
451
+
452
+ self.config = config
453
+ self.checkpoint = checkpoint
454
+ self.codec_path = codec_path
455
+ self.decoder_dir = decoder_dir
456
+ self.decoder4_features_onnx = decoder4_features_onnx
457
+ self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
458
+ self.dtype = select_dtype(self.device, config.dtype)
459
+
460
+ wants_cuda = (
461
+ config.decoder4_provider == "CUDAExecutionProvider"
462
+ or config.decoder_runtime == "onnx_cuda"
463
+ or config.decoder4_features_runtime == "torch_fp16"
464
+ )
465
+ if wants_cuda and hasattr(ort, "preload_dlls"):
466
+ ort.preload_dlls(cuda=True, cudnn=True, directory="")
467
+ available_providers = ort.get_available_providers()
468
+ session_options = ort.SessionOptions()
469
+ session_options.log_severity_level = 2
470
+ self.available_ort_providers = available_providers
471
+
472
+ self.processor = build_processor_without_audio_model(checkpoint)
473
+ self.processor.model_config.sampling_rate = 48000
474
+ if config.decoder4_features_runtime == "onnx":
475
+ self.decoder4_session = ort.InferenceSession(
476
+ str(decoder4_features_onnx),
477
+ sess_options=session_options,
478
+ providers=choose_providers(config.decoder4_provider, available_providers),
479
+ )
480
+ elif config.decoder4_features_runtime == "torch_fp16":
481
+ self.decoder4_session = TorchDecoder4FeatureRuntime(codec_path, device=self.device, dtype=torch.float16)
482
+ else:
483
+ raise ValueError(f"Unsupported decoder4_features_runtime: {config.decoder4_features_runtime}")
484
+
485
+ if config.decoder_runtime.startswith("torchscript_"):
486
+ vocoder_device = torch.device(
487
+ "cuda" if config.decoder_runtime == "torchscript_cuda" and torch.cuda.is_available() else "cpu"
488
+ )
489
+ self.vocoder_session = TorchScriptVocoderRuntime(
490
+ decoder_dir,
491
+ vocoder_device,
492
+ use_cudagraph=config.vocoder_cudagraph,
493
+ bucket_frames=config.vocoder_bucket_frames,
494
+ )
495
+ else:
496
+ vocoder_provider = "CUDAExecutionProvider" if config.decoder_runtime == "onnx_cuda" else "CPUExecutionProvider"
497
+ self.vocoder_session = ort.InferenceSession(
498
+ str(decoder_dir / "istftnet2_decoder.onnx"),
499
+ sess_options=session_options,
500
+ providers=choose_providers(vocoder_provider, available_providers),
501
+ )
502
+ self.vocoder_prewarm_result: dict[str, Any] | None = None
503
+ if isinstance(self.vocoder_session, TorchScriptVocoderRuntime):
504
+ prewarm_buckets = parse_int_csv(config.vocoder_prewarm_buckets)
505
+ if prewarm_buckets:
506
+ self.vocoder_prewarm_result = self.vocoder_session.prewarm_buckets(prewarm_buckets)
507
+ tts_load_kwargs: dict[str, Any] = {
508
+ "trust_remote_code": True,
509
+ "torch_dtype": self.dtype,
510
+ "attn_implementation": config.attn_implementation,
511
+ }
512
+ if config.tts_quantization != "none":
513
+ if self.device.type != "cuda":
514
+ raise RuntimeError("TorchAO TTS quantization requires CUDA in this runner.")
515
+ modules_to_not_convert = None
516
+ if config.tts_quantization in {
517
+ "torchao_fp8_weight_only_global",
518
+ "torchao_fp8_dynamic_activation_fp8_weight_global",
519
+ }:
520
+ modules_to_not_convert = [
521
+ "local_transformer",
522
+ "speech_embedding_to_local_mlp",
523
+ "local_to_speech_embedding_mlps",
524
+ "layer_norm_before_lm_heads",
525
+ "lm_heads",
526
+ ]
527
+ if config.tts_quantization == "torchao_fp8_weight_only_global":
528
+ quant_kind = "torchao_fp8_weight_only"
529
+ else:
530
+ quant_kind = "torchao_fp8_dynamic_activation_fp8_weight"
531
+ else:
532
+ quant_kind = config.tts_quantization
533
+ if quant_kind == "torchao_fp8_weight_only":
534
+ from torchao.quantization import Float8WeightOnlyConfig
535
+
536
+ tts_load_kwargs["quantization_config"] = TorchAoConfig(
537
+ Float8WeightOnlyConfig(),
538
+ modules_to_not_convert=modules_to_not_convert,
539
+ )
540
+ elif quant_kind == "torchao_fp8_dynamic_activation_fp8_weight":
541
+ from torchao.quantization import Float8DynamicActivationFloat8WeightConfig
542
+
543
+ tts_load_kwargs["quantization_config"] = TorchAoConfig(
544
+ Float8DynamicActivationFloat8WeightConfig(),
545
+ modules_to_not_convert=modules_to_not_convert,
546
+ )
547
+ else:
548
+ raise ValueError(f"Unsupported tts_quantization: {config.tts_quantization}")
549
+ tts_load_kwargs["device_map"] = {"": self.device.index or 0}
550
+ self.model = AutoModel.from_pretrained(checkpoint, **tts_load_kwargs)
551
+ if config.tts_quantization == "none":
552
+ self.model = self.model.to(self.device)
553
+ self.model.eval()
554
+ self.style_feature_dim = int(getattr(self.model.config, "style_feature_dim", 0) or 0)
555
+ self.num_emotions = int(getattr(self.model.config, "num_emotions", 0) or 0)
556
+ self.style_extractor = None
557
+ if self.style_feature_dim > 0 and self.style_feature_dim != 2:
558
+ BertStyleFeatureExtractor = load_style_extractor_class(checkpoint)
559
+ self.style_extractor = BertStyleFeatureExtractor(
560
+ repo_id=config.style_bert_model,
561
+ layer_index=config.style_bert_layer,
562
+ max_length=config.style_bert_max_length,
563
+ target_dim=self.style_feature_dim,
564
+ device=self.device,
565
+ dtype=self.dtype,
566
+ attn_implementation="sdpa" if self.device.type == "cuda" else "eager",
567
+ )
568
+ self.tensorized_rmsnorm_eps = 0
569
+ if config.tensorize_rmsnorm_eps:
570
+ self.tensorized_rmsnorm_eps = tensorize_rmsnorm_eps(self.model, device=self.device)
571
+ if config.compile_global_transformer:
572
+ configure_torch_compile()
573
+ if config.compile_global_transformer or config.cache_implementation == "static":
574
+ enable_static_cache_for_global_model(self.model)
575
+ if config.compile_global_transformer:
576
+ if config.global_compile_mode == "default":
577
+ global_compile_mode = None
578
+ global_compile_options = None
579
+ self.global_compile_mode_effective = "default"
580
+ else:
581
+ global_compile_mode = None
582
+ global_compile_options = {
583
+ "triton.cudagraphs": False,
584
+ "triton.cudagraph_trees": False,
585
+ "triton.cudagraph_skip_dynamic_graphs": True,
586
+ }
587
+ self.global_compile_mode_effective = "default-no-inductor-cudagraphs"
588
+ self.model.model.language_model = torch.compile(
589
+ self.model.model.language_model,
590
+ mode=global_compile_mode,
591
+ options=global_compile_options,
592
+ fullgraph=False,
593
+ dynamic=True,
594
+ )
595
+ else:
596
+ self.global_compile_mode_effective = "disabled"
597
+ install_torch_frame_sampler(
598
+ self.model,
599
+ mode=config.torch_opt_mode,
600
+ compile_mode=config.compile_mode,
601
+ packed_local_qkv=config.packed_local_qkv,
602
+ packed_local_mlp=config.packed_local_mlp,
603
+ packed_adapter_mlp=config.packed_adapter_mlp,
604
+ packed_adapter_mlp_scope=config.packed_adapter_mlp_scope,
605
+ static_packed_weights=config.static_packed_weights,
606
+ triton_rmsnorm=config.triton_rmsnorm,
607
+ tensorrt_local=config.tensorrt_local,
608
+ triton_top_p=config.triton_top_p,
609
+ triton_fused_lm_head=config.triton_fused_lm_head,
610
+ triton_qkv_cache=config.triton_qkv_cache,
611
+ fast_prepare_inputs=config.fast_prepare_inputs,
612
+ fast_control_head=config.fast_control_head,
613
+ feedback_lookup=config.feedback_lookup,
614
+ local_compile_fullgraph=config.local_compile_fullgraph,
615
+ )
616
+ self.sample_rate = int(self.processor.model_config.sampling_rate)
617
+
618
+ def decode_outputs(self, outputs: list[tuple[int, torch.Tensor]]) -> tuple[torch.Tensor, float]:
619
+ if torch.cuda.is_available():
620
+ torch.cuda.synchronize()
621
+ start = time.perf_counter()
622
+ decoded_segments: list[torch.Tensor] = []
623
+ codes_input_name = self.decoder4_session.get_inputs()[0].name
624
+ lengths_input_name = self.decoder4_session.get_inputs()[1].name
625
+ for start_length, generation_ids in outputs:
626
+ frame_tokens = generation_ids.detach().cpu()[:, 0]
627
+ audio_codes = generation_ids.detach().cpu()[:, 1:]
628
+ is_pad = (audio_codes == int(self.processor.model_config.audio_pad_code)).all(dim=1)
629
+ is_eos = frame_tokens == int(self.processor.model_config.audio_end_token_id)
630
+ non_pad = ~is_pad & ~is_eos
631
+ if not non_pad.any():
632
+ continue
633
+ idx = torch.nonzero(non_pad).squeeze(1)
634
+ breaks = torch.where(idx[1:] != idx[:-1] + 1)[0] + 1
635
+ segments_idx = [idx] if breaks.numel() == 0 else list(torch.split(idx, breaks.tolist()))
636
+ for segment_index, segment_idx in enumerate(segments_idx):
637
+ segment_codes = audio_codes[segment_idx].contiguous()
638
+ if int(segment_codes.shape[0]) <= 0:
639
+ continue
640
+ codes_np = segment_codes.T.unsqueeze(1).numpy().astype(np.int64, copy=False)
641
+ lengths_np = np.asarray([int(segment_codes.shape[0])], dtype=np.int64)
642
+ feeds = {codes_input_name: codes_np, lengths_input_name: lengths_np}
643
+ if hasattr(self.decoder4_session, "run_tensors"):
644
+ features, feature_lengths = self.decoder4_session.run_tensors(feeds)
645
+ else:
646
+ features, feature_lengths = self.decoder4_session.run(None, feeds)
647
+ segment_audio = run_vocoder(self.vocoder_session, features, feature_lengths)
648
+ if segment_index == 0 and int(start_length) > 0:
649
+ trim_ratio = max(0.0, min(float(start_length) / float(segment_codes.shape[0]), 1.0))
650
+ if trim_ratio >= 1.0:
651
+ continue
652
+ if trim_ratio > 0.0:
653
+ segment_audio = segment_audio[..., int(segment_audio.shape[-1] * trim_ratio) :]
654
+ decoded_segments.append(segment_audio)
655
+ if torch.cuda.is_available():
656
+ torch.cuda.synchronize()
657
+ elapsed = time.perf_counter() - start
658
+ if not decoded_segments:
659
+ raise RuntimeError("Generation did not produce decodable audio.")
660
+ return torch.cat(decoded_segments, dim=-1).float().cpu(), elapsed
661
+
662
+ def synthesize(
663
+ self,
664
+ *,
665
+ text: str,
666
+ language: str = "en",
667
+ speaker_id: int = 31,
668
+ max_new_tokens: int = 160,
669
+ text_temperature: float = 0.0,
670
+ text_top_p: float = 1.0,
671
+ text_top_k: int | None = None,
672
+ audio_temperature: float = 0.8,
673
+ audio_top_p: float = 0.92,
674
+ audio_top_k: int | None = None,
675
+ audio_repetition_penalty: float = 1.0,
676
+ n_vq_for_inference: int = 32,
677
+ style_text: str | None = None,
678
+ style_features: torch.Tensor | None = None,
679
+ style_emotion_id: int | None = None,
680
+ style_energy: float = 0.7,
681
+ ) -> dict[str, Any]:
682
+ conversations = [[self.processor.build_user_message(text=text, language=language)]]
683
+ prompt_start = time.perf_counter()
684
+ batch = self.processor(conversations, mode="generation")
685
+ input_ids = batch["input_ids"].to(self.device)
686
+ attention_mask = batch["attention_mask"].to(self.device)
687
+ prompt_sec = time.perf_counter() - prompt_start
688
+
689
+ if self.device.type == "cuda":
690
+ torch.cuda.synchronize()
691
+ generate_start = time.perf_counter()
692
+ generation_kwargs = {}
693
+ if self.config.cache_implementation != "none":
694
+ generation_kwargs["cache_implementation"] = self.config.cache_implementation
695
+ speaker_tensor = torch.tensor([speaker_id], device=self.device, dtype=torch.long)
696
+ generation_kwargs["speaker_ids"] = speaker_tensor
697
+ if self.style_feature_dim > 0:
698
+ if style_features is None:
699
+ if self.style_feature_dim == 2 and style_emotion_id is not None:
700
+ max_emotion = max(0, (self.num_emotions or 1) - 1)
701
+ style_features = torch.tensor(
702
+ [
703
+ [
704
+ float(max(0, min(max_emotion, int(style_emotion_id)))),
705
+ float(max(0.0, min(1.0, style_energy))),
706
+ ]
707
+ ],
708
+ dtype=torch.float32,
709
+ )
710
+ elif self.style_extractor is None:
711
+ raise RuntimeError("Checkpoint expects style features, but no style extractor is available.")
712
+ else:
713
+ style_features = self.style_extractor.encode((style_text or text).strip())
714
+ generation_kwargs["style_features"] = style_features.to(device=self.device)
715
+ with torch.inference_mode():
716
+ outputs = self.model.generate(
717
+ input_ids=input_ids,
718
+ attention_mask=attention_mask,
719
+ max_new_tokens=max_new_tokens,
720
+ n_vq_for_inference=n_vq_for_inference,
721
+ text_temperature=text_temperature,
722
+ text_top_p=text_top_p,
723
+ text_top_k=text_top_k,
724
+ audio_temperature=audio_temperature,
725
+ audio_top_p=audio_top_p,
726
+ audio_top_k=None,
727
+ audio_repetition_penalty=audio_repetition_penalty,
728
+ **generation_kwargs,
729
+ )
730
+ if self.device.type == "cuda":
731
+ torch.cuda.synchronize()
732
+ generate_sec = time.perf_counter() - generate_start
733
+
734
+ audio, decode_sec = self.decode_outputs(outputs)
735
+ audio_sec = float(audio.numel() / self.sample_rate)
736
+ return {
737
+ "audio": audio,
738
+ "audio_sec": audio_sec,
739
+ "prompt_sec": prompt_sec,
740
+ "generate_sec": generate_sec,
741
+ "decode_sec": decode_sec,
742
+ "generate_x_realtime": audio_sec / generate_sec if generate_sec else 0.0,
743
+ "total_x_realtime": audio_sec / (generate_sec + decode_sec) if generate_sec + decode_sec else 0.0,
744
+ "generated_tokens": int(outputs[0][1].shape[0]) if outputs else 0,
745
+ "prompt_tokens": int(input_ids.shape[1]),
746
+ }
747
+
748
+ def runtime_summary(self) -> dict[str, Any]:
749
+ return {
750
+ "device": str(self.device),
751
+ "dtype": str(self.dtype),
752
+ "tts_quantization": self.config.tts_quantization,
753
+ "processor_loads_audio_tokenizer": False,
754
+ "audio_decode_runtime": f"{self.config.decoder4_features_runtime}_decoder4_features_plus_{self.config.decoder_runtime}_vocoder",
755
+ "checkpoint": str(self.checkpoint),
756
+ "codec_path": str(self.codec_path),
757
+ "decoder_dir": str(self.decoder_dir),
758
+ "decoder4_features_onnx": str(self.decoder4_features_onnx),
759
+ "vocoder_onnx": str(self.decoder_dir / "istftnet2_decoder.onnx"),
760
+ "decoder4_providers": self.decoder4_session.get_providers(),
761
+ "vocoder_providers": self.vocoder_session.get_providers(),
762
+ "available_ort_providers": self.available_ort_providers,
763
+ "torch_opt_mode": self.config.torch_opt_mode,
764
+ "compile_mode": self.config.compile_mode,
765
+ "cache_implementation": self.config.cache_implementation,
766
+ "compile_global_transformer": bool(self.config.compile_global_transformer),
767
+ "global_compile_mode": self.global_compile_mode_effective,
768
+ "fast_prepare_inputs": bool(self.config.fast_prepare_inputs),
769
+ "tensorrt_local": bool(self.config.tensorrt_local),
770
+ "triton_top_p": bool(self.config.triton_top_p),
771
+ "triton_fused_lm_head": bool(self.config.triton_fused_lm_head),
772
+ "triton_qkv_cache": bool(self.config.triton_qkv_cache),
773
+ "packed_local_qkv": bool(self.config.packed_local_qkv),
774
+ "packed_local_mlp": bool(self.config.packed_local_mlp),
775
+ "packed_adapter_mlp": bool(self.config.packed_adapter_mlp),
776
+ "packed_adapter_mlp_scope": self.config.packed_adapter_mlp_scope,
777
+ "static_packed_weights": bool(self.config.static_packed_weights),
778
+ "triton_rmsnorm": bool(self.config.triton_rmsnorm),
779
+ "tensorize_rmsnorm_eps": bool(self.config.tensorize_rmsnorm_eps),
780
+ "tensorized_rmsnorm_eps": int(self.tensorized_rmsnorm_eps),
781
+ "fast_control_head": bool(self.config.fast_control_head),
782
+ "feedback_lookup": bool(self.config.feedback_lookup),
783
+ "local_compile_fullgraph": bool(self.config.local_compile_fullgraph),
784
+ "style_feature_dim": int(self.style_feature_dim),
785
+ "num_emotions": int(self.num_emotions),
786
+ "vocoder_cudagraph": bool(self.config.vocoder_cudagraph),
787
+ "vocoder_bucket_frames": int(self.config.vocoder_bucket_frames),
788
+ "vocoder_prewarm_buckets": parse_int_csv(self.config.vocoder_prewarm_buckets),
789
+ "vocoder_prewarm_result": self.vocoder_prewarm_result,
790
+ }
791
+
792
+
793
+ def write_summary(path: Path, summary: dict[str, Any]) -> None:
794
+ path.parent.mkdir(parents=True, exist_ok=True)
795
+ serializable = {
796
+ key: value
797
+ for key, value in summary.items()
798
+ if not isinstance(value, torch.Tensor)
799
+ }
800
+ path.write_text(json.dumps(serializable, indent=2))
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ transformers==4.55.0
2
+ onnxruntime-gpu==1.26.0
3
+ safetensors
4
+ numpy
speaker_hours_tiers.csv ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ tier,speaker_id,speaker,utterances,codec_hours,flac_hours,avg_sec,min_sec,max_sec,missing
2
+ 1,0,Apple Bloom,1296,0.8802888888888889,0.8941135879629628,2.445246913580247,0.32,8.64,0
3
+ 1,1,Applejack,3143,2.1064666666666665,2.1398366145833307,2.412752147629653,0.24,8.4,0
4
+ 3,2,Autumn Blaze,161,0.11495555555555555,0.11679866898148142,2.5704347826086953,0.16,7.2,0
5
+ 3,3,Big Mac,222,0.11515555555555555,0.11741705439814816,1.8673873873873874,0.32,7.12,0
6
+ 3,4,Cadance,165,0.12504444444444446,0.12680443865740743,2.7282424242424246,0.64,9.6,0
7
+ 2,5,Celestia,583,0.4552222222222222,0.46121357638888894,2.8109777015437394,0.48,10.56,0
8
+ 3,6,Cheerilee,111,0.07991111111111111,0.08106452546296296,2.591711711711712,0.72,6.56,0
9
+ 2,7,Chrysalis,178,0.1591111111111111,0.16089883101851857,3.2179775280898872,0.48,8.24,0
10
+ 3,8,Cozy Glow,157,0.11946666666666667,0.1210344386574074,2.739363057324841,0.72,7.2,0
11
+ 3,9,Diamond Tiara,102,0.08711111111111111,0.08823683449074078,3.074509803921569,0.8,8.4,0
12
+ 2,10,Discord,476,0.38424444444444444,0.38949334490740734,2.906050420168067,0.56,9.12,0
13
+ 1,11,Fluttershy,1792,1.2326666666666668,1.252261811342593,2.476339285714286,0.24,9.04,0
14
+ 2,12,Goldie Delicious,177,0.15811111111111112,0.16000179398148143,3.215819209039548,0.96,7.44,0
15
+ 2,13,Granny Smith,286,0.23762222222222223,0.24077664351851852,2.9910489510489513,0.64,9.36,0
16
+ 3,14,Luna,191,0.1398888888888889,0.14183635416666662,2.636649214659686,0.56,7.28,0
17
+ 3,15,Maud,177,0.09362222222222223,0.0954588541666667,1.904180790960452,0.48,8.32,0
18
+ 3,16,Mrs. Cake,135,0.11806666666666667,0.11953124421296295,3.1484444444444444,0.72,7.04,0
19
+ 1,17,Pinkie,2080,1.2596,1.2820753935185158,2.1800769230769235,0.24,8.8,0
20
+ 1,18,Rainbow,2333,1.513088888888889,1.5377426851851859,2.3348135447921132,0.32,9.2,0
21
+ 1,19,Rarity,2383,1.6880444444444445,1.71413467013889,2.550130088124213,0.16,12.16,0
22
+ 1,20,Scootaloo,774,0.5134888888888889,0.5219100347222223,2.3883204134366927,0.4,8.32,0
23
+ 3,21,Shining Armor,118,0.09484444444444444,0.09610426504629628,2.8935593220338984,0.4,11.28,0
24
+ 1,22,Spike,1791,1.0443111111111112,1.0638599907486153,2.099117811278615,0.32,9.6,0
25
+ 3,23,Spitfire,134,0.08724444444444444,0.08878343749999998,2.3438805970149255,0.4,6.16,0
26
+ 1,24,Starlight,1125,0.8553777777777778,0.8673828676933746,2.737208888888889,0.24,8.56,0
27
+ 3,25,Sunburst,198,0.131,0.1332123370575082,2.381818181818182,0.4,7.6,0
28
+ 2,26,Sunset Shimmer,549,0.36691111111111113,0.3728748379629632,2.4059744990892535,0.48,7.6,0
29
+ 1,27,Sweetie Belle,771,0.5104222222222222,0.5186199594907405,2.383294422827497,0.16,8.08,0
30
+ 3,28,Thorax,124,0.08544444444444445,0.0869096238425926,2.480645161290323,0.64,7.04,0
31
+ 2,29,Tirek,150,0.15304444444444446,0.1547323090277777,3.6730666666666667,0.88,8.16,0
32
+ 1,30,Trixie,609,0.5680888888888889,0.5745289583333333,3.3581609195402295,0.4,9.12,0
33
+ 1,31,Twilight,4628,3.041355555555555,3.0922718935027667,2.365790838375108,0.16,10.96,0
34
+ 3,32,Zecora,111,0.10197777777777778,0.1031401215277778,3.3073873873873874,0.96,6.8,0
35
+ 2,33,Demo,571,0.4166666666666667,0.42297333333333315,2.626970227670753,0.32,10.56,0
36
+ 2,34,Engineer,665,0.35619999999999996,0.3636970312500003,1.928300751879699,0.24,9.04,0
37
+ 1,35,Heavy,807,0.5254888888888889,0.5347120081018519,2.344188351920694,0.32,19.52,0
38
+ 2,36,Medic,489,0.25353333333333333,0.2588444097222222,1.8665030674846625,0.24,6.4,0
39
+ 1,37,Merasmus,739,1.079911111111111,1.0880798206018514,5.260730717185385,0.72,43.68,0
40
+ 2,38,Pauling,317,0.20524444444444445,0.20856595486111115,2.3308517350157727,0.4,11.36,0
41
+ 2,39,Scout,992,0.4975333333333333,0.5081581655092591,1.8055645161290321,0.16,15.28,0
42
+ 2,40,Sniper,852,0.43008888888888885,0.43924168402777747,1.8172769953051642,0.24,7.52,0
43
+ 1,41,Soldier,898,0.5638444444444444,0.5731967187500004,2.260400890868597,0.24,13.04,0
44
+ 2,42,Spy,691,0.37537777777777775,0.38310017939814806,1.9556584659913168,0.32,9.92,0
speaker_id_map.csv ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ speaker_id,name,display_name,label,utterance_count
2
+ 0,Apple Bloom,Apple Bloom,Apple Bloom (0),1296
3
+ 1,Applejack,Applejack,Applejack (1),3143
4
+ 2,Autumn Blaze,Autumn Blaze,Autumn Blaze (2),161
5
+ 3,Big Mac,Big Mac,Big Mac (3),222
6
+ 4,Cadance,Cadance,Cadance (4),165
7
+ 5,Celestia,Celestia,Celestia (5),583
8
+ 6,Cheerilee,Cheerilee,Cheerilee (6),111
9
+ 7,Chrysalis,Chrysalis,Chrysalis (7),178
10
+ 8,Cozy Glow,Cozy Glow,Cozy Glow (8),157
11
+ 9,Diamond Tiara,Diamond Tiara,Diamond Tiara (9),102
12
+ 10,Discord,Discord,Discord (10),476
13
+ 11,Fluttershy,Fluttershy,Fluttershy (11),1792
14
+ 12,Goldie Delicious,Goldie Delicious,Goldie Delicious (12),177
15
+ 13,Granny Smith,Granny Smith,Granny Smith (13),286
16
+ 14,Luna,Luna,Luna (14),191
17
+ 15,Maud,Maud,Maud (15),177
18
+ 16,Mrs. Cake,Mrs. Cake,Mrs. Cake (16),135
19
+ 17,Pinkie,Pinkie,Pinkie (17),2080
20
+ 18,Rainbow,Rainbow,Rainbow (18),2333
21
+ 19,Rarity,Rarity,Rarity (19),2383
22
+ 20,Scootaloo,Scootaloo,Scootaloo (20),774
23
+ 21,Shining Armor,Shining Armor,Shining Armor (21),118
24
+ 22,Spike,Spike,Spike (22),1791
25
+ 23,Spitfire,Spitfire,Spitfire (23),134
26
+ 24,Starlight,Starlight,Starlight (24),1125
27
+ 25,Sunburst,Sunburst,Sunburst (25),198
28
+ 26,Sunset Shimmer,Sunset Shimmer,Sunset Shimmer (26),549
29
+ 27,Sweetie Belle,Sweetie Belle,Sweetie Belle (27),771
30
+ 28,Thorax,Thorax,Thorax (28),124
31
+ 29,Tirek,Tirek,Tirek (29),150
32
+ 30,Trixie,Trixie,Trixie (30),609
33
+ 31,Twilight,Twilight,Twilight (31),4628
34
+ 32,Zecora,Zecora,Zecora (32),111
35
+ 33,demo,Demo,Demo (33),571
36
+ 34,engineer,Engineer,Engineer (34),665
37
+ 35,heavy,Heavy,Heavy (35),807
38
+ 36,medic,Medic,Medic (36),489
39
+ 37,merasmus,Merasmus,Merasmus (37),739
40
+ 38,pauling,Pauling,Pauling (38),317
41
+ 39,scout,Scout,Scout (39),992
42
+ 40,sniper,Sniper,Sniper (40),852
43
+ 41,soldier,Soldier,Soldier (41),898
44
+ 42,spy,Spy,Spy (42),691
torch_hf_optimizations.py ADDED
The diff for this file is too large to render. See raw diff