import { AudioListener as ThreeAudioListener } from "three"; import { Application } from "../engine/engine_application.js"; import { Camera } from "./Camera.js"; import { Behaviour, GameObject } from "./Component.js"; /** * AudioListener represents a listener that can hear audio sources in the scene. * This component creates and manages a Three.js {@link three#AudioListener}, automatically connecting it * to the main camera or a Camera in the parent hierarchy. * @category Multimedia * @group Components */ export class AudioListener extends Behaviour { /** * Gets the existing Three.js {@link three#AudioListener} instance or creates a new one if it doesn't exist. * This listener is responsible for capturing audio in the 3D scene. * @returns The {@link three#AudioListener} instance */ get listener(): ThreeAudioListener { if (this._listener == null) this._listener = new ThreeAudioListener(); return this._listener; } private _listener: ThreeAudioListener | null = null; /** * Registers for interaction events and initializes the audio listener * when this component is enabled. * @internal */ onEnable(): void { Application.registerWaitForInteraction(this.onInteraction); this.addListenerIfItExists(); } /** * Cleans up event registrations and removes the audio listener * when this component is disabled. * @internal */ onDisable(): void { Application.unregisterWaitForInteraction(this.onInteraction); this.removeListenerIfItExists(); } private onInteraction = () => { if (this.destroyed) return; const listener = this.listener; if (listener == null) return; this.addListenerIfItExists(); } private addListenerIfItExists() { const listener = this._listener; if (!listener) return; // if the listener is already parented to some object dont change it if (listener?.parent) return; const cam = this.context.mainCameraComponent || GameObject.getComponentInParent(this.gameObject, Camera); if (cam?.threeCamera) { cam.threeCamera.add(listener); } else { this.gameObject.add(listener); } // connect the listeners audio nodes if (!listener.filter) { listener.gain.connect(listener.context.destination); } else { listener.gain.connect(listener.filter); listener.filter.connect(listener.context.destination); } } private removeListenerIfItExists() { const listener = this._listener; if (!listener) return; listener.removeFromParent(); // disconnect the listeners audio nodes if (listener.filter) { listener.filter.disconnect(); } if (listener.gain) { listener.gain.disconnect(); } } }