extends Node # ═══════════════════════════════════════════════════════════════════════════ # "STELLAR DRIFT" · Normal-OST (A-Moll, 140 BPM, Am→G→F→Em) # "CRITICAL MASS" · Boss-OST #1 WRAITH (A-Moll, 175 BPM, Super Hexagon) # "EVENT HORIZON" · Boss-OST #2 LEVIATHAN (E-Phrygisch, 140 BPM, Cosmic Dread) # # Voices in einem AudioStreamPlayer auf dem "Music"-Bus: # Voice 0 · Melodie · Triangle (normal/boss2), Square (boss1) # Voice 1 · Arpeggio · Square (normal/boss1), Triangle leise (boss2) # Voice 2 · Bass · Sawtooth (boss2: +detuneter 2. Layer für Schwebung) # Voice 3 · Schlagzeug · Hi-Hat+Kick+Snare (normal/boss1) · # Kick+Floor-Tom+Mid-Tom+Reverse-Whoosh (boss2) # Voice 4 · BH-Bass · Sine Sub-Bass, proximity-driven, beat-pulsed # ═══════════════════════════════════════════════════════════════════════════ @export var volume_db: float = -8.0 @export var loop: bool = true @export var autoplay: bool = true const SAMPLE_RATE := 44100 const BUFFER_SIZE := 0.1 # ── Normale Musik ───────────────────────────────────────────────────────── const BPM := 140.0 const BEAT := 60.0 / BPM # ── Boss-Musik #1 (WRAITH) ──────────────────────────────────────────────── const BOSS_BPM := 175.0 const BOSS_BEAT := 60.0 / BOSS_BPM # ── Boss-Musik #2 (LEVIATHAN) ───────────────────────────────────────────── const BOSS2_BPM := 140.0 const BOSS2_BEAT := 60.0 / BOSS2_BPM # ── Frequenztabelle ─────────────────────────────────────────────────────── const FREQ := { "R" : 0.0, "D2" : 73.42, "E2" : 82.41, "F2" : 87.31, "A2" : 110.00, "C3" : 130.81, "D3" : 146.83, "E3" : 164.81, "F3" : 174.61, "G3" : 196.00, "A3" : 220.00, "B3" : 246.94, "C4" : 261.63, "D4" : 293.66, "E4" : 329.63, "F4" : 349.23, "G4" : 392.00, "A4" : 440.00, "B4" : 493.88, "C5" : 523.25, "D5" : 587.33, "E5" : 659.25, "F5" : 698.46, "G5" : 783.99, "A5" : 880.00, "B5" : 987.77, "C6" :1046.50, "E6" :1318.51, } # ── Normal-Akkorde (chord: 0=Am 1=G 2=F 3=Em) ───────────────────────── const ARP := { 0: ["A4","C5","E5","C5"], 1: ["G4","B4","D5","B4"], 2: ["F4","A4","C5","A4"], 3: ["E4","G4","B4","G4"], } const BASS := { 0:"A3", 1:"G3", 2:"F3", 3:"E3" } # ── Boss-Akkorde (chord: 0=Am 1=Em) ───────────────────────────────────── const BOSS_ARP := { 0: ["A4","E5","A5","E5"], # Am — Power-Arpeggio 1: ["E4","B4","E5","B4"], # Em — Moll-Arpeggio } const BOSS_BASS := { 0:"A2", 1:"E2" } # tiefer Sub-Bass # ── Boss2-Akkorde (chord: 0=E 1=F 2=Dm) — E-Phrygisch ────────────────── const BOSS2_ARP := { 0: ["E4","B4","E5","G4"], # E (phrygisch, mit kleiner Terz G) 1: ["F4","C5","F5","A4"], # F — der phrygische II-Akkord (F→E ist der Killer) 2: ["D4","A4","D5","F4"], # Dm — dunkler iv-Akkord } const BOSS2_BASS := { 0:"E2", 1:"F2", 2:"D2" } # Drone-Bass Grundtöne # ═══════════════════════════════════════════════════════════════════════════ # NORMAL-SCORE — "STELLAR DRIFT" # Format: [mel_note, beats, chord_idx, section] # section 0=Intro 1=Build 2=Main # ═══════════════════════════════════════════════════════════════════════════ const SCORE: Array = [ # ═══ INTRO ═══ ["A4", 2.0, 0, 0], ["E5", 2.0, 0, 0], ["C5", 2.0, 0, 0], ["A4", 2.0, 0, 0], ["E5", 1.0, 0, 0], ["A5", 1.0, 0, 0], ["E5", 1.0, 0, 0], ["C5", 1.0, 0, 0], ["A4", 4.0, 0, 0], # ═══ BUILD ═══ ["A4",0.5,0,1],["C5",0.25,0,1],["E5",0.25,0,1], ["A5",0.5,0,1],["G5",0.25,0,1],["E5",0.25,0,1], ["D5",0.5,0,1],["C5",0.25,0,1],["A4",0.25,0,1], ["C5",0.5,0,1],["R", 0.5, 0,1], ["C5",0.5,0,1],["E5",0.25,0,1],["A5",0.25,0,1], ["C6",0.5,0,1],["A5",0.25,0,1],["E5",0.25,0,1], ["G5",0.5,0,1],["E5",0.25,0,1],["C5",0.25,0,1],["A4",1.0,0,1], ["G4",0.5,1,1],["B4",0.25,1,1],["D5",0.25,1,1], ["G5",0.5,1,1],["D5",0.25,1,1],["B4",0.25,1,1], ["G4",0.5,1,1],["R", 0.25,1,1],["B4",0.25,1,1], ["D5",0.5,1,1],["G5",0.5,1,1], ["F4",0.5,2,1],["A4",0.25,2,1],["C5",0.25,2,1], ["F5",0.5,2,1],["C5",0.25,2,1],["A4",0.25,2,1], ["F5",0.5,2,1],["A5",0.25,2,1],["C6",0.25,2,1], ["A5",0.5,2,1],["F5",0.5, 2,1], ["E5",0.5,3,1],["G5",0.25,3,1],["B5",0.25,3,1], ["E6",0.5,3,1],["B5",0.25,3,1],["G5",0.25,3,1], ["A5",0.5,3,1],["G5",0.25,3,1],["E5",0.25,3,1],["B4",1.0,3,1], # ═══ MAIN ═══ ["A4",0.5,0,2],["C5",0.25,0,2],["E5",0.25,0,2], ["A5",0.5,0,2],["G5",0.25,0,2],["E5",0.25,0,2], ["D5",0.5,0,2],["C5",0.25,0,2],["A4",0.25,0,2], ["C5",0.5,0,2],["R", 0.5, 0,2], ["C5",0.5,0,2],["E5",0.25,0,2],["A5",0.25,0,2], ["C6",0.5,0,2],["A5",0.25,0,2],["E5",0.25,0,2], ["G5",0.5,0,2],["E5",0.25,0,2],["C5",0.25,0,2],["A4",1.0,0,2], ["G4",0.5,1,2],["B4",0.25,1,2],["D5",0.25,1,2], ["G5",0.5,1,2],["D5",0.25,1,2],["B4",0.25,1,2], ["G4",0.5,1,2],["R", 0.25,1,2],["B4",0.25,1,2], ["D5",0.5,1,2],["G5",0.5,1,2], ["F4",0.5,2,2],["A4",0.25,2,2],["C5",0.25,2,2], ["F5",0.5,2,2],["C5",0.25,2,2],["A4",0.25,2,2], ["F5",0.5,2,2],["A5",0.25,2,2],["C6",0.25,2,2], ["A5",0.5,2,2],["F5",0.5, 2,2], ["E5",0.5,3,2],["G5",0.25,3,2],["B5",0.25,3,2], ["E6",0.5,3,2],["B5",0.25,3,2],["G5",0.25,3,2], ["A5",0.5,3,2],["G5",0.25,3,2],["E5",0.25,3,2],["B4",1.0,3,2], ] const LOOP_START := 9 # Loop ab BUILD (Intro überspringen) # ═══════════════════════════════════════════════════════════════════════════ # BOSS-SCORE — "CRITICAL MASS" # Format: [mel_note, beats, chord_idx, section] # chord 0=Am 1=Em · section=3 (volle Intensität immer) # BPM=175, Stil: Super Hexagon — stakkato, getrieben, 4-Bar-Loop # ═══════════════════════════════════════════════════════════════════════════ const BOSS_SCORE: Array = [ # ══ Bar 1 (Am) — Aufstiegs-Stabs ══ ["E5", 0.25, 0, 3], ["R", 0.25, 0, 3], ["G5", 0.25, 0, 3], ["A5", 0.25, 0, 3], ["E5", 0.5, 0, 3], ["R", 0.25, 0, 3], ["C5", 0.25, 0, 3], ["E5", 0.25, 0, 3], ["R", 0.25, 0, 3], ["A4", 0.5, 0, 3], ["R", 1.0, 0, 3], # ══ Bar 2 (Am) — Synkopierter Lauf ══ ["G5", 0.25, 0, 3], ["A5", 0.25, 0, 3], ["G5", 0.25, 0, 3], ["E5", 0.25, 0, 3], ["C5", 0.25, 0, 3], ["E5", 0.25, 0, 3], ["G5", 0.5, 0, 3], ["A5", 0.25, 0, 3], ["R", 0.25, 0, 3], ["G5", 0.25, 0, 3], ["E5", 0.25, 0, 3], ["C5", 0.5, 0, 3], ["R", 0.5, 0, 3], # ══ Bar 3 (Em) — Dunkle Dringlichkeit ══ ["E5", 0.25, 1, 3], ["R", 0.25, 1, 3], ["B4", 0.25, 1, 3], ["R", 0.25, 1, 3], ["G5", 0.25, 1, 3], ["E5", 0.25, 1, 3], ["D5", 0.5, 1, 3], ["B4", 0.25, 1, 3], ["R", 0.25, 1, 3], ["G5", 0.5, 1, 3], ["E5", 0.25, 1, 3], ["D5", 0.25, 1, 3], ["B4", 0.5, 1, 3], # ══ Bar 4 (Em) — Klimax ══ ["G5", 0.25, 1, 3], ["B5", 0.25, 1, 3], ["A5", 0.25, 1, 3], ["G5", 0.25, 1, 3], ["E5", 0.5, 1, 3], ["R", 0.5, 1, 3], ["D5", 0.25, 1, 3], ["R", 0.25, 1, 3], ["B4", 0.25, 1, 3], ["R", 0.25, 1, 3], ["E5", 1.0, 1, 3], ] const BOSS_LOOP_START := 0 # Voller Loop, kein Intro # ═══════════════════════════════════════════════════════════════════════════ # BOSS2-SCORE — "EVENT HORIZON" # Format: [mel_note, beats, chord_idx, section] # chord 0=E 1=F 2=Dm · section=3 (Bass+Drums immer aktiv) # BPM=140 · E-Phrygisch · 16-Bar-Struktur: 8 Bars Phase 1 + 8 Bars Phase 2 # Melodie: lange, sparsame Töne in Phase 1 — verdichtet sich in Phase 2 # ═══════════════════════════════════════════════════════════════════════════ const BOSS2_SCORE: Array = [ # ══ PHASE 1 — Bars 1–8 (Index 0–14) ═══════════════════════════════════ # Bar 1 (E) — langer Tonika-Drone ["E5", 3.0, 0, 3], ["R", 1.0, 0, 3], # Bar 2 (E) — leichte Bewegung ["G5", 2.0, 0, 3], ["E5", 2.0, 0, 3], # Bar 3 (F) — phrygische Spannung bricht ein ["F5", 3.0, 1, 3], ["C5", 1.0, 1, 3], # Bar 4 (F) — hält die Spannung ["F5", 2.0, 1, 3], ["A5", 2.0, 1, 3], # Bar 5 (Dm) — dunkler Abstieg ["D5", 2.0, 2, 3], ["F5", 2.0, 2, 3], # Bar 6 (Dm) — Weite, Atem ["A5", 3.0, 2, 3], ["R", 1.0, 2, 3], # Bar 7 (E) — Rückkehr ["B4", 2.0, 0, 3], ["E5", 2.0, 0, 3], # Bar 8 (E) — ganze Note als Übergang ["E5", 4.0, 0, 3], # ══ PHASE 2 — Bars 9–16 (Index 15–47) ═════════════════════════════════ # Verdichtung: mehr Achtel, aufsteigende Linien, Klimax-Oktav # Bar 9 (E) — Phrygisch-Lauf aufwärts ["E5", 1.0, 0, 3], ["G5", 1.0, 0, 3], ["B5", 1.0, 0, 3], ["G5", 1.0, 0, 3], # Bar 10 (E) — Stakkato-Antwort ["E5", 0.5, 0, 3], ["R", 0.5, 0, 3], ["B4", 1.0, 0, 3], ["G5", 1.0, 0, 3], ["E5", 1.0, 0, 3], # Bar 11 (F) — hochklettern, der F→E-Halbton beißt ["F5", 1.0, 1, 3], ["A5", 1.0, 1, 3], ["C6", 1.0, 1, 3], ["A5", 1.0, 1, 3], # Bar 12 (F) — absteigende Variation ["F5", 0.5, 1, 3], ["R", 0.5, 1, 3], ["C5", 1.0, 1, 3], ["A5", 1.0, 1, 3], ["F5", 1.0, 1, 3], # Bar 13 (Dm) — Dm-Arpeggio-Lick ["D5", 1.0, 2, 3], ["F5", 1.0, 2, 3], ["A5", 1.0, 2, 3], ["F5", 1.0, 2, 3], # Bar 14 (Dm) — Dreiklangs-Dichte ["D5", 0.5, 2, 3], ["A4", 0.5, 2, 3], ["D5", 1.0, 2, 3], ["F5", 1.0, 2, 3], ["A5", 1.0, 2, 3], # Bar 15 (E) — finaler Aufstieg ["B4", 1.0, 0, 3], ["E5", 1.0, 0, 3], ["G5", 1.0, 0, 3], ["B5", 1.0, 0, 3], # Bar 16 (E) — Klimax-Oktavsprung E5→E4 ["E5", 2.0, 0, 3], ["E4", 2.0, 0, 3], ] const BOSS2_LOOP_START := 0 # bei Phase 1 loopt der ganze Track const BOSS2_PHASE2_OFFSET := 15 # Index wo Bar 9 (Phase 2) beginnt # ── Player-Zustand ──────────────────────────────────────────────────────── var _player: AudioStreamPlayer var _generator: AudioStreamGenerator var _playback: AudioStreamGeneratorPlayback var _playing := false # Modus: 0=normal, 1=WRAITH (CRITICAL MASS), 2=LEVIATHAN (EVENT HORIZON) var _boss_mode: int = 0 var _cur_beat: float = BEAT # Phase-2-Umschaltung (nur boss_mode=2) var _phase2_active: bool = false var _pending_phase2_jump: bool = false # Melody var _mel_idx := 0 var _mel_pos := 0 var _mel_samples := 0 var _mel_freq := 0.0 var _mel_phase := 0.0 # Arpeggio (16tel-Noten) var _arp_step := 0 var _arp_pos := 0 var _arp_samples := 0 var _arp_freq := 0.0 var _arp_phase := 0.0 # Bass var _bass_chord := -1 var _bass_freq := 0.0 var _bass_phase := 0.0 # Bass-Zweit-Oszillator (nur boss_mode=2, detunet für Schwebung) var _bass2_phase := 0.0 # Voice 4: BH Proximity Sub-Bass var _v4_freq: float = 0.0 var _v4_freq_cur: float = 0.0 var _v4_gain: float = 0.0 var _v4_phase: float = 0.0 var _v4_is_smbh: bool = false # Music event stingers (short pitched overlays) var _stingers: Array = [] # Globaler Zustand var _section := 0 var _chord := 0 var _sample_g := 0 # ── Initialisierung ─────────────────────────────────────────────────────── func _ready() -> void: var bus_idx := AudioServer.get_bus_index("Music") if bus_idx == -1: AudioServer.add_bus() bus_idx = AudioServer.get_bus_count() - 1 AudioServer.set_bus_name(bus_idx, "Music") AudioServer.set_bus_send(bus_idx, "Master") AudioServer.set_bus_volume_db(bus_idx, linear_to_db(Settings.music_volume) if Settings.music_volume > 0.0 else -80.0) _generator = AudioStreamGenerator.new() _generator.mix_rate = SAMPLE_RATE _generator.buffer_length = BUFFER_SIZE _player = AudioStreamPlayer.new() _player.stream = _generator _player.volume_db = volume_db _player.bus = "Music" add_child(_player) if autoplay: play() func play() -> void: _boss_mode = 0 _cur_beat = BEAT _phase2_active = false; _pending_phase2_jump = false _mel_idx = 0; _mel_pos = 0; _mel_phase = 0.0 _arp_step = 0; _arp_pos = 0; _arp_phase = 0.0 _bass_chord = -1; _bass_phase = 0.0; _bass2_phase = 0.0 _sample_g = 0; _section = 0; _chord = 0 _v4_gain = 0.0; _v4_freq = 0.0; _v4_freq_cur = 0.0; _v4_phase = 0.0; _v4_is_smbh = false; _stingers.clear() _playing = true _player.play() _playback = _player.get_stream_playback() _load_mel() func play_boss() -> void: _boss_mode = 1 _cur_beat = BOSS_BEAT _phase2_active = false; _pending_phase2_jump = false _mel_idx = 0; _mel_pos = 0; _mel_phase = 0.0 _arp_step = 0; _arp_pos = 0; _arp_phase = 0.0 _bass_chord = -1; _bass_phase = 0.0; _bass2_phase = 0.0 _sample_g = 0; _section = 3; _chord = 0 _v4_gain = 0.0; _v4_freq = 0.0; _v4_freq_cur = 0.0; _v4_phase = 0.0; _v4_is_smbh = false; _stingers.clear() if not _playing: _playing = true _player.play() _playback = _player.get_stream_playback() _load_mel() func play_boss_leviathan(phase: int = 1) -> void: _boss_mode = 2 _cur_beat = BOSS2_BEAT _phase2_active = (phase >= 2) _pending_phase2_jump = false _mel_idx = BOSS2_PHASE2_OFFSET if _phase2_active else 0 _mel_pos = 0; _mel_phase = 0.0 _arp_step = 0; _arp_pos = 0; _arp_phase = 0.0 _bass_chord = -1; _bass_phase = 0.0; _bass2_phase = 0.0 _sample_g = 0; _section = 3; _chord = 0 _v4_gain = 0.0; _v4_freq = 0.0; _v4_freq_cur = 0.0; _v4_phase = 0.0; _v4_is_smbh = false; _stingers.clear() if not _playing: _playing = true _player.play() _playback = _player.get_stream_playback() _load_mel() # Wird vom game_world.gd aufgerufen, wenn LEVIATHAN Phase 2 erreicht (< 50% HP). # Setzt nur ein Flag — der Sprung geschieht beim nächsten Note-Wechsel, # damit der Rhythmus nicht bricht. func enter_phase2() -> void: if _boss_mode == 2 and not _phase2_active: _pending_phase2_jump = true func play_normal() -> void: if _boss_mode == 0: return # already playing normal — don't restart _boss_mode = 0 _cur_beat = BEAT _phase2_active = false; _pending_phase2_jump = false _mel_idx = LOOP_START; _mel_pos = 0; _mel_phase = 0.0 _arp_step = 0; _arp_pos = 0; _arp_phase = 0.0 _bass_chord = -1; _bass_phase = 0.0; _bass2_phase = 0.0 _sample_g = 0 _v4_gain = 0.0; _v4_freq = 0.0; _v4_freq_cur = 0.0; _v4_phase = 0.0; _v4_is_smbh = false; _stingers.clear() _load_mel() func stop_music() -> void: _playing = false _player.stop() func set_music_volume(linear: float) -> void: var idx := AudioServer.get_bus_index("Music") if idx != -1: AudioServer.set_bus_volume_db(idx, linear_to_db(linear) if linear > 0.0 else -80.0) func set_muted(muted: bool) -> void: var idx := AudioServer.get_bus_index("Music") if idx != -1: AudioServer.set_bus_mute(idx, muted) func set_bh_proximity(proximity: float, size_factor: float, is_smbh: bool) -> void: _v4_is_smbh = is_smbh var sf := minf(size_factor * 0.12, 0.55) _v4_gain = clamp(proximity * sf, 0.0, 0.50) _v4_freq = _get_v4_freq() func _get_v4_freq() -> float: var root_freq: float match _boss_mode: 2: root_freq = FREQ.get(BOSS2_BASS.get(_chord, "E2"), FREQ["E2"]) 1: root_freq = FREQ.get(BOSS_BASS.get(_chord, "A2"), FREQ["A2"]) _: root_freq = FREQ.get(BASS.get(_chord, "A3"), FREQ["A3"]) var base := maxf(30.0, root_freq * 0.25) if _v4_is_smbh: return maxf(22.0, base * 0.5) return base func play_music_event(event_name: String) -> void: var root_hz: float = _bass_freq if _bass_freq > 0.0 else FREQ["A3"] match event_name: "bh_pulse": _stingers.append({"type": "sine", "phase": 0.0, "freq": root_hz * 0.25, "freq_end": root_hz * 0.125, "gain": 0.20, "pos": 0, "total": int(1.2 * SAMPLE_RATE), "attack": int(0.05 * SAMPLE_RATE)}) "neutron_beam": _stingers.append({"type": "noise", "phase": 0.0, "freq": 0.0, "freq_end": 0.0, "gain": 0.14, "pos": 0, "total": int(0.07 * SAMPLE_RATE)}) _stingers.append({"type": "sine", "phase": 0.0, "freq": root_hz * 8.0, "freq_end": root_hz * 4.0, "gain": 0.10, "pos": 0, "total": int(0.10 * SAMPLE_RATE)}) "white_hole_eject": var arp_table: Dictionary match _boss_mode: 2: arp_table = BOSS2_ARP 1: arp_table = BOSS_ARP _: arp_table = ARP var chord_notes: Array = arp_table.get(_chord, ["A4", "C5", "E5"]) for i in 3: var note_str: String = chord_notes[i] if i < chord_notes.size() else "A4" var note_hz: float = FREQ.get(note_str, root_hz * 2.0) _stingers.append({"type": "triangle", "phase": 0.0, "freq": note_hz, "freq_end": note_hz, "gain": 0.10 - i * 0.025, "pos": -int(i * 0.09 * SAMPLE_RATE), "total": int(0.28 * SAMPLE_RATE)}) "quasar_jet": var base_hz := root_hz * 2.0 var scale_mult := [1.0, 1.122, 1.260, 1.498] for i in 4: var note_hz: float = base_hz * float(scale_mult[i]) _stingers.append({"type": "sine", "phase": 0.0, "freq": note_hz, "freq_end": note_hz * 1.04, "gain": 0.08 + i * 0.01, "pos": -int(i * 0.11 * SAMPLE_RATE), "total": int(0.18 * SAMPLE_RATE)}) "galaxy_consumed": var swell_freqs: Array[float] = [root_hz * 0.5, root_hz * 0.75, root_hz] for i in 3: _stingers.append({"type": "sine", "phase": 0.0, "freq": swell_freqs[i], "freq_end": swell_freqs[i], "gain": 0.18 - i * 0.04, "pos": 0, "total": int(1.6 * SAMPLE_RATE), "attack": int(0.5 * SAMPLE_RATE)}) "planet_captured": _stingers.append({"type": "noise", "phase": 0.0, "freq": 0.0, "freq_end": 0.0, "gain": 0.20, "pos": 0, "total": int(0.10 * SAMPLE_RATE)}) _stingers.append({"type": "sine", "phase": 0.0, "freq": root_hz * 2.0, "freq_end": root_hz * 0.5, "gain": 0.18, "pos": 0, "total": int(0.45 * SAMPLE_RATE)}) "comet_pass": _stingers.append({"type": "sine", "phase": 0.0, "freq": root_hz * 14.0, "freq_end": root_hz * 7.0, "gain": 0.06, "pos": 0, "total": int(0.16 * SAMPLE_RATE)}) # ── Hauptschleife ───────────────────────────────────────────────────────── func _process(_delta: float) -> void: if _playing: _fill_buffer() func _fill_buffer() -> void: var frames := _playback.get_frames_available() for _i in frames: # ── Melodie-Note weiterschalten ────────────────────────────── if _mel_pos >= _mel_samples: # Phase-2-Sprung bei LEVIATHAN: smooth am Note-Ende if _pending_phase2_jump and _boss_mode == 2: _mel_idx = BOSS2_PHASE2_OFFSET _phase2_active = true _pending_phase2_jump = false else: _mel_idx += 1 var score: Array var loop_start: int match _boss_mode: 2: score = BOSS2_SCORE # Phase-2 loopt nur die zweite Hälfte (endlose Eskalation) loop_start = BOSS2_PHASE2_OFFSET if _phase2_active else BOSS2_LOOP_START 1: score = BOSS_SCORE loop_start = BOSS_LOOP_START _: score = SCORE loop_start = LOOP_START if _mel_idx >= score.size(): if loop: _mel_idx = loop_start else: _playing = false _player.stop() return _mel_pos = 0 _load_mel() # ── Arp-Note weiterschalten (16tel) ────────────────────────── if _arp_pos >= _arp_samples: _arp_step = (_arp_step + 1) % 4 _arp_pos = 0 _load_arp() # ── Samples mischen ────────────────────────────────────────── var s := 0.0 # Voice 0: Melodie # Normal: Triangle warm · Boss1 (WRAITH): Square scharf · Boss2 (LEVIATHAN): Triangle mit langem Attack if _mel_freq > 0.0: var attack: int var release: int match _boss_mode: 2: attack = 800; release = 1500 1: attack = 100; release = 500 _: attack = 200; release = 1200 var env := _env(_mel_pos, _mel_samples, attack, release) var ph := fmod(_mel_phase, 1.0) if _boss_mode == 1: # Square-Wave: schärfer, beißender Klang var sq := 1.0 if ph < 0.5 else -1.0 s += sq * env * 0.26 else: # Triangle-Wave: warm, vibraphone-artig (normal & boss2) var tri := 1.0 - absf(4.0 * ph - 2.0) var mel_gain := 0.32 if _boss_mode == 2 else 0.38 s += tri * env * mel_gain # Phase-2-Oktav-Layer: zusätzliche Triangle eine Oktave höher if _boss_mode == 2 and _phase2_active: var ph2 := fmod(_mel_phase * 2.0, 1.0) var tri2 := 1.0 - absf(4.0 * ph2 - 2.0) s += tri2 * env * 0.14 _mel_phase += _mel_freq / SAMPLE_RATE # Voice 1: Arpeggio # Normal/Boss1: Square 50% · Boss2: leise Triangle (Sternengeflimmer) if _arp_freq > 0.0: var env := _env(_arp_pos, _arp_samples, 40, 400) var ph := fmod(_arp_phase, 1.0) if _boss_mode == 2: var tri := 1.0 - absf(4.0 * ph - 2.0) var gain := 0.09 if _phase2_active else 0.06 s += tri * env * gain else: var sq := 1.0 if ph < 0.5 else -1.0 var gain: float if _section == 0: gain = 0.05 elif _boss_mode == 1: gain = 0.20 else: gain = 0.11 s += sq * env * gain _arp_phase += _arp_freq / SAMPLE_RATE # Voice 2: Bass — Sawtooth (ab section 1) # Boss2: zusätzlicher 2. Saw-Oszillator, detunet für Drone-Schwebung if _section >= 1 and _bass_freq > 0.0: var ph := fmod(_bass_phase, 1.0) var bass_gain: float if _boss_mode == 2: bass_gain = 0.22 elif _boss_mode == 1: bass_gain = 0.30 elif _section == 1: bass_gain = 0.15 else: bass_gain = 0.25 s += (2.0 * ph - 1.0) * bass_gain _bass_phase += _bass_freq / SAMPLE_RATE if _boss_mode == 2: # 2. Oszillator detunet (~3‰ = leichte Schwebung bei ~82 Hz: ~0.25 Hz) var ph2 := fmod(_bass2_phase, 1.0) s += (2.0 * ph2 - 1.0) * 0.22 _bass2_phase += (_bass_freq * 1.003) / SAMPLE_RATE # Voice 3: Schlagzeug (ab section 2 oder Boss-Modus) if _section >= 2: if _boss_mode == 2: # ── LEVIATHAN: Tom-lastig, keine Hi-Hats ───────────────── # Kick auf Beat 1 jedes Bars (in Phase 2 zusätzlich auf Beat 3) var bar_period := int(4.0 * _cur_beat * SAMPLE_RATE) var bar_pos := _sample_g % bar_period var kick_len := 900 if bar_pos < kick_len: var kick_env := 1.0 - float(bar_pos) / float(kick_len) var kick_f := 1.0 - float(bar_pos) / float(kick_len) s += sin(kick_f * kick_f * 55.0) * kick_env * 0.26 if _phase2_active: var beat3_offset := int(2.0 * _cur_beat * SAMPLE_RATE) var kick2_pos := (_sample_g + bar_period - beat3_offset) % bar_period if kick2_pos < kick_len: var kick2_env := 1.0 - float(kick2_pos) / float(kick_len) var kick2_f := 1.0 - float(kick2_pos) / float(kick_len) s += sin(kick2_f * kick2_f * 55.0) * kick2_env * 0.22 # Floor-Tom auf Beats 1 & 3 (period = 2 beats, tiefer Sinus mit Pitch-Drop) var tom_period := int(2.0 * _cur_beat * SAMPLE_RATE) var tom_pos := _sample_g % tom_period var tom_len := 2200 if tom_pos < tom_len: var tom_t := float(tom_pos) / float(tom_len) var tom_env := 1.0 - tom_t # Pitch-Envelope: startet bei 130 Hz, fällt auf 70 Hz var tom_freq := 130.0 - 60.0 * tom_t var tom_ph := float(tom_pos) / float(SAMPLE_RATE) * tom_freq s += sin(tom_ph * TAU) * tom_env * 0.18 # Mid-Tom-Fill: Beat 4 jedes 4. Bars (= Ende jeder 4-Bar-Phrase) var phrase_period := int(16.0 * _cur_beat * SAMPLE_RATE) var phrase_pos := _sample_g % phrase_period var mid_tom_start := int(15.0 * _cur_beat * SAMPLE_RATE) var mid_tom_rel := phrase_pos - mid_tom_start if mid_tom_rel >= 0: # 4 schnelle Mid-Tom-Hits auf dem letzten Beat var hit_len: int = int(_cur_beat * SAMPLE_RATE * 0.25) @warning_ignore("integer_division") var hit_idx: int = mid_tom_rel / hit_len if hit_idx < 4: var hit_pos := mid_tom_rel % hit_len if hit_pos < 1400: var mt_t := float(hit_pos) / 1400.0 var mt_env := 1.0 - mt_t var mt_freq := 220.0 - 80.0 * mt_t var mt_ph := float(hit_pos) / float(SAMPLE_RATE) * mt_freq s += sin(mt_ph * TAU) * mt_env * 0.14 # Reverse-Whoosh alle 8 Bars: aufsteigende Noise-Envelope als Übergang var whoosh_period := int(32.0 * _cur_beat * SAMPLE_RATE) var whoosh_pos := _sample_g % whoosh_period var whoosh_start := whoosh_period - int(2.0 * _cur_beat * SAMPLE_RATE) if whoosh_pos >= whoosh_start: var w_t := float(whoosh_pos - whoosh_start) / float(whoosh_period - whoosh_start) var w_env := w_t * w_t # aufsteigend s += randf_range(-1.0, 1.0) * w_env * 0.09 else: # ── Normal & WRAITH: Hi-Hats + Kick + (Boss1) Snare ────── var hat_beat_frac := 0.25 if _boss_mode == 1 else 0.5 var hat_period := int(hat_beat_frac * _cur_beat * SAMPLE_RATE) var hat_pos := _sample_g % hat_period var hat_len := 200 if _boss_mode == 1 else 600 if hat_pos < hat_len: var hat_env := 1.0 - float(hat_pos) / float(hat_len) var hat_vol := 0.08 if _boss_mode == 1 else 0.07 s += randf_range(-1.0, 1.0) * hat_env * hat_vol var kick_period := int(_cur_beat * SAMPLE_RATE) var kick_pos := _sample_g % kick_period var kick_len := 750 if _boss_mode == 1 else 800 if kick_pos < kick_len: var kick_env := 1.0 - float(kick_pos) / float(kick_len) var kick_f := 1.0 - float(kick_pos) / float(kick_len) var kick_vol := 0.22 if _boss_mode == 1 else 0.18 s += sin(kick_f * kick_f * 60.0) * kick_env * kick_vol if _boss_mode == 1: var snare_period := int(2.0 * _cur_beat * SAMPLE_RATE) var snare_offset := int(1.0 * _cur_beat * SAMPLE_RATE) var snare_pos := (_sample_g + snare_offset) % snare_period if snare_pos < 340: var snare_env := 1.0 - float(snare_pos) / 340.0 s += randf_range(-1.0, 1.0) * snare_env * 0.13 # Voice 4: BH Proximity Sub-Bass — sine, beat-pulsed, frequency glide var v4_glide := 0.9997 _v4_freq_cur = _v4_freq_cur * v4_glide + _v4_freq * (1.0 - v4_glide) if _v4_gain > 0.004 and _v4_freq_cur > 0.0: var beat_samples_v4 := int(_cur_beat * SAMPLE_RATE) if beat_samples_v4 > 0: var beat_pos_v4 := _sample_g % beat_samples_v4 var beat_t_v4 := float(beat_pos_v4) / float(beat_samples_v4) var beat_pulse_v4: float if beat_t_v4 < 0.06: beat_pulse_v4 = beat_t_v4 / 0.06 else: beat_pulse_v4 = 1.0 - (beat_t_v4 - 0.06) / 0.94 * 0.55 _v4_phase += _v4_freq_cur / SAMPLE_RATE s += sin(_v4_phase * TAU) * _v4_gain * (0.45 + 0.55 * beat_pulse_v4) # Stingers: short pitched musical overlays for celestial events for st in _stingers: st["pos"] = int(st["pos"]) + 1 var sp: int = int(st["pos"]) if sp <= 0: continue var stotal: int = int(st["total"]) if sp > stotal: continue var t_st := float(sp) / float(stotal) var atk: int = int(st.get("attack", int(0.008 * SAMPLE_RATE))) var env_st: float if sp < atk: env_st = float(sp) / float(atk) else: var remaining := stotal - atk if remaining > 0: env_st = maxf(0.0, 1.0 - float(sp - atk) / float(remaining)) else: env_st = 0.0 var sf_st: float = lerp(float(st["freq"]), float(st["freq_end"]), t_st) match st["type"]: "sine": st["phase"] = fmod(float(st["phase"]) + sf_st / SAMPLE_RATE, 1.0) s += sin(float(st["phase"]) * TAU) * env_st * float(st["gain"]) "triangle": st["phase"] = fmod(float(st["phase"]) + sf_st / SAMPLE_RATE, 1.0) s += (1.0 - absf(4.0 * float(st["phase"]) - 2.0)) * env_st * float(st["gain"]) "noise": s += randf_range(-1.0, 1.0) * env_st * float(st["gain"]) _stingers = _stingers.filter(func(st): return int(st["pos"]) <= int(st["total"])) _playback.push_frame(Vector2(s, s)) _mel_pos += 1 _arp_pos += 1 _sample_g += 1 # ── Note laden ──────────────────────────────────────────────────────────── func _load_mel() -> void: var score: Array match _boss_mode: 2: score = BOSS2_SCORE 1: score = BOSS_SCORE _: score = SCORE var e: Array = score[_mel_idx] _mel_freq = FREQ.get(e[0], 0.0) _mel_samples = int(float(e[1]) * _cur_beat * SAMPLE_RATE) _mel_phase = 0.0 _section = e[3] if e[2] != _chord: _chord = e[2] _arp_step = 0 _arp_pos = 0 _load_arp() if e[2] != _bass_chord: _bass_chord = e[2] var bass_dict: Dictionary match _boss_mode: 2: bass_dict = BOSS2_BASS 1: bass_dict = BOSS_BASS _: bass_dict = BASS _bass_freq = FREQ.get(bass_dict[_bass_chord], 0.0) _bass_phase = 0.0 _bass2_phase = 0.0 _v4_freq = _get_v4_freq() func _load_arp() -> void: var arp_dict: Dictionary var arp_step_count: int = 4 match _boss_mode: 2: arp_dict = BOSS2_ARP # Arp-Geschwindigkeit: Phase 1 = 8tel, Phase 2 = 16tel (verdichtet) var arp_frac := 0.25 if _phase2_active else 0.5 _arp_samples = int(arp_frac * _cur_beat * SAMPLE_RATE) 1: arp_dict = BOSS_ARP _arp_samples = int(0.25 * _cur_beat * SAMPLE_RATE) _: arp_dict = ARP _arp_samples = int(0.25 * _cur_beat * SAMPLE_RATE) _arp_freq = FREQ.get(arp_dict[_chord][_arp_step % arp_step_count], 0.0) _arp_phase = 0.0 # ── Hüllkurve ───────────────────────────────────────────────────────────── func _env(pos: int, total: int, attack: int, release: int) -> float: if pos < attack: return float(pos) / maxf(attack, 1.0) elif pos > total - release: return float(total - pos) / maxf(release, 1.0) return 1.0