FS.GG.Audio.Engine
0.4.0
Prefix Reserved
dotnet add package FS.GG.Audio.Engine --version 0.4.0
NuGet\Install-Package FS.GG.Audio.Engine -Version 0.4.0
<PackageReference Include="FS.GG.Audio.Engine" Version="0.4.0" />
<PackageVersion Include="FS.GG.Audio.Engine" Version="0.4.0" />
<PackageReference Include="FS.GG.Audio.Engine" />
paket add FS.GG.Audio.Engine --version 0.4.0
#r "nuget: FS.GG.Audio.Engine, 0.4.0"
#:package FS.GG.Audio.Engine@0.4.0
#addin nuget:?package=FS.GG.Audio.Engine&version=0.4.0
#tool nuget:?package=FS.GG.Audio.Engine&version=0.4.0
FS.GG.Audio.Engine
The mixing/voice layer of the FS.GG.Audio component: named
buses with independent gain, linear fades and equal-power cross-fades, side-chain ducking, and 3D
listener/emitter positioning — all as a pure, deterministic state model advanced once per frame by
Engine.step.
The engine opens no device of its own. It realizes through the
FS.GG.Audio.Host seam, feature-detecting
IMixingBackend for continuous mixing and spatial control; against a plain IAudioBackend it
degrades gracefully, folding bus/fade/duck gains into one-shot volumes and collapsing 3D voices to
non-positional ones.
Install
dotnet add package FS.GG.Audio.Engine
Quick start
open FS.GG.Audio.Core
open FS.GG.Audio.Host
open FS.GG.Audio.Engine
// NullBackend is not an IMixingBackend, so 3D voices degrade to non-positional here.
// Supply an IMixingBackend (e.g. the OpenAL backend) to have them spatialize.
let engine = Engine.create (NullBackend.create () :> IAudioBackend)
Engine.setListener engine 0.0 0.0 0.0
Engine.step engine 0.016 [ Audio.playMusic (TrackId "bgm") true
Audio.duck Music 0.5 250.0 // side-chain duck for 250 ms
Audio.playSfx3D (SoundId "step") 3.0 0.0 0.0 1.0 ]
Engine.fadeBus engine Sfx 0.2 1.5 // linear fade over 1.5 s
Engine.crossFade engine Music Ambient 2.0 // equal-power, constant summed power
engine.LastVoices // the voices realized on the most recent step
The sink: mixing by default
A host-level product wires a per-frame sink of type AudioEffect list -> unit. Building it with
FS.GG.Audio.Host's Audio.play backend plays straight at the device: no mixer, no clock, so
SetBusVolume/Duck are silently discarded and PlaySfx3D plays non-positional — a volume slider
wired that way does nothing (#27). Engine.createSink returns a sink of that same type which steps
an owned engine instead, so the mixing semantics apply:
use backend = OpenAlBackend.create resolver
let audioSink = Engine.createSink backend // AudioEffect list -> unit, and it MIXES
audioSink [ Audio.setBusVolume Sfx 0.25 // the slider now actually attenuates...
Audio.playSfx (SoundId "click") 1.0 ] // ...this, to 0.25
Create the sink once and reuse it: it owns the engine and the clock, so one rebuilt per frame
would start from a fresh mix every frame and no envelope would ever advance. dt is the interval
between successive calls, so drive it once a frame and fades and ducks progress on their own.
Engine.createSinkOver engine— same, over an engine you own, sofadeBus/crossFade/setListener(which are engine calls, not effects) stay reachable.Engine.createSinkWith dt engine— you supplydt. Use it when the product already owns a frame clock, and in tests, where a wall clock is not deterministic.
Surface
Engine.createSink/createSinkOver/createSinkWith— build a mixingAudioEffect list -> unitsink (see above).Engine.create/createWith— build over anIAudioBackend; buses start at unity gain.createWithtakes an explicitSpatialConfig(RefDistance,Rolloff,MaxDistance);Engine.defaultSpatialis1,1, uncapped.Engine.step engine dt effects— advancedtseconds and apply a batch, in order: complete elapsed fade/duck envelopes, apply the batch, resolve effective gains, realize through the backend. Total; never throws.Engine.fadeBus/Engine.crossFade/Engine.setListener— timed envelopes and listener pose.T.BusGain/MusicGain/Listener/LastVoices— the observable state, exposed for headless assertions.Voice—EffectiveGain= request gain × bus gain × Master gain × distance attenuation, clamped to[0,1];Panin[-1,1];Positional = falsewhen the voice degraded.
Master is not folded into BusGain — it is applied per voice, in EffectiveGain.
Determinism
Identical inputs produce identical realized backend calls. Paired with the NullBackend record
path, that makes buses, fades, ducking, and 3D assertable headless, with no device in the loop.
License
MIT — see LICENSE.
| 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
- FS.GG.Audio.Core (>= 0.4.0)
- FS.GG.Audio.Host (>= 0.4.0)
- FSharp.Core (>= 10.1.301)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on FS.GG.Audio.Engine:
| Package | Downloads |
|---|---|
|
FS.GG.Audio.Elmish
Elmish Cmd authoring surface over FS.GG.Audio.Host/.Engine — Audio.Cmd.playSfx/playMusic/stopMusic/setMasterVolume produce Elmish commands that play straight through an IAudioBackend (no mixing), while Audio.Cmd.ofEngine routes a batch through FS.GG.Audio.Engine so bus volume, ducking, and 3D positioning apply. Depends on Elmish (MIT), never on FS.GG.UI. Depends on FS.GG.Audio.Host, so it also redistributes the OpenAL Soft native library (LGPL-2.0-or-later) as a separate, dynamically-linked, replaceable shared library — see THIRD-PARTY-NOTICES.md in this package. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.4.0 | 211 | 7/21/2026 |
| 0.3.0 | 986 | 7/16/2026 |
| 0.2.0 | 1,527 | 7/11/2026 |
| 0.1.0 | 393 | 7/9/2026 |
| 0.1.0-preview.1 | 80 | 7/7/2026 |