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 文档
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""RPG《剑与声》程序合成音效(5 件):hurt flee step coin levelup。
|
||||
|
||||
输出 /tmp/rpg_syn/*.wav(44.1kHz 立体声,峰值归一 0.89),随后 ffmpeg 转 ogg+m4a。
|
||||
音高敏感件用合成保证可控:levelup 为 A 小调五声(A4 C5 D5 E5 A5)上行钟声琶音。
|
||||
"""
|
||||
import os
|
||||
|
||||
import numpy as np
|
||||
import soundfile as sf
|
||||
|
||||
sr = 44100
|
||||
OUT = "/tmp/rpg_syn"
|
||||
os.makedirs(OUT, exist_ok=True)
|
||||
|
||||
|
||||
def norm(y, peak=0.89):
|
||||
return (y / np.max(np.abs(y)) * peak).astype(np.float32)
|
||||
|
||||
|
||||
def stereo(y):
|
||||
return np.stack([y, y], axis=1)
|
||||
|
||||
|
||||
def place(y, start_s, sig):
|
||||
s = int(start_s * sr)
|
||||
n = min(len(sig), len(y) - s)
|
||||
y[s:s + n] += sig[:n]
|
||||
|
||||
|
||||
# hurt:低频闷击(60Hz 轰 + 平滑噪声团),与 atk_hit 的金属/肉质听感区分
|
||||
t = np.arange(int(sr * 0.35)) / sr
|
||||
thud = np.sin(2 * np.pi * 60 * t) * np.exp(-t * 18)
|
||||
noise = np.random.default_rng(7).standard_normal(len(t))
|
||||
noise = np.convolve(noise, np.ones(64) / 64, mode="same") * np.exp(-t * 25) * 0.6
|
||||
sf.write(f"{OUT}/hurt.wav", stereo(norm(thud + noise)), sr)
|
||||
|
||||
# flee:渐远脚步(4 步,幅度逐次衰减、越走越闷)
|
||||
y = np.zeros(int(sr * 1.2), dtype=np.float32)
|
||||
for i in range(4):
|
||||
L = int(0.12 * sr)
|
||||
tt = np.arange(L) / sr
|
||||
w = 32 + i * 24
|
||||
st = np.convolve(np.random.default_rng(i).standard_normal(L), np.ones(w) / w, mode="same")
|
||||
st *= np.exp(-tt * 30) * (0.9 ** i)
|
||||
place(y, i * 0.28, st)
|
||||
sf.write(f"{OUT}/flee.wav", stereo(norm(y)), sr)
|
||||
|
||||
# step:单步脚步(短噪声团 + 90Hz 触点)
|
||||
t = np.arange(int(sr * 0.22)) / sr
|
||||
n = np.random.default_rng(3).standard_normal(len(t))
|
||||
body = np.convolve(n, np.ones(24) / 24, mode="same") * np.exp(-t * 35)
|
||||
toe = np.sin(2 * np.pi * 90 * t) * np.exp(-t * 30) * 0.5
|
||||
sf.write(f"{OUT}/step.wav", stereo(norm(body + toe)), sr)
|
||||
|
||||
# coin:硬币叮当(非谐波高频泛音簇,两次碰撞)
|
||||
y = np.zeros(int(sr * 0.45), dtype=np.float32)
|
||||
for start, dec in [(0.0, 28), (0.12, 32)]:
|
||||
seg = np.arange(int(sr * (0.45 - start))) / sr
|
||||
sig = sum(a * np.sin(2 * np.pi * f * seg) * np.exp(-seg * dec) * 0.3
|
||||
for f, a in [(5200, 1.0), (6900, 0.7), (9100, 0.45), (11400, 0.3)])
|
||||
place(y, start, sig)
|
||||
sf.write(f"{OUT}/coin.wav", stereo(norm(y)), sr)
|
||||
|
||||
# levelup:上行钟声琶音(A 小调五声 A4 C5 D5 E5 A5;钟 = 基音 + 非谐波泛音快衰减)
|
||||
y = np.zeros(int(sr * 1.4), dtype=np.float32)
|
||||
for i, f in enumerate([440.0, 523.25, 587.33, 659.25, 880.0]):
|
||||
seg = np.arange(int(sr * 1.0)) / sr
|
||||
bell = sum(a * np.sin(2 * np.pi * f * r * seg) * np.exp(-seg * d)
|
||||
for r, a, d in [(1, 1.0, 4), (2.4, 0.35, 7), (3.9, 0.15, 10)])
|
||||
place(y, i * 0.18, bell * 0.4)
|
||||
sf.write(f"{OUT}/levelup.wav", stereo(norm(y)), sr)
|
||||
print("SYN done:", sorted(os.listdir(OUT)))
|
||||
Reference in New Issue
Block a user