Example: Create a generator
Root Outputs
A root output lets you implement custom audio systems or sound generators, and integrate third party middleware (FMOD, Wise) that write directly to the main output, bypassing the Unity’s built-in mixer entirely.
Setting up a root output
You can set up root outputs from code using the following steps:
-
Implement the interfaces: Provide implementations of
RootOutputInstance.IRealtime and RootOutputInstance.IControl.
-
Allocate the root output: Call
ControlContext.AllocateRootOutput, passing your real-time and control implementations.
-
Destroy when finished: When the root output is no longer needed, call
ControlContext.Destroy to release it.
When you use ControlContext.AllocateRootOutput with the built-in control context, it connects the new root output to Unity’s main audio output and uses the real-time part of your instance to generate audio.
Root output processing stages
Implementations of RootOutputInstance.IRealtime processes audio in three stages:
-
Early processing: Perform work that must complete before later stages and before you use any resources managed by the root output. For example, a hardware input might sample its data here once so that later stages read a stable snapshot.
-
Processing: Schedule the main body of work. This stage runs in parallel with other root outputs. If you use jobs, you are responsible for managing dependencies and ensuring they complete in order. When consuming other processors (for example, generators), either schedule your jobs with the provided input dependency or complete that work first.
-
End processing: Mix the final results into the main audio output.
At the end of early processing, each root output might return an optional JobHandle. Before the processing stage starts, Unity combines all returned handles into a single dependency and passes this dependency to every root output during processing. If you schedule jobs in the processing stage, use this dependency to make sure any work started in early processing completes first.
Threading & real-time constraints
For each invocation of a root output, the early processing, processing, and end processing stages all execute sequentially on the same thread. Depending on the control context, this thread might be the audio thread (for the built-in context) or a job worker thread (for manual contexts).
The following real-time constraints apply to all three stages:
- No memory allocations neither managed nor native.
- No system calls (such as file access, networking, logging to disk/console, or sleeping).
- No locks or blocking operations such as mutexes, waiting on events/semaphores, or blocking of file access.
Best practices
- Keep the processing stages real‑time safe. Avoid allocations, locks, blocking I/O, logging, and throwing exceptions on the audio thread. Prefer stack-allocated or pooled buffers and struct-based state.
- Beware of threading. Don’t call
UnityEngine APIs from within Process. Use thread-safe and lock-free patterns for synchronizing shared state.
- Use Burst-compiled, struct-based code with simple value types and tight loops. Choose
Unity.Mathematics and lay out data for Burst auto-vectorization.
- Return a valid
JobHandle from the early stage if you schedule work there.
- Chain your processing-stage jobs to the combined dependency so the audio pipeline doesn’t stall waiting for them to finish.
Additional resources
Example: Create a generator
Copyright © 2023 Unity Technologies
优美缔软件(上海)有限公司 版权所有
"Unity"、Unity 徽标及其他 Unity 商标是 Unity Technologies 或其附属机构在美国及其他地区的商标或注册商标。其他名称或品牌是其各自所有者的商标。