You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
my-bricks/scripts/audio.gd

66 lines
2.3 KiB

extends Node
## 简易音效:用 AudioStreamWAV 程序合成 4 种音效。
## 每次播放 new 一个 AudioStreamPlayer,结束后自动释放。
## 之后要替换成 .wav 素材,把 _generate_xxx() 换成 load() 资源即可。
const SAMPLE_RATE: int = 22050
func play_hit() -> void:
_play(_make_sin(880.0, 0.05, 0.4))
func play_break() -> void:
_play(_make_noise(0.18, 0.5))
func play_lost() -> void:
_play(_make_sweep(440.0, 110.0, 0.4, 0.5))
func play_win() -> void:
_play(_make_sweep(523.0, 1046.0, 0.5, 0.5))
# ───────── 合成函数 ─────────
func _make_sin(freq: float, duration: float, volume: float) -> AudioStreamWAV:
return _build(duration, func(t: float, d: float) -> float:
var env: float = 1.0 - t / d # 线性衰减
return sin(TAU * freq * t) * volume * env
)
func _make_noise(duration: float, volume: float) -> AudioStreamWAV:
return _build(duration, func(t: float, d: float) -> float:
var env: float = 1.0 - t / d
return randf_range(-volume, volume) * env
)
func _make_sweep(from_freq: float, to_freq: float, duration: float, volume: float) -> AudioStreamWAV:
return _build(duration, func(t: float, d: float) -> float:
var progress: float = t / d
var f: float = lerp(from_freq, to_freq, progress)
var env: float = (1.0 - progress) * (1.0 - progress) # 平方衰减
return sin(TAU * f * t) * volume * env
)
# 构造一个 AudioStreamWAV;sample_fn(t, duration) 返回 [-1, 1] 的振幅
func _build(duration: float, sample_fn: Callable) -> AudioStreamWAV:
var sample_count: int = int(SAMPLE_RATE * duration)
var data := PackedByteArray()
data.resize(sample_count * 2) # 16-bit 单声道
for i in range(sample_count):
var t: float = float(i) / SAMPLE_RATE
var s: float = clamp(sample_fn.call(t, duration), -1.0, 1.0)
# 16-bit: 范围 [-32768, 32767]
var sample_i: int = int(s * 32767.0)
data[2 * i] = sample_i & 0xFF
data[2 * i + 1] = (sample_i >> 8) & 0xFF
var stream := AudioStreamWAV.new()
stream.format = AudioStreamWAV.FORMAT_16_BITS
stream.mix_rate = SAMPLE_RATE
stream.stereo = false
stream.data = data
return stream
func _play(stream: AudioStream) -> void:
var player := AudioStreamPlayer.new()
player.stream = stream
add_child(player)
player.play()
player.finished.connect(player.queue_free)