248 lines
9.9 KiB
Markdown
248 lines
9.9 KiB
Markdown
# Ming-omni-tts Offline Inference
|
|||
|
|
|
||
|
|
`end2end.py` runs Ming dense 0.5B end to end with vLLM-Omni. It uses the in-repo Ming prompt assembly helper directly, so the example request shape matches the real integration instead of a simplified wrapper.
|
||
|
|
|
||
|
|
## Files
|
||
|
|
|
||
|
|
| File | Purpose |
|
||
|
|
|---|---|
|
||
|
|
| `end2end.py` | Driver: CLI, case loading, prompt construction, orchestration (~150 lines) |
|
||
|
|
| `cases.yaml` | All 11 built-in case definitions (prompt, text, instruction, ref-audio flags, flow controls) |
|
||
|
|
| `runner.py` | Engine management and audio output (streaming + blocking paths) |
|
||
|
|
|
||
|
|
## Model Overview
|
||
|
|
|
||
|
|
Ming dense 0.5B is exposed here as a two-stage offline pipeline:
|
||
|
|
|
||
|
|
- **Stage 0**: Qwen2-based AR generation with Ming prompt formatting and inline flow controls
|
||
|
|
- **Stage 1**: audio VAE decode to mono 44.1 kHz waveform
|
||
|
|
|
||
|
|
`config_ming_tts.py` adapts the checkpoint's HuggingFace config fields
|
||
|
|
(LLM, DiT, aggregator, AudioVAE, token ids). `vllm_omni/deploy/ming_tts.yaml`
|
||
|
|
selects the vLLM-Omni pipeline and stage runtime topology, including
|
||
|
|
connectors, async chunking, memory limits, and sampling defaults.
|
||
|
|
|
||
|
|
The example supports both:
|
||
|
|
|
||
|
|
- **Blocking eager** via `vllm_omni/deploy/ming_tts.yaml`
|
||
|
|
- **Async chunk eager** via `vllm_omni/deploy/ming_tts.yaml` (default `async_chunk: true`)
|
||
|
|
|
||
|
|
## Setup
|
||
|
|
|
||
|
|
Install vLLM-Omni with the platform requirements for your accelerator:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
uv pip install -e .
|
||
|
|
```
|
||
|
|
|
||
|
|
The Ming offline example does not require a separate upstream Ming package.
|
||
|
|
Reference-audio cases use the repo dependencies for audio loading,
|
||
|
|
resampling, and CampPlus speaker extraction, including `soundfile`,
|
||
|
|
`torchaudio`, and `onnxruntime-rocm` in the documented ROCm environment.
|
||
|
|
|
||
|
|
The tested ROCm environment is summarized in the
|
||
|
|
[Ming recipe](https://github.com/vllm-project/vllm-omni/blob/main/recipes/inclusionAI/Ming-omni-tts-0.5B.md).
|
||
|
|
|
||
|
|
## Supported Cases
|
||
|
|
|
||
|
|
These cases cover the upstream dense 0.5B cookbook surface that maps cleanly onto the current vLLM-Omni example:
|
||
|
|
|
||
|
|
- `style`: zero-speaker style-conditioned speech
|
||
|
|
- `ip`: zero-speaker IP voice generation
|
||
|
|
- `bgm`: music-only generation
|
||
|
|
- `tta`: text-to-audio event generation with FlowLoss controls
|
||
|
|
- `emotion`: reference-audio speech with emotion control
|
||
|
|
- `basic`: reference-audio speech with speed / pitch / volume control
|
||
|
|
- `dialect`: reference-audio speech with dialect control
|
||
|
|
- `zero_shot`: reference-audio cloning with explicit transcript
|
||
|
|
- `podcast`: multi-reference dialogue generation with automatic speaker embedding extraction
|
||
|
|
- `speech_bgm`: speech with background music conditioning
|
||
|
|
- `speech_sound`: speech with environmental sound conditioning
|
||
|
|
|
||
|
|
## Quick Start
|
||
|
|
|
||
|
|
Run the zero-speaker style example:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python examples/offline_inference/text_to_speech/ming_tts/end2end.py \
|
||
|
|
--case style \
|
||
|
|
--deploy-config vllm_omni/deploy/ming_tts.yaml \
|
||
|
|
--enforce-eager
|
||
|
|
```
|
||
|
|
|
||
|
|
Run zero-shot cloning with a transcript:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python examples/offline_inference/text_to_speech/ming_tts/end2end.py \
|
||
|
|
--case zero_shot \
|
||
|
|
--ref-audio /path/to/10002287-00000094.wav \
|
||
|
|
--ref-text "在此奉劝大家别乱打美白针。" \
|
||
|
|
--deploy-config vllm_omni/deploy/ming_tts.yaml \
|
||
|
|
--enforce-eager
|
||
|
|
```
|
||
|
|
|
||
|
|
Run emotion-controlled speech:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python examples/offline_inference/text_to_speech/ming_tts/end2end.py \
|
||
|
|
--case emotion \
|
||
|
|
--ref-audio /path/to/emotion_prompt.wav \
|
||
|
|
--deploy-config vllm_omni/deploy/ming_tts.yaml \
|
||
|
|
--enforce-eager
|
||
|
|
```
|
||
|
|
|
||
|
|
Run podcast generation with two reference clips:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python examples/offline_inference/text_to_speech/ming_tts/end2end.py \
|
||
|
|
--case podcast \
|
||
|
|
--ref-audio-paths /path/to/CTS-CN-F2F-2019-11-11-423-012-A.wav /path/to/CTS-CN-F2F-2019-11-11-423-012-B.wav \
|
||
|
|
--deploy-config vllm_omni/deploy/ming_tts.yaml \
|
||
|
|
--enforce-eager
|
||
|
|
```
|
||
|
|
|
||
|
|
The script automatically extracts one 192-d speaker embedding per reference WAV using the Ming model's `campplus.onnx`.
|
||
|
|
|
||
|
|
If you already have precomputed multi-speaker embeddings, you can override extraction with:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
--speaker-embedding /path/to/podcast_speaker_embeddings.json
|
||
|
|
```
|
||
|
|
|
||
|
|
where the JSON is a list of speaker embeddings, one 192-d vector per speaker.
|
||
|
|
|
||
|
|
Run text-to-audio event generation:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python examples/offline_inference/text_to_speech/ming_tts/end2end.py \
|
||
|
|
--case tta \
|
||
|
|
--deploy-config vllm_omni/deploy/ming_tts.yaml \
|
||
|
|
--enforce-eager
|
||
|
|
```
|
||
|
|
|
||
|
|
Use async_chunk streaming:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python examples/offline_inference/text_to_speech/ming_tts/end2end.py \
|
||
|
|
--case basic \
|
||
|
|
--ref-audio /path/to/10002287-00000095.wav \
|
||
|
|
--streaming \
|
||
|
|
--deploy-config vllm_omni/deploy/ming_tts.yaml \
|
||
|
|
--enforce-eager
|
||
|
|
```
|
||
|
|
|
||
|
|
`--streaming` uses `AsyncOmni` and the async_chunk stage config. It currently
|
||
|
|
supports one prompt per process invocation; use blocking mode for
|
||
|
|
`--num-prompts > 1`.
|
||
|
|
|
||
|
|
Collect runtime stats and a manifest:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python examples/offline_inference/text_to_speech/ming_tts/end2end.py \
|
||
|
|
--case style \
|
||
|
|
--deploy-config vllm_omni/deploy/ming_tts.yaml \
|
||
|
|
--enforce-eager \
|
||
|
|
--enable-stats \
|
||
|
|
--stats-log-file output_audio/ming_style_pipeline.log \
|
||
|
|
--metadata-json output_audio/ming_style_manifest.json
|
||
|
|
```
|
||
|
|
|
||
|
|
## Reference Fixtures
|
||
|
|
|
||
|
|
The upstream Ming cookbook uses these public audio fixtures from `inclusionAI/Ming-omni-tts/data/wavs`:
|
||
|
|
|
||
|
|
- `10002287-00000094.wav` for zero-shot cloning
|
||
|
|
- `10002287-00000095.wav` for `basic`
|
||
|
|
- `emotion_prompt.wav` for `emotion`
|
||
|
|
- `yue_prompt.wav` for `dialect`
|
||
|
|
- `00000309-00000300.wav` for `speech_bgm` and `speech_sound`
|
||
|
|
- `CTS-CN-F2F-2019-11-11-423-012-A.wav` and `CTS-CN-F2F-2019-11-11-423-012-B.wav` for `podcast`
|
||
|
|
|
||
|
|
## Validation Matrix
|
||
|
|
|
||
|
|
The repo-facing example is intended to cover the same dense TTS workflows used
|
||
|
|
by the local Ming validation script:
|
||
|
|
|
||
|
|
| Case | Blocking `deploy/ming_tts.yaml` | Async chunk `deploy/ming_tts.yaml` | Extra inputs |
|
||
|
|
|---|---:|---:|---|
|
||
|
|
| `style` | Yes | Optional smoke test | none |
|
||
|
|
| `ip` | Yes | Optional smoke test | none |
|
||
|
|
| `bgm` | Yes | Optional smoke test | none |
|
||
|
|
| `tta` | Yes | Optional smoke test | none |
|
||
|
|
| `emotion` | Yes | Yes | `--ref-audio emotion_prompt.wav` |
|
||
|
|
| `basic` | Yes | Yes | `--ref-audio 10002287-00000095.wav` |
|
||
|
|
| `dialect` | Yes | Yes | `--ref-audio yue_prompt.wav` |
|
||
|
|
| `zero_shot` | Yes | Yes | `--ref-audio 10002287-00000094.wav --ref-text ...` |
|
||
|
|
| `podcast` | Yes | Yes | two `--ref-audio-paths` |
|
||
|
|
| `speech_bgm` | Yes | Yes | `--ref-audio 00000309-00000300.wav` |
|
||
|
|
| `speech_sound` | Yes | Yes | `--ref-audio 00000309-00000300.wav` |
|
||
|
|
|
||
|
|
## Validated Outputs
|
||
|
|
|
||
|
|
The following measurements are retained from an earlier L4 CUDA validation;
|
||
|
|
they are not ROCm benchmark results. Default async_chunk matched blocking
|
||
|
|
output frame counts and Stage-1 patch counts for every case:
|
||
|
|
|
||
|
|
| Case | Blocking frames / patches / sec | Async chunk frames / patches / sec |
|
||
|
|
|---|---:|---:|
|
||
|
|
| `style` | 409248 / 29 / 9.28 | 409248 / 29 / 9.28 |
|
||
|
|
| `ip` | 183456 / 13 / 4.16 | 183456 / 13 / 4.16 |
|
||
|
|
| `bgm` | 1326528 / 94 / 30.08 | 1326528 / 94 / 30.08 |
|
||
|
|
| `tta` | 465696 / 33 / 10.56 | 465696 / 33 / 10.56 |
|
||
|
|
| `emotion` | 324576 / 23 / 7.36 | 324576 / 23 / 7.36 |
|
||
|
|
| `basic` | 211680 / 15 / 4.80 | 211680 / 15 / 4.80 |
|
||
|
|
| `dialect` | 239904 / 17 / 5.44 | 239904 / 17 / 5.44 |
|
||
|
|
| `zero_shot` | 409248 / 29 / 9.28 | 409248 / 29 / 9.28 |
|
||
|
|
| `podcast` | 437472 / 31 / 9.92 | 437472 / 31 / 9.92 |
|
||
|
|
| `speech_bgm` | 296352 / 21 / 6.72 | 296352 / 21 / 6.72 |
|
||
|
|
| `speech_sound` | 352800 / 25 / 8.00 | 352800 / 25 / 8.00 |
|
||
|
|
|
||
|
|
## Key Arguments
|
||
|
|
|
||
|
|
| Argument | Description |
|
||
|
|
|---|---|
|
||
|
|
| `--model` | Hugging Face repo or local Ming checkpoint path |
|
||
|
|
| `--deploy-config` | Deploy config YAML. Use `vllm_omni/deploy/ming_tts.yaml` |
|
||
|
|
| `--case` | Built-in demo case |
|
||
|
|
| `--ref-audio` | Single reference wav path for cloning-style cases |
|
||
|
|
| `--ref-audio-paths` | Multiple reference wav paths, used by `podcast` |
|
||
|
|
| `--ref-text` | Reference transcript. Required for `zero_shot` |
|
||
|
|
| `--instructions` | Free-form Ming instruction string |
|
||
|
|
| `--instruction-json` | Structured Ming instruction JSON |
|
||
|
|
| `--speaker-embedding` | JSON file containing a 192-d speaker embedding |
|
||
|
|
| `--extract-speaker-embeddings` | Force CampPlus speaker extraction from the provided reference audio paths |
|
||
|
|
| `--max-decode-steps` | Override `ming_max_decode_steps` |
|
||
|
|
| `--num-prompts` | Repeat the same case N times. Outputs are indexed when `N > 1` |
|
||
|
|
| `--streaming` | Use `AsyncOmni` and async_chunk transport |
|
||
|
|
| `--enforce-eager` | Recommended for Ming dense; non-eager is out of scope |
|
||
|
|
| `--enable-stats` / `--log-stats` | Enable vLLM-Omni per-request stats logging |
|
||
|
|
| `--stats-log-file` | Optional path for the stats log |
|
||
|
|
| `--metadata-json` | Optional path for the run manifest JSON |
|
||
|
|
| `--stage-init-timeout` | Per-stage initialization timeout in seconds |
|
||
|
|
| `--init-timeout` | Total initialization timeout in seconds |
|
||
|
|
| `--batch-timeout` | Batch timeout in seconds |
|
||
|
|
| `--worker-backend` | `multi_process` or `ray` |
|
||
|
|
| `--ray-address` | Ray cluster address when using `--worker-backend ray` |
|
||
|
|
|
||
|
|
## Output
|
||
|
|
|
||
|
|
- The script writes one mono 44.1 kHz WAV file per run
|
||
|
|
- Default output directory: `output_audio/`
|
||
|
|
- Default filename: `ming_<case>.wav`
|
||
|
|
- When `--num-prompts > 1`, outputs are indexed as `ming_<case>_00000.wav`, `..._00001.wav`, etc.
|
||
|
|
- When stats are enabled, the script can also write:
|
||
|
|
- a stats log file such as `ming_style_pipeline.log`
|
||
|
|
- a manifest JSON with per-output metadata, stage durations, peak memory info,
|
||
|
|
and streaming client latency metrics when `--streaming` is used
|
||
|
|
|
||
|
|
## Notes
|
||
|
|
|
||
|
|
- `style` and `ip` are zero-speaker paths and do not require a reference clip
|
||
|
|
- `emotion`, `basic`, `dialect`, `speech_bgm`, and `speech_sound` require one reference clip
|
||
|
|
- `zero_shot` requires both `--ref-audio` and `--ref-text`
|
||
|
|
- `podcast` requires at least two reference clips via `--ref-audio-paths`
|
||
|
|
- `podcast` automatically extracts one speaker embedding per reference clip
|
||
|
|
- `--speaker-embedding` may contain either one 192-d vector or a list of 192-d vectors
|
||
|
|
- `--enforce-eager` was used for the validated runs
|
||
|
|
- The earlier L4 validation used SDPA for the Ming audio VAE instead of
|
||
|
|
FlashAttention2, which is the preferred default when available.
|