Audio¶
Learn how to add sound effects, background music, and immersive spatial audio to your Brine2D games.
Quick Start¶
using Brine2D.Audio;
public class AudioScene : Scene
{
private readonly IAudioService _audio;
private ISoundEffect? _jumpSound;
private IMusic? _backgroundMusic;
// Inject IAudioService
public AudioScene(IAudioService audio)
{
_audio = audio;
}
protected override async Task OnLoadAsync(CancellationToken ct)
{
// Load sound effect
_jumpSound = await _audio.LoadSoundAsync("assets/jump.wav", ct);
// Load music
_backgroundMusic = await _audio.LoadMusicAsync("assets/music.ogg", ct);
// Play music (looping)
_audio.PlayMusic(_backgroundMusic, loops: -1);
}
protected override void OnUpdate(GameTime gameTime)
{
if (_input.IsKeyPressed(Key.Space))
{
// Play sound effect
_audio.PlaySound(_jumpSound);
}
}
}
Topics¶
Getting Started¶
| Guide | Description | Difficulty |
|---|---|---|
| Getting Started | Audio basics and setup | ⭐ Beginner |
| Sound Effects | Play sounds (jump, shoot, etc.) | ⭐ Beginner |
| Music Playback | Background music and looping | ⭐ Beginner |
Advanced¶
| Guide | Description | Difficulty |
|---|---|---|
| Spatial Audio | 2D positional audio with distance/panning | ⭐⭐⭐ Advanced |
Key Concepts¶
Track-Based Audio (v0.9.0+)¶
Brine2D uses a track-based audio system for precise control:
// Play sound and get track handle
nint track = _audio.PlaySoundWithTrack(_shootSound, volume: 0.8f);
// Control specific track
_audio.UpdateTrackSpatialAudio(track, volume: 0.6f, pan: 0.5f);
_audio.StopTrack(track);
// Track finished event
_audio.OnTrackStopped += (stoppedTrack) =>
{
Logger.LogInformation("Track {Track} finished", stoppedTrack);
};
Benefits: - Precise control over individual sounds - Update volume/pan in real-time - Track lifecycle events
Audio Architecture¶
graph TB
AS["IAudioService<br/>(Singleton)"]
SE["Sound Effects<br/>(Short, frequent)"]
M["Music<br/>(Long, looping)"]
SA["Spatial Audio<br/>(Position-based)"]
AS --> SE
AS --> M
AS --> SA
SE --> T1["Track 1"]
SE --> T2["Track 2"]
SE --> T3["Track 3"]
M --> MT["Music Track"]
SA --> ST1["Spatial Track 1"]
SA --> ST2["Spatial Track 2"]
style AS fill:#2d5016,stroke:#4ec9b0,stroke-width:3px,color:#fff
style SE fill:#1e3a5f,stroke:#569cd6,stroke-width:2px,color:#fff
style M fill:#4a2d4a,stroke:#c586c0,stroke-width:2px,color:#fff
style SA fill:#3d3d2a,stroke:#dcdcaa,stroke-width:2px,color:#fff
Common Tasks¶
Play Sound Effect¶
private readonly IAudioService _audio;
private ISoundEffect? _explosionSound;
protected override async Task OnLoadAsync(CancellationToken ct)
{
_explosionSound = await _audio.LoadSoundAsync("assets/explosion.wav", ct);
}
protected override void OnUpdate(GameTime gameTime)
{
if (enemyKilled)
{
// Simple playback
_audio.PlaySound(_explosionSound);
// With volume control
_audio.PlaySound(_explosionSound, volume: 0.7f);
}
}
Play Background Music¶
private IMusic? _music;
protected override async Task OnLoadAsync(CancellationToken ct)
{
_music = await _audio.LoadMusicAsync("assets/background.ogg", ct);
// Play looping music
_audio.PlayMusic(_music, loops: -1); // -1 = infinite loop
}
protected override void OnUpdate(GameTime gameTime)
{
// Pause/resume
if (_input.IsKeyPressed(Key.P))
{
if (_audio.IsMusicPlaying())
{
_audio.PauseMusic();
}
else
{
_audio.ResumeMusic();
}
}
}
Spatial Audio (2D Positional)¶
// Create audio listener (player)
var player = World.CreateEntity("Player");
var listener = player.AddComponent<AudioListenerComponent>();
// Create spatial audio source (enemy)
var enemy = World.CreateEntity("Enemy");
var audioSource = enemy.AddComponent<AudioSourceComponent>();
audioSource.SoundEffect = _enemySound;
audioSource.EnableSpatialAudio = true;
// Configure distance attenuation
audioSource.MinDistance = 100f; // Full volume within 100 pixels
audioSource.MaxDistance = 500f; // Silent beyond 500 pixels
audioSource.RolloffFactor = 1.0f; // Linear falloff
// Configure stereo panning
audioSource.SpatialBlend = 1.0f; // Full stereo (0.0 = mono)
// Play
audioSource.Loop = true;
audioSource.PlayOnEnable = true;
Volume Control¶
// Master volume (affects all audio)
_audio.SetMasterVolume(0.8f); // 80%
// Sound effects volume
_audio.SetSoundVolume(0.6f); // 60%
// Music volume
_audio.SetMusicVolume(0.5f); // 50%
// Per-sound volume
_audio.PlaySound(_jumpSound, volume: 0.9f);
// Update track volume in real-time
nint track = _audio.PlaySoundWithTrack(_engineSound);
_audio.UpdateTrackSpatialAudio(track, volume: 0.7f, pan: 0.0f);
Supported Formats¶
| Format | Sound Effects | Music | Recommended For |
|---|---|---|---|
| WAV | ✅ Yes | ✅ Yes | Sound effects (uncompressed) |
| OGG | ✅ Yes | ✅ Yes | Music (compressed, high quality) |
| MP3 | ✅ Yes | ✅ Yes | Music (compressed, smaller file) |
| FLAC | ✅ Yes | ✅ Yes | Music (lossless) |
Recommendations: - Sound effects: WAV (fast loading, no decompression overhead) - Music: OGG (good compression, no licensing issues unlike MP3)
Best Practices¶
✅ DO¶
- Load sounds in OnLoadAsync() - Keep OnUpdate() fast
- Use appropriate formats - WAV for SFX, OGG for music
- Control volume - Don't max out everything
- Unload sounds - Free memory when scene ends
- Use tracks for control - Get track handle for precise control
// ✅ Good pattern
protected override async Task OnLoadAsync(CancellationToken ct)
{
_sounds = await LoadSoundsAsync(ct);
_music = await _audio.LoadMusicAsync("music.ogg", ct);
}
protected override void OnUpdate(GameTime gameTime)
{
// Quick playback
_audio.PlaySound(_jumpSound);
}
protected override Task OnUnloadAsync(CancellationToken ct)
{
// Clean up
_audio.StopAllSounds();
_audio.StopMusic();
return Task.CompletedTask;
}
❌ DON'T¶
- Don't load sounds in OnUpdate() - Causes lag
- Don't play too many sounds - Overwhelms player
- Don't forget to stop music - Plays between scenes
- Don't use MP3 for SFX - Decoding overhead
- Don't max out volume - Causes clipping/distortion
// ❌ Bad pattern
protected override void OnUpdate(GameTime gameTime)
{
// Don't load in update!
var sound = await _audio.LoadSoundAsync("jump.wav"); // NO!
_audio.PlaySound(sound);
// Don't play too many at once
for (int i = 0; i < 100; i++)
{
_audio.PlaySound(_explosionSound); // Overwhelming!
}
}
Performance Tips¶
Limit Concurrent Sounds¶
private int _activeSoundCount = 0;
private const int MaxConcurrentSounds = 16;
protected override void OnUpdate(GameTime gameTime)
{
if (shouldPlaySound && _activeSoundCount < MaxConcurrentSounds)
{
nint track = _audio.PlaySoundWithTrack(_sound);
_activeSoundCount++;
_audio.OnTrackStopped += (stoppedTrack) =>
{
if (stoppedTrack == track)
{
_activeSoundCount--;
}
};
}
}
Preload Common Sounds¶
// Load frequently used sounds at startup
protected override async Task OnLoadAsync(CancellationToken ct)
{
// Preload common sounds
_commonSounds = new Dictionary<string, ISoundEffect>
{
["jump"] = await _audio.LoadSoundAsync("jump.wav", ct),
["shoot"] = await _audio.LoadSoundAsync("shoot.wav", ct),
["hit"] = await _audio.LoadSoundAsync("hit.wav", ct),
};
}
Troubleshooting¶
No Sound Playing¶
Symptom: Audio calls succeed but nothing plays
Solutions:
- Check master volume:
_audio.SetMasterVolume(1.0f); // Make sure not muted
- Verify sound loaded:
if (_jumpSound == null)
{
Logger.LogWarning("Jump sound not loaded!");
}
- Check file exists:
- Verify path:
assets/jump.wav - Check file is copied to output
Music Continues Between Scenes¶
Symptom: Music from previous scene still playing
Solution: Stop music in OnUnloadAsync()
protected override Task OnUnloadAsync(CancellationToken ct)
{
_audio.StopMusic();
return Task.CompletedTask;
}
Spatial Audio Not Working¶
Symptom: Spatial audio sounds like regular audio
Solutions:
- Check EnableSpatialAudio is true:
audioSource.EnableSpatialAudio = true; // Must be true!
- Verify listener exists:
var listener = World.GetEntitiesWithComponent<AudioListenerComponent>();
if (!listener.Any())
{
Logger.LogWarning("No audio listener found!");
}
- Check distances:
// Make sure source is within range
float distance = Vector2.Distance(source.Position, listener.Position);
if (distance > audioSource.MaxDistance)
{
Logger.LogDebug("Source too far from listener");
}
Related Topics¶
- Getting Started - Audio basics
- Sound Effects - Play sounds
- Music Playback - Background music
- Spatial Audio - Positional audio
- What's New v0.9.0 - Track-based audio changes
Ready to add sound? Start with Getting Started!