Signed-off-by: lishunyang <lishunyang12@163.com> Signed-off-by: xiaohajiayou <923390377@qq.com> Co-authored-by: lishunyang <lishunyang12@163.com>
129 lines
4.6 KiB
Python
129 lines
4.6 KiB
Python
"""Offline e2e example for Ming-flash-omni-2.0 standalone talker (TTS)."""
|
|
|
|
import os
|
|
from typing import Any
|
|
|
|
import soundfile as sf
|
|
import torch
|
|
from vllm.utils.argparse_utils import FlexibleArgumentParser
|
|
|
|
os.environ["VLLM_WORKER_MULTIPROC_METHOD"] = "spawn"
|
|
|
|
from vllm_omni.entrypoints.omni import Omni
|
|
from vllm_omni.inputs.data import OmniTokensPrompt
|
|
from vllm_omni.model_executor.models.ming_flash_omni.prompt_utils import (
|
|
DEFAULT_PROMPT,
|
|
create_instruction,
|
|
)
|
|
|
|
MODEL_NAME = "Jonathan1909/Ming-flash-omni-2.0"
|
|
DEFAULT_STAGE_CONFIG = "vllm_omni/model_executor/stage_configs/ming_flash_omni_tts.yaml"
|
|
|
|
|
|
def get_messages(case: str, text_override: str | None) -> dict[str, Any]:
|
|
if case == "style":
|
|
text = text_override or "我会一直在这里陪着你,直到你慢慢、慢慢地沉入那个最温柔的梦里……好吗?"
|
|
instruction = create_instruction(
|
|
{
|
|
"风格": "这是一种ASMR耳语,属于一种旨在引发特殊感官体验的创意风格。这个女性使用轻柔的普通话进行耳语,声音气音成分重。音量极低,紧贴麦克风,语速极慢,旨在制造触发听者颅内快感的声学刺激。",
|
|
}
|
|
)
|
|
return {
|
|
"prompt": DEFAULT_PROMPT,
|
|
"text": text,
|
|
"instruction": instruction,
|
|
"use_zero_spk_emb": True,
|
|
}
|
|
if case == "ip":
|
|
text = text_override or "这款产品的名字,叫变态坑爹牛肉丸。"
|
|
return {
|
|
"prompt": DEFAULT_PROMPT,
|
|
"text": text,
|
|
"instruction": create_instruction({"IP": "灵小甄"}),
|
|
"use_zero_spk_emb": True,
|
|
}
|
|
if case == "basic":
|
|
text = text_override or "我们当迎着阳光辛勤耕作,去摘取,去制作,去品尝,去馈赠。"
|
|
return {
|
|
"prompt": DEFAULT_PROMPT,
|
|
"text": text,
|
|
"instruction": create_instruction({"语速": "快速", "基频": "中", "音量": "中"}),
|
|
"use_zero_spk_emb": True,
|
|
}
|
|
raise ValueError(f"Unknown case: {case}")
|
|
|
|
|
|
def save_audio(mm: dict[str, Any], output_path: str) -> None:
|
|
if not mm or "audio" not in mm:
|
|
raise RuntimeError("No audio found in model output")
|
|
audio = mm["audio"]
|
|
sr_raw = mm.get("sr", 44100)
|
|
if isinstance(sr_raw, torch.Tensor):
|
|
sample_rate = int(sr_raw.item())
|
|
else:
|
|
sample_rate = int(sr_raw)
|
|
waveform = audio.squeeze().float().cpu().numpy()
|
|
sf.write(output_path, waveform, sample_rate)
|
|
print(f"Saved {output_path} ({len(waveform) / sample_rate:.2f}s, {sample_rate}Hz)")
|
|
|
|
|
|
def parse_args():
|
|
parser = FlexibleArgumentParser(description="Ming-flash-omni standalone talker offline e2e example")
|
|
parser.add_argument("--model", type=str, default=MODEL_NAME, help="Model name or local path.")
|
|
parser.add_argument(
|
|
"--stage-configs-path",
|
|
type=str,
|
|
default=DEFAULT_STAGE_CONFIG,
|
|
help="Path to stage configs yaml for standalone talker deployment.",
|
|
)
|
|
parser.add_argument(
|
|
"--case",
|
|
type=str,
|
|
default="style",
|
|
choices=["style", "ip", "basic"],
|
|
help="Example case.",
|
|
)
|
|
parser.add_argument("--text", type=str, default=None, help="Override default text for the selected case.")
|
|
parser.add_argument("--output", type=str, default=None, help="Output wav path.")
|
|
parser.add_argument("--log-stats", action="store_true", default=False, help="Enable stats logging.")
|
|
parser.add_argument("--init-timeout", type=int, default=600, help="Engine init timeout in seconds.")
|
|
parser.add_argument("--stage-init-timeout", type=int, default=300, help="Single stage init timeout in seconds.")
|
|
return parser.parse_args()
|
|
|
|
|
|
def main():
|
|
args = parse_args()
|
|
|
|
omni = Omni(
|
|
model=args.model,
|
|
stage_configs_path=args.stage_configs_path,
|
|
log_stats=args.log_stats,
|
|
init_timeout=args.init_timeout,
|
|
stage_init_timeout=args.stage_init_timeout,
|
|
)
|
|
|
|
messages = get_messages(args.case, args.text)
|
|
decode_args = {
|
|
# Standalone TTS deployment
|
|
"ming_task": "instruct",
|
|
"max_decode_steps": 200,
|
|
"cfg": 2.0,
|
|
"sigma": 0.25,
|
|
"temperature": 0.0,
|
|
}
|
|
req = OmniTokensPrompt(
|
|
prompt_token_ids=[0],
|
|
additional_information={**messages, **decode_args},
|
|
)
|
|
|
|
outputs = omni.generate(req)
|
|
mm = outputs[0].outputs[0].multimodal_output
|
|
|
|
output_path = args.output or f"output_{args.case}.wav"
|
|
save_audio(mm, output_path)
|
|
omni.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|