115 lines
3.2 KiB
GDScript
115 lines
3.2 KiB
GDScript
class_name Sfx
|
|
extends Node
|
|
|
|
## Primitive Klänge, im Code erzeugt: kurze Töne mit Hüllkurve.
|
|
## Keine Audiodateien, passend zu den primitiven Meshes.
|
|
|
|
const RATE := 22050
|
|
const HIT_COOLDOWN := 0.06
|
|
|
|
var _streams := {}
|
|
var _players: Array = []
|
|
var _next := 0
|
|
var _hit_gate := 0.0
|
|
|
|
|
|
func _ready() -> void:
|
|
for i in 8:
|
|
var pl := AudioStreamPlayer.new()
|
|
pl.volume_db = -8.0
|
|
add_child(pl)
|
|
_players.append(pl)
|
|
_streams["kauf"] = _tone([[660.0, 0.07]], 0.5)
|
|
_streams["verkauf"] = _tone([[520.0, 0.05], [330.0, 0.07]], 0.45)
|
|
_streams["wuerfeln"] = _noise(0.04, 0.35)
|
|
_streams["setzen"] = _tone([[180.0, 0.06]], 0.6, true)
|
|
_streams["aufwerten"] = _tone([[523.0, 0.08], [659.0, 0.08], [784.0, 0.12]], 0.55)
|
|
_streams["level"] = _tone([[440.0, 0.08], [880.0, 0.14]], 0.5)
|
|
_streams["treffer"] = _noise(0.03, 0.22)
|
|
_streams["krit"] = _tone([[220.0, 0.05]], 0.55, true)
|
|
_streams["heilung"] = _tone([[988.0, 0.06]], 0.3)
|
|
_streams["zauber"] = _tone([[880.0, 0.05], [1320.0, 0.09]], 0.45)
|
|
_streams["sieg"] = _tone([[523.0, 0.1], [659.0, 0.1], [784.0, 0.1], [1046.0, 0.2]], 0.6)
|
|
_streams["niederlage"] = _tone([[392.0, 0.12], [311.0, 0.12], [261.0, 0.24]], 0.6)
|
|
_streams["runde"] = _tone([[440.0, 0.06], [440.0, 0.06]], 0.4)
|
|
_streams["fehler"] = _tone([[200.0, 0.1]], 0.5, true)
|
|
|
|
|
|
## Lautstärke 0..1, 0 schaltet stumm.
|
|
func set_volume(linear: float) -> void:
|
|
for pl in _players:
|
|
pl.volume_db = linear_to_db(maxf(0.0001, linear)) - 8.0
|
|
pl.stream_paused = false
|
|
_muted = linear <= 0.001
|
|
|
|
|
|
var _muted := false
|
|
|
|
|
|
func play(name: String) -> void:
|
|
if _muted:
|
|
return
|
|
if not _streams.has(name):
|
|
return
|
|
if name == "treffer":
|
|
var now := Time.get_ticks_msec() / 1000.0
|
|
if now < _hit_gate:
|
|
return
|
|
_hit_gate = now + HIT_COOLDOWN
|
|
var pl: AudioStreamPlayer = _players[_next]
|
|
_next = (_next + 1) % _players.size()
|
|
pl.stream = _streams[name]
|
|
pl.play()
|
|
|
|
|
|
## Tonfolge [[frequenz, dauer], ...] mit Ein- und Ausblenden.
|
|
func _tone(notes: Array, gain: float, square: bool = false) -> AudioStreamWAV:
|
|
var total := 0.0
|
|
for n in notes:
|
|
total += n[1]
|
|
var frames := int(total * RATE)
|
|
var data := PackedByteArray()
|
|
data.resize(frames * 2)
|
|
var idx := 0
|
|
for n in notes:
|
|
var freq: float = n[0]
|
|
var len_frames := int(n[1] * RATE)
|
|
for i in len_frames:
|
|
var t := float(i) / RATE
|
|
var env := minf(1.0, float(i) / (0.005 * RATE)) * minf(1.0, float(len_frames - i) / (0.02 * RATE))
|
|
var v := sin(TAU * freq * t)
|
|
if square:
|
|
v = 1.0 if v >= 0.0 else -1.0
|
|
_put(data, idx, v * env * gain)
|
|
idx += 1
|
|
if idx >= frames:
|
|
break
|
|
return _wav(data)
|
|
|
|
|
|
## Kurzes Rauschen für Treffer und Würfeln.
|
|
func _noise(dur: float, gain: float) -> AudioStreamWAV:
|
|
var frames := int(dur * RATE)
|
|
var data := PackedByteArray()
|
|
data.resize(frames * 2)
|
|
var rng := RandomNumberGenerator.new()
|
|
rng.seed = 1234
|
|
for i in frames:
|
|
var env := 1.0 - float(i) / float(frames)
|
|
_put(data, i, rng.randf_range(-1.0, 1.0) * env * env * gain)
|
|
return _wav(data)
|
|
|
|
|
|
func _put(data: PackedByteArray, index: int, v: float) -> void:
|
|
var s := int(clampf(v, -1.0, 1.0) * 32767.0)
|
|
data.encode_s16(index * 2, s)
|
|
|
|
|
|
func _wav(data: PackedByteArray) -> AudioStreamWAV:
|
|
var w := AudioStreamWAV.new()
|
|
w.format = AudioStreamWAV.FORMAT_16_BITS
|
|
w.mix_rate = RATE
|
|
w.stereo = false
|
|
w.data = data
|
|
return w
|