Skip to content

What's New in v0.9.6-beta

Release Date: 2026 Target Framework: .NET 10

v0.9.6-beta is a complete audio overhaul and introduces post-processing effects to the GPU renderer.


Highlights

Complete Audio Overhaul

The audio system has been redesigned from the ground up. The old channel-based IAudioService has been replaced with a track-based IAudioPlayer that provides fine-grained per-track control, audio buses for group operations, crossfading, and spatial audio.

Track-Based Playback

PlaySound now returns a nint track handle you can use to control a specific instance:

// Play a sound and get a handle
nint track = Audio.PlaySound(_explosion, volume: 0.8f, pan: -0.3f, pitch: 1.1f);

// Control it later
Audio.SetTrackVolume(track, 0.5f);
Audio.SetTrackPan(track, 0.5f);
Audio.SetTrackPitch(track, 0.75f);
Audio.PauseTrack(track);
Audio.ResumeTrack(track);
Audio.StopTrack(track);

// Check if still playing
if (Audio.IsTrackAlive(track)) { ... }

Audio Buses

Tag tracks to control groups of sounds together:

// Tag at play time (atomic -- can't be missed by PauseBus)
nint track = Audio.PlaySound(_footstep, bus: "sfx");
nint uiTrack = Audio.PlaySound(_click, bus: "ui");

// Or tag after
Audio.TagTrack(track, "ambient");

// Group operations
Audio.SetBusVolume("sfx", 0.6f);
Audio.PauseBus("ui");
Audio.ResumeBus("ui");
Audio.StopBus("ambient", fadeOutSeconds: 1.0f);

float vol = Audio.GetBusVolume("sfx");
bool paused = Audio.IsBusPaused("ui");

Music with Crossfade and Loop Points

// Play music with a loop intro point
Audio.PlayMusic(_battleTheme, loops: -1, loopStartMs: 4000);

// Crossfade to a new track over 2 seconds
Audio.CrossfadeMusic(_bossTheme, duration: 2.0f);

// Stop with fade-out
Audio.StopMusic(fadeDuration: 1.5f);

// Real-time music control
Audio.PauseMusic();
Audio.ResumeMusic();
Audio.SeekMusic(positionMs: 30000);
Audio.SetMusicTrackVolume(0.8f);
Audio.SetMusicPitch(1.05f);

// Playback info
double pos = Audio.MusicPositionMs;
double dur = Audio.MusicDurationMs;
bool fading = Audio.IsMusicFadingOut;

Volume Hierarchy

Audio.MasterVolume = 0.9f;   // Master gain
Audio.MusicVolume  = 0.7f;   // Applied to music tracks
Audio.SoundVolume  = 1.0f;   // Applied to sound effect tracks
// Final gain = MasterVolume × MusicVolume/SoundVolume × BusVolume × TrackVolume

Spatial Audio via ECS

Attach SoundEffectSourceComponent for automatic distance-based volume and stereo panning:

entity.AddComponent<SoundEffectSourceComponent>(s =>
{
    s.SoundEffect         = _footstepSound;
    s.EnableSpatialAudio  = true;
    s.MinDistance         = 50f;   // Full volume within this radius
    s.MaxDistance         = 500f;  // Silent beyond this radius
});

Track Limits and Priority

Audio.PlaySound(_sound, priority: 5); // Higher priority survives track eviction
int active = Audio.ActiveSoundTrackCount;
int max    = Audio.MaxSoundTracks;

Post-Processing Effects

The GPU renderer now supports a post-processing pass. Add effects to the renderer pipeline from your scene:

protected override void OnLoadAsync(IEntityWorld world)
{
    Renderer.PostProcessing.Add(new ChromaticAberrationEffect { Intensity = 0.005f });
    Renderer.PostProcessing.Add(new VignetteEffect { Radius = 0.75f, Softness = 0.4f });
}

Breaking Changes

Area Before (v0.9.5) After (v0.9.6)
Sound playback PlaySound() returned channel int Returns track nint handle
Stop by channel StopChannel(int) StopTrack(nint)
Pause by channel PauseChannel(int) PauseTrack(nint)
Channel check IsChannelPlaying(int) IsTrackAlive(nint)
Group control No concept of buses TagTrack / bus operations

Migration

Replace all StopChannel, PauseChannel, and IsChannelPlaying call sites with the track-handle equivalents. Store the nint returned by PlaySound wherever you previously stored the channel int.

// Before
int channel = Audio.PlaySound(_sound);
Audio.StopChannel(channel);

// After
nint track = Audio.PlaySound(_sound);
Audio.StopTrack(track);