初始提交:3 款纯音频游戏 + 共享引擎 + 完整文档与资产管线

- 共享引擎(app/src/main):GestureEngine 6 手势识别、AudioEngine 三层音频
  (ExoPlayer BGM 交叉淡入 / 对白 ducking / SoundPool 低延迟)、Game/GameEngine
  状态机接口、MainActivity 全屏封面 + TalkBack
- 《声命线》storyline:末世互动广播剧,3 幕 8 抉择点 4 结局
- 《声东击西》reflex:听声辨位反应,12 波战斗 + BGM 三档 BPM 变速
- 《暗室》escape:3x3 网格密室逃脱,钥匙→铁柜→保险丝→电箱→大门谜题链
- 文档:setup(从零搭建)/ architecture(架构)/ engine-api(引擎 API)/
  asset-pipeline(资产流水线)/ games/*(三款游戏设计文档)
- 资产管线:Qwen3-TTS 对白 + 程序合成音效/BGM + Mureka BGM 的 gen 脚本与台账,
  全部 ogg 资产入库可复现
- env.d 环境脚本:android/tts 入库;sfx/mureka 含密钥不入库,仅提交 .example.sh 模板
This commit is contained in:
SoundGame Dev
2026-08-29 13:00:34 +08:00
commit f0f0a9b50e
446 changed files with 2627836 additions and 0 deletions
@@ -0,0 +1,43 @@
#!/usr/bin/env python3
"""Add a dynamic silence-pad node to an encoder onnx so it accepts ANY audio length: pads the sample
axis up to the next multiple of 4096 with zeros (matches what eager does internally). argv: <in> <out>."""
import sys, os
import onnx
from onnx import helper, TensorProto
IN, OUT = sys.argv[1], sys.argv[2]
m = onnx.load(IN, load_external_data=True); g = m.graph
audio = g.input[0].name; adt = g.input[0].type.tensor_type.elem_type
opset = {o.domain: o.version for o in m.opset_import}.get("", 0)
print(f"input '{audio}' dtype={adt} opset={opset}", flush=True)
g.initializer.extend([
helper.make_tensor("_pc4096", TensorProto.INT64, [1], [4096]),
helper.make_tensor("_pzeros5", TensorProto.INT64, [5], [0, 0, 0, 0, 0]),
helper.make_tensor("_pidx2", TensorProto.INT64, [1], [2]),
helper.make_tensor("_pc0", adt, [], [0]),
])
for n in g.node: # rewire consumers to the padded tensor
for i, inp in enumerate(n.input):
if inp == audio: n.input[i] = "_audio_pad"
g.node.extend([ # pad = (4096 - N%4096) % 4096, pad end of axis 2
helper.make_node("Shape", [audio], ["_pshape"], name="_pad_shape"),
helper.make_node("Gather", ["_pshape", "_pidx2"], ["_pN"], name="_pad_gather", axis=0),
helper.make_node("Mod", ["_pN", "_pc4096"], ["_pr"], name="_pad_mod1"),
helper.make_node("Sub", ["_pc4096", "_pr"], ["_pt"], name="_pad_sub"),
helper.make_node("Mod", ["_pt", "_pc4096"], ["_ppad"], name="_pad_mod2"),
helper.make_node("Concat", ["_pzeros5", "_ppad"], ["_ppads"], name="_pad_concat", axis=0),
helper.make_node("Pad", [audio, "_ppads", "_pc0"], ["_audio_pad"], name="_pad_pad", mode="constant"),
])
avail = {i.name for i in g.initializer} | {i.name for i in g.input} | {""}
order, rem = [], list(g.node)
while rem:
nx, prog = [], False
for nd in rem:
if all(i in avail for i in nd.input): order.append(nd); [avail.add(o) for o in nd.output]; prog = True
else: nx.append(nd)
rem = nx
if not prog: raise RuntimeError("topo stuck")
del g.node[:]; g.node.extend(order)
for f in (OUT, OUT + ".data"):
if os.path.exists(f): os.remove(f)
onnx.save(m, OUT, save_as_external_data=True, all_tensors_to_one_file=True, location=os.path.basename(OUT) + ".data", size_threshold=1024)
print(f"padded encoder -> {OUT}", flush=True)