Files
SoundGame/web/rhythm/gen/gen_voice.py
T
SoundGame Dev cc0f8c008d HTML5 重写:6 款游戏 + 用户系统 + 全局设置 + 5 轮迭代重构
- 新增 web/ 纯音频 HTML5 平台:共享引擎(手势/WebAudio 三层/设置/震动)、
  Node 零依赖服务器(静态 + 注册登录 + 进度存档 API)
- 游戏:剑与声(RPG 新作)、声命线、暗室(重设计)、音序回响(重设计)、
  节拍行者(跟随领奏重构)、盲航(同向闪避重构);声东击西删除
- 全局功能:男声/女声双套 TTS 资产、三档语速、BGM 音量三档、
  震动反馈语言(10 种震型)、服务器进度同步
- 资产:899 ogg + 899 m4a(Qwen3-TTS 双性别语音、Stable Audio 3 音效、
  Mureka BGM 4+3 首),终检零缺失
- 文档:docs/design/ 13 篇设计审查文档、docs/audio/ 资产管线、
  web/server/README.md API 文档
2026-08-29 21:55:44 +08:00

79 lines
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""节拍行者 — 语音对白生成(Qwen3-TTS CustomVoice)。
用法(必须 flock,GPU 与其他 agent 共享):
flock /home/ubuntu/SoundGame/.gpu.lock \
/home/ubuntu/SoundGame/.venv-tts/bin/python gen_voice.py
产出 web/rhythm/audio/voice/*.wav(24kHz),随后由 gen_convert.sh 转 ogg/m4a。
"""
import os
os.environ.setdefault("HF_HOME", "/home/ubuntu/SoundGame/models/hf")
os.environ.setdefault("HF_ENDPOINT", "https://hf-mirror.com")
import numpy as np
import soundfile as sf
import torch
from qwen_tts import Qwen3TTSModel
OUT = "/home/ubuntu/SoundGame/web/rhythm/audio/voice"
os.makedirs(OUT, exist_ok=True)
# (id, 文本, 情感指令)
LINES = [
("welcome",
"欢迎来到节拍行者!规则很简单:仔细听提示音,在下一个节拍,做出对应的动作。"
"建议用有线耳机或扬声器游玩,蓝牙耳机会有延迟。",
"热情活力的游戏解说语气"),
("rules",
"听好了:上行音,向上滑;下行音,向下滑;低音,向左滑;高音,向右滑;"
"一声轻响,单击;两声轻响,快速双击。",
"清晰利落的教学语气,节奏感强"),
("tut_start",
"先来四小节练习。跟着节拍器,听到提示音,就在下一拍做动作。大胆做!",
"鼓励的语气"),
("tut_done",
"练习结束!正式挑战开始,一共四十个指令。预备!",
"兴奋、充满干劲的语气"),
("grade_s",
"评级,S!完美无缺,你就是节拍大师!",
"极度兴奋、欢呼的语气"),
("grade_a",
"评级,A!非常出色,差一点就完美了!",
"开心赞许的语气"),
("grade_b",
"评级,B。不错不错,继续加油!",
"温和鼓励的语气"),
("grade_c",
"评级,C。别灰心,多听节拍,你一定行!",
"安慰打气的语气"),
("restart",
"单击屏幕,再来一局。",
"轻松愉快的语气"),
]
model = Qwen3TTSModel.from_pretrained(
"Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice",
device_map="cuda:0",
dtype=torch.bfloat16,
)
wavs, sr = model.generate_custom_voice(
text=[t for _, t, _ in LINES],
language=["Chinese"] * len(LINES),
speaker=["Vivian"] * len(LINES),
instruct=[i for _, _, i in LINES],
)
for (vid, text, _), wav in zip(LINES, wavs):
wav = np.asarray(wav, dtype=np.float32)
dur = len(wav) / sr
peak = float(np.max(np.abs(wav))) if len(wav) else 0.0
assert dur > 0.3, f"{vid}: 时长异常 {dur:.2f}s"
assert peak > 0.05, f"{vid}: 峰值异常 {peak:.3f}"
path = os.path.join(OUT, vid + ".wav")
sf.write(path, wav, sr)
print(f"{vid}: {dur:.2f}s peak={peak:.3f} -> {path}")
print("ALL OK")