PixelBullet  0.0.1
A C++ game engine
Loading...
Searching...
No Matches
Audio Runtime

The engine audio runtime owns reusable playback mechanics only. Product code chooses sources, priorities, admission classes, gain groups, spatial policy, and when to request playback. The runtime does not know Arena cue IDs, campaign/boss/perk meaning, captions, music intensity, or accessibility policy.

Public compatibility

Audio::play_sound, stop, pause/resume, volume, looping, listener transforms, spatial settings, playback position, and stop_all remain supported. Existing decoded one-shots now use the bounded backend cache and voice pool; explicit stream requests retain bounded file streaming. Existing callers do not need to adopt resource handles or gain groups.

The extended API adds stable AudioResourceHandle preload/release/query, cached one-shot playback, explicit streamed playback, generic gain groups, master gain, pitch, and copied AudioDiagnosticsSnapshot. Backend objects and miniaudio pointers never cross the public boundary.

Fixed ownership

The committed reusable limits are:

Resource Bound
resource handles 64
distinct decoded resources 32
decoded PCM 32 MiB
one-shot voices 32
general-access voices 24
generic reserved voices 8
streams 4
gain groups 8
queued commands 512

Decoded resources retain one normalized path and immutable float PCM. Each voice owns fixed ma_audio_buffer/ma_sound state with an independent cursor; repeated cached playback does not reopen or decode the source. Release removes the public resource handle immediately while active voices retain the decoded data until their safe completion. Failed decodes are cached until all references release, preventing automatic retry loops.

The command path is a fixed typed ring protected by one small mutex. Producer submission is explicit, the audio thread drains source order, close drains accepted commands, and full/closed submission increments copied rejection diagnostics. This is intentionally not a lock-free queue.

Admission, groups, and streams

General requests can occupy only the first 24 voice slots. Reserved-class requests may use any free slot. When no compatible slot is free, only a strictly lower-priority voice is eligible for replacement; the oldest admission serial followed by slot index makes the choice stable. Equal priority does not steal, and general requests cannot consume or steal reserved slots.

Effective voice gain is caller gain multiplied by generic group gain and master gain. Group changes update active and future voices without re-decoding or restarting them. Streams have four fixed source slots and use playback handles as their source identity; looping, gain, pitch, spatial parameters, pause/resume, completion, and stop remain generic backend behavior.

Diagnostics and deferrals

The copied version-1 snapshot exposes backend availability, command current and high-water counts, rejections, resource/cache counts and bytes, active/free and high-water voices, reserved use, admissions/rejections/steals, stale handles, streams, gain groups, decode/start failures, teardown failures, bounded resource/playback/group observations, and the last typed error. It contains no paths, backend pointers, or mutable authority.

PixelBullet-owned command and voice storage is fixed after construction; decoded PCM grows only during explicit preload and is reclaimed after explicit release and final voice completion. Miniaudio may perform its own bounded internal work, so the engine makes no unsupported claim about miniaudio-internal zero allocation.

Arena cue routing, product media, bus mapping/persistence, music policy, captions, and device-loss reconstruction are not implemented here. Device-loss recovery remains Phase 7 work.