GraphAudio.Kit
0.6.0
dotnet add package GraphAudio.Kit --version 0.6.0
NuGet\Install-Package GraphAudio.Kit -Version 0.6.0
<PackageReference Include="GraphAudio.Kit" Version="0.6.0" />
<PackageVersion Include="GraphAudio.Kit" Version="0.6.0" />
<PackageReference Include="GraphAudio.Kit" />
paket add GraphAudio.Kit --version 0.6.0
#r "nuget: GraphAudio.Kit, 0.6.0"
#:package GraphAudio.Kit@0.6.0
#addin nuget:?package=GraphAudio.Kit&version=0.6.0
#tool nuget:?package=GraphAudio.Kit&version=0.6.0
GraphAudio.Kit
A high-level, game-focused audio library for .NET
Why GraphAudio.Kit?
- Play sounds in one line after initial setup
- 3D spatial audio that just works.
- Flexible mixing with hierarchical audio buses.
- Effect chains you can build and modify at runtime
- Smart caching so you're not constantly reloading the same audio files
Installation
dotnet add package GraphAudio.Kit
Quick Start
Set up your audio engine once when your game starts:
using GraphAudio.Kit;
using GraphAudio.Kit.DataProviders;
using GraphAudio.Realtime;
// Create the engine that manages all your sounds
var context = new RealtimeAudioContext(
sampleRate: 48000,
channels: 2,
bufferSize: 256
);
var engine = new AudioEngine(context);
// Tell it where to find your audio files
engine.DataProvider = new FileSystemDataProvider("path/to/your/audio/folder");
// Start the audio hardware
context.Start();
// That's it! Now you're ready to play sounds.
Keep the engine alive for the lifetime of your game. When you're done:
engine.Dispose();
Playing Your First Sound
Once you've done the setup above, playing a sound is dead simple:
// One-shot sounds (fire and forget)
engine.PlayOneShot("footstep.ogg");
// PlayOneShot also takes a setup callback, which you can use to set up the sounds properties before it plays and the engine owns it.
That's it! The sound plays, finishes, and cleans itself up automatically.
When You Need More Control
For sounds you want to control (music, ambience , etc.), create a Sound object:
// Load a sound into memory for instant playback
var sound = await engine.CreateBufferedSoundAsync("music.ogg");
// Control it like you'd expect
sound.Play();
sound.Pause();
sound.Stop();
sound.Gain = 0.5f; // Volume (0.0 to 1.0)
sound.IsLooping = true; // Loop it
sound.PlaybackRate = 1.2f; // Speed it up 20%
// Jump to a specific time
sound.Seek(TimeSpan.FromSeconds(30));
Buffered vs Streaming Sounds
Buffered sounds load the entire audio file into memory. They're perfect for short, frequently-played sounds:
var click = await engine.CreateBufferedSoundAsync("click.ogg");
Streaming sounds read audio on-demand. Use these for long files like music.
var bgMusic = await engine.CreateStreamingSoundAsync("music/background.mp3");
bgMusic.IsLooping = true;
bgMusic.Play();
3D Spatial audio
The Simple Way: Position-Based
For detached sounds in your 3D world, just set their position:
var sound = await engine.CreateBufferedSoundAsync(
"engine.ogg",
mixState: SoundMixState.BinoralSpatialized // Enable 3D audio for that sound.
);
sound.Position = new Vector3(10, 0, 5); // World position
sound.IsLooping = true;
sound.Play();
// Update the listener (your player/camera) position
engine.SetListener(
position: playerPosition,
forward: playerForward,
up: Vector3.UnitY
);
The sound will now pan with hrtf. Call engine.Update() in your game loop to keep everything in sync.
Spatial Anchors
If you have entities that play multiple sounds, Instead of manually updating every sound's position, you attach sounds to an anchor and update the anchor with your game entity's position:
// Create an anchor for your car entity
var carAnchor = new SpatialAnchor();
// Attach the engine sound to it
var engineSound = await engine.CreateBufferedSoundAsync(
"car_engine.ogg",
mixState: SoundMixState.BinoralSpatialized
);
engineSound.Anchor = carAnchor;
engineSound.IsLooping = true;
engineSound.Play();
// In your game update loop:
void Update()
{
// Just update the anchor once and all attached sounds follow automatically!
carAnchor.Position = carEntity.Transform.Position;
// Update the engine (you must call this periodically)
engine.Update();
}
You can even use position offsets if the sound source isn't exactly at the entity's center:
engineSound.Anchor = carAnchor;
engineSound.Position = new Vector3(0, -0.5f, 2); // Offset from anchor
Advanced: distance Behavior
Control how sounds fade with distance:
sound.SetDistanceModel(
model: SpatialPannerNode.DistanceModelType.Inverse, // Realistic falloff
refDistance: 1.0f, // Distance where volume starts to decrease
maxDistance: 50.0f, // Distance where volume reaches minimum
rolloffFactor: 1.0f // How quickly volume decreases (higher = faster)
);
Advanced: Directional Sounds
Make sounds emit in a specific direction (like a megaphone or engine exhaust):
sound.Orientation = Vector3.UnitX; // Sound points along X axis
sound.SetCone(
innerAngle: 45f, // Full volume within this angle
outerAngle: 90f, // Transition to outer volume
outerGain: 0.3f // Volume outside the cone (0.0 to 1.0)
);
Advanced: Occlusion & Transmission
Simulate sound passing through walls or obstacles:
// 0.0 = Open Air, 1.0 = Fully Blocked
sound.Occlusion = 1.0f;
// Occlusion must be > 0 for transmission to work.
sound.SetTransmission(
low: 0.9f,
mid: 0.9f,
high: 0.8f
);
Audio Buses
Buses let you group and control the gain and effects on multiple sounds at once. Think a music bus, a UI bus, gameplay bus, etc.
var musicBus = engine.GetBus("music");
// Attach sounds to buses
var bgMusic = await engine.CreateStreamingSoundAsync("music.mp3", bus: musicBus);
// Control the entire bus
musicBus.Gain = 0.5f; // All music plays at 50% volume
music.Muted = true; // Silence all music instantly
// Smooth volume changes
musicBus.Fade(target: 0.2f, duration: 2.0); // Fade to 20% over 2 seconds
Hierarchical Buses
You can create bus hierarchies using forward slashes:
var playerSfx = engine.GetBus("sfx/player");
var enemySfx = engine.GetBus("sfx/enemy");
var uiSfx = engine.GetBus("sfx/ui");
// Adjusting the parent affects all children
var sfxBus = engine.GetBus("sfx");
sfxBus.Gain = 0.5f; // All sfx/* buses now play at 50% of their individual volume
All buses eventually connect to the master bus:
engine.MasterBus.Gain = 0.8f; // Global volume control
Advanced: Effect Chains
Effect chains let you add high-level effects to individual sounds or entire buses. Effects process in the order you add them.
You can use high-level effects like ReverbEffect:
var reverb = new ReverbEffect(engine);
await reverb.SetImpulseResponseAsync("irs/hall.wav");
reverb.Wet.Value = 0.5f;
soundOrBus.Effects.Add(reverb);
Or you can wrap standard GraphAudio nodes using NodeEffect:
var lowpass = new BiQuadFilterNode(engine.Context);
lowpass.Type = BiQuadFilterNode.FilterType.LowPass;
lowpass.Frequency.Value = 800;
sound.Effects.Add(new NodeEffect(engine, lowpass));
The EffectChain takes ownership of the effects. When you remove an effect or dispose the chain, the effects (and their underlying nodes) are automatically disposed.
soundOrBus.Effects.Remove(reverb);
soundOrBus.Effects.Clear();
soundOrBus.Effects.Insert(0, newReverb);
Advanced: creating Custom Effects
You are encouraged to create your own reusable effects by subclassing Effect. An effect is simply a mini-graph with an input and an output. This allows you to encapsulate complex DSP graphs (like a multi-tap delay or a specific filter chain) into a single, easy-to-use package.
To implement a custom effect, you need to:
- Inherit from
Effect: Pass theAudioEngineto the base constructor. - Define Input/Output: Expose the entry and exit nodes of your internal graph via the
InputandOutputproperties. - Manage Resources: Override
OnDisposeto clean up any nodes you created.
Here is an example custom effect:
public class BoostEffect : Effect
{
private readonly GainNode _gain;
public override AudioNode Input => _gain;
public override AudioNode Output => _gain;
public BoostEffect(AudioEngine engine, float amount) : base(engine)
{
_gain = new GainNode(engine.Context);
_gain.Gain.Value = amount;
}
protected override void OnDispose()
{
_gain.Dispose();
}
}
Preloading for Performance
If you know you'll need certain sounds soon, preload them to avoid slowdowns:
// Load multiple sounds in parallel
await engine.PreloadBuffersAsync(new[]
{
"footstep_1.ogg",
"footstep_2.ogg",
"footstep_3.ogg",
"jump.ogg"
});
// Now they'll play instantly when needed
engine.PlayOneShot("footstep_1.ogg", setup: sound => sound.Anchor = playerAnchor); // No load time!
Fade In/Out
Sounds and buses both support smooth volume transitions:
// Fade in when playing
sound.Play(fadeInDuration: 1.5); // Fade in over 1.5 seconds
// Fade out before stopping
await sound.Stop(fadeOutDuration: 2.0); // Fade out over 2 seconds, then stop
// Bus fades affect all sounds on the bus
musicBus.Fade(target: 0.0f, duration: 3.0); // Fade out all music
Advanced: Spatial Blend Controllers
Spatial blend controllers let you customize how sounds transition between 2D and 3D audio based on distance. By default, sounds are fully 3D, but you might want close sounds to feel more "direct":
using GraphAudio.Kit.SpatialBlendControllers;
// Make close sounds more direct (easier to hear), far sounds fully spatial
var controller = new LinearSpatialBlendController(
minDistance: 0f,
maxDistance: 10f,
minBlend: 0.3f, // 30% spatial when close
maxBlend: 1.0f // 100% spatial when far
);
sound.SpatialBlendController = controller;
You can also set a default controller for all new sounds:
Sound.DefaultSpatialBlendController = controller;
Best Practices
Call
engine.Update()every frame in your game loop. It handles spatial audio updates and cleans up finished one-shot sounds.Use
PlayOneShotfor short effects that fire and forget. Use explicitSoundobjects when you need long-term control.Attach sounds to
SpatialAnchorobjects rather than updating positions manually. It's cleaner and more efficient.Organize with buses from the start. Even if you don't need per-category volume or effects now, you will later.
Preload frequently-used sounds
Use streaming for long audio (music, ambience, cutscenes...). Use buffered sounds for everything else.
Dispose sounds you're done with if you hold a reference.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- GraphAudio.Core (>= 0.4.1)
- GraphAudio.IO (>= 0.4.1)
- GraphAudio.Realtime (>= 0.4.3)
- GraphAudio.SteamAudio (>= 0.4.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.