Audio Service
A lightweight, extensible audio management addon for Godot (C#). It gives you a single autoload — AudioHost — that manages buses for you, so you stop wiring up AudioStreamPlayer nodes by hand and start calling PlaySfx() / PlayMusic() instead.
AudioHost.Instance.PlaySfx(ClickSound);
AudioHost.Instance.PlaySfx3D(ExplosionSound, worldPosition);
AudioHost.Instance.PlayMusic(GameplayTheme, fadeDuration: 1.5f);
Why
Godot gives you AudioStreamPlayer, AudioStreamPlayer2D, and AudioStreamPlayer3D as raw building blocks. Audio Service adds the layer most games end up hand-rolling on top of them:
- Pooling — one-shot sounds (SFX) reuse a pool of players per bus instead of spawning/freeing nodes constantly.
- Crossfading — streamed audio (Music) alternates between two channels and tweens volume automatically.
- Bus-driven config — behavior (pooled vs. streamed) is attached to a Godot audio bus, not scattered across scenes.
Installation
- Copy
addons/audio_serviceinto your project'saddons/folder. - Enable Audio Service under Project → Project Settings → Plugins.
- Make sure the bus names you intend to use (
SFX,Music, or any custom ones) exist in Project → Audio Bus Layout.
The plugin registers an AudioHost autoload for you — no manual autoload setup needed.
Quick start
// One-shot SFX
AudioHost.Instance.PlaySfx(ClickSound);
// Positional SFX (2D / 3D)
AudioHost.Instance.PlaySfx2D(ImpactSound, worldPosition2D);
AudioHost.Instance.PlaySfx3D(ImpactSound, worldPosition3D);
// Per-call tuning: quieter, slightly pitched up, with variance so repeats don't sound identical
AudioHost.Instance.PlaySfx(ClickSound,
volumeDb: -4f,
pitchScale: 1.1f,
pitchVariance: 0.05f
));
// Music, crossfaded automatically between two channels
AudioHost.Instance.PlayMusic(GameplayTheme, fadeDuration: 1.5f);
AudioHost.Instance.StopMusic(fadeDuration: 3f);
By default, two buses are registered for you:
| Bus | Mode | Meaning |
|---|---|---|
SFX |
Pooled | Fire-and-forget one-shots, players are recycled |
Music |
Streamed | Long-running tracks, crossfaded via two alternating channels |
Custom buses
You aren't limited to SFX and Music. Register any bus your project's audio layout defines:
AudioHost.Instance.RegisterPooledBus("UI", capacity: 8);
AudioHost.Instance.RegisterStreamedBus("Ambience");
// Pooled: fetch the handler and play directly
var uiBus = AudioHost.Instance.GetPooledBus("UI");
uiBus?.Play(UiClickSound, new AudioOptions(VolumeDb: -3f));
// Streamed: use the generic PlayStream/StopStream overloads
AudioHost.Instance.PlayStream("Ambience", AmbienceLoop, fadeDuration: 4f);
The bus name must already exist in your project's Audio Bus Layout — registration is skipped with a console warning otherwise.
See examples/ for complete, runnable scenes covering SFX, positional audio, music, and custom buses.
Designed to be extended
Audio Service is deliberately built as a small set of composable, overridable pieces rather than a closed API. Reach past the convenience methods whenever you need to:
- Subclass the bus handlers.
PooledBusHandlerandStreamedBusHandlerare unsealed withvirtualentry points (Play/Play2D/Play3D/PlayStream/StopStream), so you can override playback behavior wholesale — e.g. add a concurrency cap, layer in analytics, or change how a bus responds toPlay. - Override just the pooling strategy.
PooledBusHandlerexposesprotected virtual Acquire<TNode>andRelease<TNode>methods, separate fromPlay. OverrideReleasealone to change eviction policy (e.g. priority-based culling instead of a hard capacity cutoff) without touching anything else. - Register your own subclass.
AudioHost.RegisterPooledBus<T>(busName, handler)andRegisterStreamedBus<T>(busName, handler)accept a handler instance you construct yourself, and the matching genericGetPooledBus<T>/GetStreamedBus<T>return it back fully typed — no casting required. - Add new player types. Pooling logic is written once, generically, against
IAudioPlayerAdapter<TNode>— implement that interface for a new node type and the sameCommit<TNode, TAdapter>pipeline can pool it.Player1DAdapter,Player2DAdapter, andPlayer3DAdapterare just the three adapters shipped out of the box. - Add your own high-level API.
PlaySfx,PlaySfx2D,PlaySfx3D,PlayMusic, andStopMusicare plain C# extension methods onAudioHostinAudioServiceExtensions.cs— not special-cased members. Add your own extension methods the same way (e.g.PlayVoiceLine,DuckMusic) instead of modifyingAudioHostitself. - Control randomness. Each
PooledBusHandlertakes an optionalRandomNumberGeneratorat construction (or reseed later viaSeedRng(ulong)), so pitch-variance rolls can be made deterministic — useful for replays or tests. - Bus volume/mute helpers.
SetBusVolume,GetBusVolume, andSetBusMuteonAudioHostwork off linear volume (0–1) and wrap Godot'sAudioServerfor you, but you can always drop toAudioServerdirectly for anything more advanced.
// Example: a pooled bus that never exceeds N concurrent players of the *same* stream
public class LimitedPooledBusHandler : PooledBusHandler
{
public LimitedPooledBusHandler(Node owner, StringName busName, int capacity, RandomNumberGenerator rng = null)
: base(owner, busName, capacity, rng) { }
// override Play / Acquire / Release here to add the limiting behavior
}
AudioHost.Instance.RegisterPooledBus("SFX", new LimitedPooledBusHandler(AudioHost.Instance, "SFX", capacity: 16));
var handler = AudioHost.Instance.GetPooledBus<LimitedPooledBusHandler>("SFX"); // typed, no cast
Changelog for version v1.1.0
No changelog provided for this version.
Reviews
Try adding screenshots of the editor to the presentation, because otherwise it might draw too much attention—like a screenshot of the code used by the extension in the Godot editor (Translated by DeepL)
Amazing ! I like how it's simple and customizable at the same time. I would appreciate if you could add presets for most common cases.
Login to write a review.