class_name Sfx extends Node ## Kurze, zur Laufzeit erzeugte Töne (keine Audiodateien nötig). var streams := {} var players: Array[AudioStreamPlayer] = [] var volume := 0.0 # Ton ist standardmäßig aus var muted := false # Tests: nie etwas abspielen, egal was die Regler sagen var music_player: AudioStreamPlayer var music_volume := 0.0 var music_task := -1 # die Musik wird im Hintergrund berechnet (spart ~150 ms beim Start) var music_box := {} func _ready() -> void: streams["click"] = _tone(880, 0.05, "sine") streams["play"] = _tone(440, 0.14, "sine", 660) streams["attack"] = _tone(220, 0.16, "square", 90) streams["damage"] = _tone(140, 0.12, "square") streams["heal"] = _tone(523, 0.22, "sine", 784) streams["combo"] = _tone(660, 0.2, "sine", 1100) streams["buy"] = _tone(1200, 0.08, "sine", 1700) streams["turn"] = _tone(520, 0.3, "sine", 1040) streams["win"] = _tone(400, 0.6, "sine", 1200) streams["lose"] = _tone(320, 0.7, "square", 90) for i in 4: var p := AudioStreamPlayer.new() add_child(p) players.append(p) music_player = AudioStreamPlayer.new() add_child(music_player) var box := music_box music_task = WorkerThreadPool.add_task(func(): box["wav"] = _music()) func _process(_delta: float) -> void: if music_task >= 0 and WorkerThreadPool.is_task_completed(music_task): WorkerThreadPool.wait_for_task_completion(music_task) music_task = -1 music_player.stream = music_box.wav set_music_volume(music_volume) func _exit_tree() -> void: if music_task >= 0: WorkerThreadPool.wait_for_task_completion(music_task) music_task = -1 ## Leise Hintergrundmusik: Arpeggio über vier Akkorde, als Schleife erzeugt. func _music() -> AudioStreamWAV: var rate := 22050 var chords := [[220.0, 261.63, 329.63], [174.61, 220.0, 261.63], [130.81, 164.81, 196.0], [196.0, 246.94, 293.66]] var note_len := 0.25 var pattern := [0, 1, 2, 1, 0, 2, 1, 2] var total := chords.size() * pattern.size() * note_len var n := int(total * rate) var data := PackedByteArray() data.resize(n * 2) for i in n: var t := float(i) / rate var step := int(t / note_len) var chord: Array = chords[(step / pattern.size()) % chords.size()] var f: float = chord[pattern[step % pattern.size()]] var lt := t - step * note_len var env := minf(1.0, lt * 30.0) * maxf(0.0, 1.0 - lt / note_len) var v := sin(TAU * f * t) * 0.6 + sin(TAU * f * 2.0 * t) * 0.15 + sin(TAU * chord[0] * 0.5 * t) * 0.25 data.encode_s16(i * 2, int(clampf(v * env * 0.25, -1.0, 1.0) * 32767)) var wav := AudioStreamWAV.new() wav.format = AudioStreamWAV.FORMAT_16_BITS wav.mix_rate = rate wav.stereo = false wav.data = data wav.loop_mode = AudioStreamWAV.LOOP_FORWARD wav.loop_begin = 0 wav.loop_end = n return wav func set_music_volume(v: float) -> void: music_volume = clampf(v, 0.0, 1.0) if music_volume <= 0.0 or muted: music_player.stop() else: music_player.volume_db = linear_to_db(music_volume * 0.6) if not music_player.playing and music_player.stream != null: music_player.play() func set_volume(v: float) -> void: volume = clampf(v, 0.0, 1.0) func play(kind: String) -> void: if volume <= 0.0 or muted or not streams.has(kind): return for p in players: if not p.playing: p.stream = streams[kind] p.volume_db = linear_to_db(volume) p.play() return func _tone(freq: float, dur: float, shape: String, freq_end: float = -1.0) -> AudioStreamWAV: var rate := 22050 var n := int(dur * rate) var data := PackedByteArray() data.resize(n * 2) for i in n: var t := float(i) / rate var f := freq if freq_end < 0 else lerpf(freq, freq_end, t / dur) var env := 1.0 - t / dur var v := sin(TAU * f * t) if shape == "square": v = 1.0 if v >= 0 else -1.0 data.encode_s16(i * 2, int(clampf(v * env * 0.4, -1.0, 1.0) * 32767)) var wav := AudioStreamWAV.new() wav.format = AudioStreamWAV.FORMAT_16_BITS wav.mix_rate = rate wav.stereo = false wav.data = data return wav