vrp-gruppe-sechs/disco-escape-roomv-5/script/audio_manager.gd

135 lines
4.1 KiB
GDScript

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
}