Beryllium.Audio
0.9.990
Prefix Reserved
dotnet add package Beryllium.Audio --version 0.9.990
NuGet\Install-Package Beryllium.Audio -Version 0.9.990
<PackageReference Include="Beryllium.Audio" Version="0.9.990" />
<PackageVersion Include="Beryllium.Audio" Version="0.9.990" />
<PackageReference Include="Beryllium.Audio" />
paket add Beryllium.Audio --version 0.9.990
#r "nuget: Beryllium.Audio, 0.9.990"
#:package Beryllium.Audio@0.9.990
#addin nuget:?package=Beryllium.Audio&version=0.9.990
#tool nuget:?package=Beryllium.Audio&version=0.9.990
Beryllium.Audio
Audio manager for the Beryllium engine: 3D positional sound with HRTF spatialization, per-emitter effect chains and a shared reverb bus, built on NAudio and NVorbis.
- Emitters that play WAV or Ogg Vorbis clips, either decoded into a shared in-memory cache or streamed from disk
- HRTF spatialization from OpenAL Soft
.mhrdata sets (a default set is embedded) - Distance attenuation with air absorption, Doppler pitch shift, 3-band EQ, delay, pitch, fade in/out, volume and a global reverb bus
- Freely routable effect chains per emitter
- Output device tracking: follows the Windows default render device and recovers when a device is unplugged
- Real-time safe audio path: parameter changes from the game thread reach the audio thread through lock-free publication, and the audio thread doesn't allocate in steady state
Audio output uses WASAPI, so it requires Windows. The package targets
net10.0.
Installation
dotnet add package Beryllium.Audio
Quick start
using System.Numerics;
using BerylliumAudio;
using BerylliumAudio.EffectWrappers;
using BerylliumAudio.EffectWrappers.Base;
using BerylliumAudio.Emitters.Base;
using var audio = new AudioManager(); // default HRTF, master volume 1.0
audio.TryAddEmitter("campfire", AudioEmitterTypes.Cached, out var fire);
fire.TryAddAudio("Sounds/campfire.ogg");
fire.IsLooped = true;
fire.Position = new Vector3(4, 0, -2);
// Add effects, then wire them between the emitter's send and return endpoints.
fire.TryAddAudioEffect(AudioEffectTypes.Distance, out var distance);
fire.TryAddAudioEffect(AudioEffectTypes.Spatial, out var spatial);
((DistanceAudioEffect)distance).LoudnessDb = 70;
distance.Input = fire.EffectLoopSend; // send -> distance
spatial.Input = distance; // distance -> spatial
fire.EffectLoopReturn.Input = spatial; // spatial -> return
fire.Play();
// Once per game frame:
audio.SetListenerData(cameraPosition, cameraForward, cameraUp, cameraRight);
audio.Update(deltaSeconds);
Concepts
AudioManager
AudioManager owns the output device, the mixer (48 kHz, stereo, 32-bit float), the reverb bus and all emitters. Create one per application and dispose of it on shutdown.
| Member | Purpose |
|---|---|
Update(elapsedSeconds) |
Call once per frame. Advances emitter states and loop pauses, feeds Doppler velocities and re-selects the output device after device changes. |
SetListenerData(position, forward, up, right) |
Sets the whole listener pose in one call (preferred over the individual Listener* properties). The vectors should be normalized. |
MasterVolume |
Global output gain in [0, 1]. |
ReverbBusRoomSize, ReverbBusCutoffHz, ReverbBusWet |
Reverb bus decay time (RT60, seconds), brightness (Hz) and wet/dry mix. |
TryLoadMhrFile(path) |
Loads an HRTF data set. A null or unreadable path loads the embedded default. Returns false for invalid data, in which case no data set is loaded and spatial effects pass audio through unspatialized. |
MhrFilePath |
File the current HRTF data came from, or null when the embedded default is in use (including after a fallback). |
PlayAll(), PauseAll(), StopAll() |
Transport control for every emitter. |
EmittersVersion |
Changes whenever emitters are added or removed, so you can invalidate cached emitter references. |
OnMmDeviceChanged |
Raised after the output device has been re-selected. |
HearingMinimumDb, AirSoundAbsorptionCoefficient |
Static environment settings used by distance attenuation. |
Most emitter operations are also available on the manager by emitter name (TryPlayEmitter, TryAddAudioToEmitter, TryAddEffectToEmitter, ...).
Emitters
An emitter is a sound source with a position, a list of clips and an effect chain.
AudioEmitterTypes.Cacheddecodes each clip once (resampled and converted to the mixer format) into a process-wide cache shared by all emitters. Use it for short, frequently played sounds.AudioManager.ClearAudioCache()releases the cache.AudioEmitterTypes.Streamingdecodes from disk while playing. Use it for music and long ambiences.
An emitter can hold several clips (TryAddAudio). Each Play() from a stopped or finished state, and each loop iteration, picks one of them at random. Looping emitters can wait a random time between iterations (LoopPauseRangeMs = (min, max)).
State (Idle, Playing, Paused, Finished, EffectLoopDisconnected) is refreshed by Update(); subscribe to OnStateChanged or OnCurrentAudioChanged to react to changes.
Supported files: WAV (8/16/24/32-bit PCM or 32-bit float) and Ogg Vorbis, mono or stereo, any sample rate. Mono clips are upmixed and clips at other sample rates are resampled. Files are identified by their header, not their extension.
Effect chains
Every emitter has two fixed endpoints: EffectLoopSend (the clip's samples) and EffectLoopReturn (what goes to the mixer). A new emitter connects send directly to return. Effects added with TryAddAudioEffect start out unconnected; you place them by setting Input (or Output):
emitter.TryAddAudioEffect(AudioEffectTypes.Equalizer, out var eq);
emitter.TryAddAudioEffect(AudioEffectTypes.Reverb, out var reverb);
eq.Input = emitter.EffectLoopSend;
reverb.Input = eq;
emitter.EffectLoopReturn.Input = reverb;
While the chain from send to return is broken, the emitter is stopped and reports EffectLoopDisconnected, and Play() is ignored until the chain is complete again. Removing an effect that sits in the chain breaks it the same way. Rewiring is thread-safe while audio is playing.
| Effect | Wrapper | Main parameters |
|---|---|---|
Distance* |
DistanceAudioEffect |
LoudnessDb (sets MaxAudibleDistanceMeters), volume and low-pass attenuation curves, DistanceMinimumCutoffHz / DistanceMaximumCutoffHz |
Doppler* |
DopplerAudioEffect |
Pitch shift from the relative velocity of listener and emitter (measured between Update calls) |
Spatial* |
SpatialAudioEffect |
HRTF binaural rendering; AudioSpread from 0 (point source) to 1 (omnidirectional) |
Equalizer |
ThreeBandEqualizerAudioEffect |
Low shelf, mid peak and high shelf: *GainDb, *FreqHz, LowSlope, MidQ, HighSlope |
FadeInOut |
FadeInOutAudioEffect |
FadeInDurationMs, FadeOutDurationMs, shaping functions |
Pitch |
PitchAudioEffect |
Pitch factor (0.5 = octave down, 2 = octave up) |
Delay |
DelayAudioEffect |
DelayMs (up to 3 s), Feedback, Mix |
Reverb |
ReverbAudioEffect |
ReverbSend to the shared reverb bus |
TimePosition* |
TimePositionAudioEffect |
Read or seek the playback position |
Volume |
VolumeAudioEffect |
Volume within VolumeRange |
* At most one per emitter.
Every effect has IsBypass to pass audio through unchanged. Distance, Doppler and Spatial also offer a demo mode (IsDemoMode + DemoEmitterPosition) that ignores the real listener and places a listener at the origin facing -Z, which is handy for editor previews.
Attenuation curves and fade shapes take BaseShapingFunction instances from Beryllium.ShapingFunctions.
Reverb bus
Emitters with a Reverb effect send part of their signal (ReverbSend) to one shared Freeverb-style reverb. Configure it through the manager:
audio.ReverbBusRoomSize = 1.8f; // RT60 in seconds (0.05-6)
audio.ReverbBusCutoffHz = 6000; // darker <-> brighter tail
audio.ReverbBusWet = 0.3f; // wet/dry balance of the final mix
When nothing is sent to the bus and the tail has died out, the reverb's processing is skipped entirely.
HRTF data
Spatialization uses OpenAL Soft's minimum-phase HRTF format (.mhr, MinPHR03). The embedded default set is used unless you pass a path to the constructor or to TryLoadMhrFile. Data sets are resampled to the mixer rate on load.
Threading
Call the control API (emitters, effects, listener, Update) from your game thread. Mixing runs on the WASAPI render thread. Parameter changes reach it through lock-free publication or per-block snapshots, and effect-graph rewiring is serialized with rendering. The emitter registry itself is concurrent, so emitters can be looked up or added from other threads.
Building and testing
dotnet build
dotnet test
Tests live in Tests/BerylliumAudio.Tests (xUnit v3, run through Microsoft.Testing.Platform, which global.json opts into). They render audio offline without an output device, so they also run on CI machines without sound hardware.
License
MIT © Beryllium Co.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. 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. |
-
net10.0
- Beryllium.Math (>= 0.2.1)
- Beryllium.ShapingFunctions (>= 1.6.0)
- NAudio (>= 2.3.0)
- NVorbis (>= 0.10.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.9.990 | 66 | 9/30/2026 |