🏠 1. Introduction
Advanced Audio Dispatcher for DOTS is a high-performance, thread-safe, and zero-GC bridge between Unity's Data-Oriented Technology Stack (DOTS) and the native FMOD Studio C-API.
Built specifically for Entities 1.0+ and the Burst Compiler, this asset allows you to dispatch thousands of audio events directly from your IJobEntity systems without allocating a single byte of managed memory.
Key Features:
- Zero GC Allocations: 100% unmanaged data structures.
- Burst Compatible: Dispatch audio commands from any background worker thread.
- Automatic Lifecycle: Looping sounds automatically track entity positions and clean themselves up when the target entity is destroyed.
- Asynchronous Occlusion: Built-in, multi-threaded raycast occlusion that doesn't block the Main Thread.
Why this asset?
The official FMOD Unity integration is fantastic for traditional OOP projects, but it relies heavily on MonoBehaviour, managed strings, and Main Thread execution. Using it in a DOTS project forces you to break the ECS paradigm, leading to thread locks and garbage collection spikes.
Audio Dispatcher solves this by bypassing the official C# wrapper entirely. It communicates directly with the raw FMOD native binaries (fmodstudioL.dll), providing a pure ECS architecture designed for AA/AAA performance.
⚙️ 2. Getting Started
Installation
🛑 CRITICAL WARNING: Do NOT import the full official FMOD Unity package into your DOTS project. It will clutter your project with unnecessary
MonoBehavioursand cause compilation conflicts.
Step 1: Get the Native Libraries
- Download the official FMOD for Unity package from fmod.com.
- Import the
.unitypackageinto your Unity project. - In the Import Window, click "None", and ONLY check the
Pluginsfolder. UncheckScripts,Editor, and everything else.
Step 2: Install Audio Dispatcher
- Open Window -> Package Manager.
- Click the + icon and select Add package from tarball...
- Select the
com.snivelercode.audio-dispatcher.fmod-1.0.0.tgzfile.
Project Settings
Once installed, navigate to Edit -> Project Settings -> SnivelerCode.
Here you can configure the core behavior of the audio engine:
- Max Channels: Hardware voice limit (default is 256. Lower to 64-128 for mobile).
- Occlusion Layer Mask: Define which physics layers block sound.
- Generated Code Path: Where the tool will save your generated C# IDs.
Bank Synchronization
- Build your FMOD Studio project and export the
.bankand.strings.bankfiles. - Drag and drop them anywhere into your Unity project.
- Create an empty
GameObjectin yourSubSceneand attach theFmod Sounds Library Authoringcomponent. - The system will automatically detect your banks, copy them to
StreamingAssets, and generate safe, stringlessulongIDs for all your events, buses, and parameters.
💻 3. Core API
To play sounds from a Burst-compiled job, you need to pass the FmodRuntimeAudio.ParallelWriter to your job.
Playing One-Shots
One-shots are "fire-and-forget" sounds (e.g., footsteps, gunshots, impacts). If the sound is spawned outside its audible range (defined in FMOD Studio), it is instantly culled to save CPU.
[BurstCompile]
public partial struct WeaponFireJob : IJobEntity
{
public SncRuntimeAudio.ParallelWriter AudioWriter;
private void Execute(in LocalTransform transform, in FireEvent fireEvent)
{
// Plays a sound at the given position with 80% volume
SncAudioID.WeaponsGunshot.Shot(transform.Position)
.Volume(0.8f)
.Pitch(1.1f)
.Apply(AudioWriter);
}
}
Looping Sounds & Lifecycle
When you call .Loop(entity), the system creates a lightweight Shadow Entity. This entity automatically tracks your target's position, rotation, and velocity (for the Doppler effect).
💡 Info: You do not need to stop loops manually! If you destroy the target entity (e.g., the enemy dies), the Shadow Entity detects it, tells FMOD to smoothly fade out the sound, and destroys itself. Zero memory leaks.
// Start a looping engine sound attached to a vehicle entity
SncAudioID.VehiclesEngine.Loop(entity)
.Volume(1.0f)
.Apply(AudioWriter);
Dynamic Parameters & Properties
You can change FMOD Local Parameters or built-in properties (like Min/Max 3D Distance) on the fly for any active loop.
// Update the RPM parameter of an already playing engine loop
SncAudioID.VehiclesEngine.Change(entity)
.Param(SncAudioParamID.VehiclesEngine.RPM, 3500f)
.Apply(AudioWriter);
Global Commands
Control buses and global parameters without string allocations. IDs are automatically generated from your .strings.bank.
// Pause the SFX bus (e.g., when the game is paused)
AudioWriter.SetBusPaused(SncBusID.GameSfx, true);
// Change Music Volume
AudioWriter.SetBusVolume(SncBusID.Music, 0.5f);
// Update a Global Parameter
AudioWriter.SetGlobalParameter(SncGlobalParamID.TimeOfDay, 14.5f);
🎭 4. Hybrid ECS & Animation
The FmodAnimatorBridge
Syncing standard Unity Animations (like footsteps) with DOTS audio is usually a headache. We solved this with the component.
- Attach SncAnimatorBridge to your character
GameObject(next to the Animator). - Add a new element to the Audio Events array.
- Give it a name (e.g.,
Footstep) and select your sound from the beautiful[SncEvent]dropdown. - Open the Unity Animation Window, add an Animation Event, select
PlayAudioEvent(string), and typeFootstep.
The bridge will safely catch the OOP event and dispatch it to the Burst-compiled DOTS queue without any GC allocations!
Proxy API
If you need to play a sound from a standard MonoBehaviour (e.g., a UI Button click), use the Proxy API extensions:
public void OnButtonClick()
{
SncAudioID.UI_Click.Shot(transform.position).ProxyEvent();
SncBusID.Master.ProxyBusVolume(0.5f);
}
🧱 5. Advanced Features
Asynchronous Raycast Occlusion
The package includes a highly optimized, multi-threaded occlusion system. It uses Unity's RaycastCommand to check for obstacles between the listener and the sound source.
- Time-Slicing: Rays are cast every N frames (configurable in Project Settings) to save physics CPU time.
- Smooth Interpolation: When an object goes behind a wall, the occlusion parameter is smoothly interpolated, preventing harsh audio popping.
Smart Culling & Virtualization
Audio Dispatcher uses FMOD Studio as the Single Source of Truth.
It automatically reads the MaxDistance of your events from the FMOD banks.
- One-Shots: If a one-shot is spawned further than its
MaxDistance, the command is dropped. FMOD never even hears about it. - Loops: If a looping entity moves out of range, the system calls
EventSetPaused(true). This instantly frees up the hardware channel (Voice Stealing) and stops sending 3D matrices, saving massive amounts of CPU. When the entity comes back into range, it seamlessly resumes.
🐞 6. Debugging & Profiling
Visual Debugger
To help you profile your audio, we included a high-performance Visual Debugger.
- Go to
Edit -> Project Settings -> SnivelerCode. - Enable Visual Debugger.
- Open the Scene View while in Play Mode.
You will see wireframe spheres representing the Min/Max distance of your sounds, color-coded icons showing their playback state (Playing, Stopped, Virtualized), and real-time occlusion percentages.
❓ 7. FAQ & Troubleshooting
Q: I get a DllNotFoundException: fmodstudioL error.
A: You forgot to import the native libraries. Download the official FMOD for Unity package and import only the Plugins folder into your project.
Q: My background music is being culled/muted when I move the camera!
A: Your music event is being treated as a 3D sound. Open FMOD Studio, select your music event, and delete the Spatializer effect from the Master Track. Rebuild your banks. The system will automatically detect it as a 2D sound (MaxDistance = 0) and disable culling for it.
Q: When I spawn 1,000 enemies, some sounds don't play or cut off abruptly.
A: You are experiencing "Voice Stealing Thrashing". FMOD has a hard limit of 256 channels.
- Open FMOD Studio and reduce the
Max Distanceof your enemy sounds so they cull earlier. - Set
Max Instances(e.g., to 20) in the event's Macros, and setStealingtoVirtualizeorFurthest. - Ensure your looping sounds have a Loop Region on the FMOD timeline, otherwise, they will die naturally while virtualized.