#!/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)))