extends Node ## AudioManager Singleton ## Zentrale Audio-Analyse für alle Lichteffekte ## Verhindert mehrfache Spectrum-Analyzer-Aufrufe pro Frame # Frequenzbereiche const BASS_RANGE = Vector2(20, 250) const MID_RANGE = Vector2(250, 2000) const HIGH_RANGE = Vector2(2000, 20000) # Interner State var audio_player: AudioStreamPlayer3D var audio_spectrum: AudioEffectSpectrumAnalyzerInstance var is_setup: bool = false # Cache für Magnitude-Werte (wird einmal pro Frame berechnet) var _magnitude_cache: Array[float] = [0.0, 0.0, 0.0, 0.0] # [Bass, Mid, High, All] var _last_frame: int = -1 func _ready(): print("AudioManager: Singleton initialisiert") ## Setup mit AudioStreamPlayer3D func setup(player: AudioStreamPlayer3D) -> bool: if is_setup and audio_player == player: return true # Bereits korrekt eingerichtet audio_player = player if not audio_player: push_error("AudioManager: Kein AudioStreamPlayer3D übergeben!") return false # Setup Spectrum Analyzer var bus_index = AudioServer.get_bus_index(audio_player.bus) var has_spectrum = false # Prüfe, ob bereits ein Spectrum Analyzer vorhanden ist for i in range(AudioServer.get_bus_effect_count(bus_index)): if AudioServer.get_bus_effect(bus_index, i) is AudioEffectSpectrumAnalyzer: has_spectrum = true break # Falls nicht, füge einen hinzu if not has_spectrum: var spectrum = AudioEffectSpectrumAnalyzer.new() AudioServer.add_bus_effect(bus_index, spectrum) print("AudioManager: Spectrum Analyzer hinzugefügt zu Bus '%s'" % audio_player.bus) # Hole die Instanz for i in range(AudioServer.get_bus_effect_count(bus_index)): var effect = AudioServer.get_bus_effect(bus_index, i) if effect is AudioEffectSpectrumAnalyzer: audio_spectrum = AudioServer.get_bus_effect_instance(bus_index, i) break if not audio_spectrum: push_error("AudioManager: Konnte Spectrum Analyzer Instanz nicht holen!") return false is_setup = true print("AudioManager: Erfolgreich eingerichtet mit Bus '%s'" % audio_player.bus) return true ## Prüft, ob Audio gerade abgespielt wird func is_playing() -> bool: return is_setup and audio_player and audio_player.playing ## Gibt ROHE Magnitude für Frequenzbereich zurück (ohne Verarbeitung) ## freq_range: 0=Bass, 1=Mitten, 2=Höhen, 3=Alle func get_raw_magnitude(freq_range: int) -> float: if not is_setup or not audio_spectrum: return 0.0 _update_cache_if_needed() return _magnitude_cache[clamp(freq_range, 0, 3)] ## Optional: Gibt Magnitude mit Standard-Verarbeitung zurück func get_magnitude(freq_range: int, sensitivity: float = 1.0) -> float: var raw = get_raw_magnitude(freq_range) return clamp(raw * sensitivity, 0.0, 1.0) ## Interner Cache-Update (nur einmal pro Frame) func _update_cache_if_needed(): var current_frame = Engine.get_process_frames() # Bereits in diesem Frame berechnet? if _last_frame == current_frame: return _last_frame = current_frame # Berechne alle Frequenzbereiche if not audio_player.playing: _magnitude_cache = [0.0, 0.0, 0.0, 0.0] return _magnitude_cache[0] = _get_magnitude_for_range(BASS_RANGE) # Bass _magnitude_cache[1] = _get_magnitude_for_range(MID_RANGE) # Mitten _magnitude_cache[2] = _get_magnitude_for_range(HIGH_RANGE) # Höhen _magnitude_cache[3] = (_magnitude_cache[0] + _magnitude_cache[1] + _magnitude_cache[2]) / 3.0 # Alle ## Berechnet Magnitude für einen Frequenzbereich func _get_magnitude_for_range(freq_range: Vector2) -> float: if not audio_spectrum: return 0.0 var from_hz = freq_range.x var to_hz = freq_range.y var magnitude_sum = 0.0 var sample_count = 0 # Sample alle 100Hz im Bereich for hz in range(int(from_hz), int(to_hz), 100): var magnitude = audio_spectrum.get_magnitude_for_frequency_range(hz, hz + 100) magnitude_sum += magnitude.length() sample_count += 1 if sample_count > 0: return magnitude_sum / sample_count return 0.0 ## Debug-Info func get_debug_info() -> Dictionary: return { "is_setup": is_setup, "is_playing": is_playing(), "bass": _magnitude_cache[0], "mid": _magnitude_cache[1], "high": _magnitude_cache[2], "all": _magnitude_cache[3], "frame": _last_frame }