HammeriteAudioManager
Audio. Inherits RefCounted
One map's sound: plays sounds through its audio graph, sets the reverb, and tells listeners.
Manages all acoustic propagation logic, sound playback, and audio stream management. Kept in the view's runtime data (HammeriteMap3D.get_runtime_data()) by the HammeriteAudio autoload, which makes a new one whenever the map or its variant changes; reach it with HammeriteAudio.get_manager(node) rather than holding on to one. Responsibilities: - Sound submission API for entities - AudioStreamPlayer3D pool management (dynamic allocation) - AudioStream caching with resource names as keys - All propagation calculations (zone detection, portal finding, path queries) - Position && volume updates each frame - Reverb application based on listener position - Multi-source sound support
Properties
| Type | Name | Default |
|---|---|---|
HammeriteMap | map | null |
HammeriteAudioSoundTypes | sound_types | null |
HammeriteAudioMixSettings | mix | <unknown> |
HammeriteAudioHearing | hearing | null |
Methods
| Returns | Method |
|---|---|
void | initialize(p_audio_graph: HammeriteAudioNodeGraph, p_map: HammeriteMap, p_map_3d: HammeriteMap3D) |
int | create_positional_sound(spec: HammeriteAudioSoundSpec) |
int | create_environmental_sound(spec: HammeriteAudioSoundSpec) |
bool | play_sound(handle: int) |
bool | is_sound_playing(handle: int) |
bool | stop_sound(handle: int) |
bool | delete_sound(handle: int) |
bool | set_sound_volume_db(handle: int, volume_db: float) |
bool | set_sound_position(handle: int, position: Vector3) |
bool | set_sound_looping(handle: int, loop: bool) |
bool | set_sound_attenuation_scale(handle: int, attenuation_scale: float) |
bool | set_sound_propagation(handle: int, propagate: bool) |
bool | set_sound_direction(handle: int, direction: Vector3) |
bool | set_portal_occlusion(portal_name: String, occlusion: float) |
void | press_ear_to_portal(portal_name: String, gain_db: float) |
void | take_ear_from_portal() |
void | update(delta: float) |
void | update_for_listener(delta: float, listener_position: Vector3) |
void | clear_cache() |
void | cleanup() |
Constants
BUS_NAME
const BUS_NAME = "Hammerite Audio"
The bus every sound the graph carries plays on, made at runtime and sent to Master. A game that sends it somewhere of its own - a world bus its settings turn down - names it by this.
Property descriptions
map
var map: HammeriteMap = null
Reference to the map (for raycasting)
sound_types
var sound_types: HammeriteAudioSoundTypes = null
The game's sound-type table, pushed in by HammeriteAudioRuntime. Held here rather than read from a global so the manager is a self-contained object that can be built and tested on its own.
mix
var mix: HammeriteAudioMixSettings = <unknown>
How this map's sound is mixed; see HammeriteAudioMixSettings. Pushed in by HammeriteAudioRuntime, as sound_types is, and never null: assigning null puts the defaults back, so nothing here has to check before reading it.
hearing
var hearing: HammeriteAudioHearing = null
Who can hear what in this map, and what they heard. The gameplay half of audio: a guard's HammeriteAudioReceiver3D registers itself here, and a noise is reported here. Kept apart from the mixing because what a game's AI touches should be small, and because the two answer different questions - this one who was in earshot, the mixer how sound gets there at all.
Method descriptions
initialize()
func initialize(p_audio_graph: HammeriteAudioNodeGraph, p_map: HammeriteMap, p_map_3d: HammeriteMap3D) -> void
Initialize the manager with required references
create_positional_sound()
func create_positional_sound(spec: HammeriteAudioSoundSpec) -> int
Create a handle for the positional sound spec describes, or -1 when its HammeriteAudioSoundSpec.sound_file does not resolve in HammeriteAudioRuntime.sounds. Nothing plays until play_sound(). The spec is read here and not held, so one spec can create any number of sounds and changing it afterwards changes none of them. What the sound is doing now is changed by handle, through set_sound_volume_db() and the rest. The handle stays valid until delete_sound().
create_environmental_sound()
func create_environmental_sound(spec: HammeriteAudioSoundSpec) -> int
Create a handle for the environmental sound (rain, wind) spec describes, or -1 when no player is free or its HammeriteAudioSoundSpec.sound_file does not resolve. Holds a pooled player from now until delete_sound(). HammeriteAudioSoundSpec.max_distance, HammeriteAudioSoundSpec.attenuation_scale, HammeriteAudioSoundSpec.propagate and HammeriteAudioSoundSpec.free_when_finished mean nothing here: weather is placed by the way out to it rather than routed, and it plays until something stops it.
play_sound()
func play_sound(handle: int) -> bool
Start the sound behind handle, placed and levelled for the listener before it starts. False with an error for an unknown handle, and false when the player pool is exhausted. A non-looping positional sound already out of earshot is not played at all, so is_sound_playing() stays false for it.
is_sound_playing()
func is_sound_playing(handle: int) -> bool
Is the sound behind handle currently playing? A one-shot played out of hearing is, for as long as it would have been heard.
stop_sound()
func stop_sound(handle: int) -> bool
Stop the sound behind handle. The handle stays valid and can be played again. Returns whether anything was there to stop. Quiet about a handle it does not know, deliberately and unlike play_sound(): making sure a sound is not playing is safe to ask twice, and a level being torn down asks exactly that of handles whose manager has already gone.
delete_sound()
func delete_sound(handle: int) -> bool
Delete the sound behind handle, giving its player back to the pool. The handle is invalid afterwards. Returns whether anything was there to delete. Quiet about a handle it does not know, for the reason stop_sound() is.
set_sound_volume_db()
func set_sound_volume_db(handle: int, volume_db: float) -> bool
Set the mix level of the sound behind handle, in dB, before anything the distance and the route cost it. False for an unknown handle, with an error.
set_sound_position()
func set_sound_position(handle: int, position: Vector3) -> bool
Move the positional sound behind handle to position, and route it again from there. False for an unknown handle or an environmental sound, which is placed by the way out to it rather than by a position of its own.
set_sound_looping()
func set_sound_looping(handle: int, loop: bool) -> bool
Make the sound behind handle loop until stopped, or play once. False for an unknown handle.
set_sound_attenuation_scale()
func set_sound_attenuation_scale(handle: int, attenuation_scale: float) -> bool
Scale what the distance costs the positional sound behind handle: below 1 carries further, above 1 falls off sooner. False for an unknown handle.
set_sound_propagation()
func set_sound_propagation(handle: int, propagate: bool) -> bool
Route the positional sound behind handle through the audio graph, or play it at its source with distance falloff alone. False for an unknown handle or an environmental sound.
set_sound_direction()
func set_sound_direction(handle: int, direction: Vector3) -> bool
Say which side the environmental sound behind handle comes from; Vector3.ZERO is all round at once. False for an unknown handle or a positional sound, which comes from where it is made.
set_portal_occlusion()
func set_portal_occlusion(portal_name: String, occlusion: float) -> bool
Open or shut the opening named portal_name: 0 is open, 1 costs the opening its authored closed attenuation, and in between is ajar. Returns whether anything carries that name. This is the call a door makes, and the most common thing a game asks of acoustics at runtime. The name is authored in the Audio Portals tool and lives on the face it is punched from, so it survives a recompile; hammerite-nav reads the same name.
press_ear_to_portal()
func press_ear_to_portal(portal_name: String, gain_db: float) -> void
Press the ear this manager plays for to the opening named portal_name: every sound that comes through it plays gain_db louder, and nothing else changes. Listening at a door. This is what is PLAYED. What a HammeriteAudioReceiver3D hears is its own HammeriteAudioReceiver3D.press_ear_to_portal(), so a player's captions can follow their ear.
take_ear_from_portal()
func take_ear_from_portal() -> void
Undo press_ear_to_portal(): nothing is heard louder through any opening.
update()
func update(delta: float) -> void
Update all active sounds for the current camera's listener, and release finished one-shots. The HammeriteMap3D holding this manager calls it every frame; calling it as well advances everything twice.
update_for_listener()
func update_for_listener(delta: float, listener_position: Vector3) -> void
Update every active sound for a listener at listener_position. Split out of update() so the listener can come from somewhere other than the current camera - a test with no viewport, a second listener in split screen - rather than being reachable only through whatever camera happens to be current. A listener_position that is not finite - Vector3.INF, which is what there being no camera reads as - is no listener at all, and nothing is updated. The origin is a place like any other: a camera standing there hears the level.
clear_cache()
func clear_cache() -> void
Clear path cache
cleanup()
func cleanup() -> void
Stop and delete every sound and free the pooled players. Called when the manager is replaced or its map is cleared; every handle it gave out is invalid afterwards.