Generators
Learn about generators.
Read time 7 minutesLast updated 13 days ago
A generator is a custom sound source that produces audio and injects it directly into a scene, typically through an . Generators are a core extension point of the scriptable audio pipeline and let you synthesize or stream real-time audio.
AudioSourceYou can attach a generator to an in the following two ways: Asset-based workflow and component-based workflow.
AudioSourceBoth workflows rely on implementing the interface, which acts as a factory for creating instances of your generator.
IAudioGeneratorAsset-based Workflow
Create a that implements .
ScriptableObjectIAudioGeneratorusing UnityEngine;[CreateAssetMenu(fileName = "MyAssetBasedGenerator", menuName = "Audio/Generators/My Asset-Based Generator", order = 1)]public class MyAssetBasedGenerator : ScriptableObject, IAudioGenerator{ public bool isFinite => ...; public bool isRealtime => ...; public DiscreteTime? length => ...; public GeneratorInstance CreateInstance( ControlContext context, AudioFormat? nestedConfiguration, ProcessorInstance.CreationParameters creationParameters) { return context.AllocateGenerator(new MyRealtime(...), new MyControl(...)); }}
After you've created the asset, you can assign it to an by dragging and dropping to the Generator field in the Inspector.
AudioSourceComponent-based workflow
To use the Component-based workflow, define a that implements and then add it to a GameObject in your scene. In the Inspector for your , assign this component to the Generator field.
MonoBehaviourIAudioGeneratorAudioSourceusing UnityEngine;public class MyComponentBasedGenerator : MonoBehaviour, IAudioGenerator{ public bool isFinite => ...; public bool isRealtime => ...; public DiscreteTime? length => ...; public GeneratorInstance CreateInstance( ControlContext context, AudioFormat? nestedConfiguration, ProcessorInstance.CreationParameters creationParameters) { return context.AllocateGenerator(new MyRealtime(...), new MyControl(...)); }}
The IAudioGenerator
interface
IAudioGeneratorAn includes the following:
IAudioGenerator- Capabilities: Properties such as that describe how the generator behaves in advance.
GeneratorInstance.ICapabilities - Factory method: The method, which the audio system calls to create a
CreateInstance.GeneratorInstance
Following is a list of the capabilities:
- Finite: When true, indicates the generator will eventually terminate. The total length does not need to be known up front.
- Real-time: When true, indicates the generator must render in real time at the system’s buffer size and sample rate. For example, hardware-driven streams, graphs that cannot run ahead, or anything with an internal timeline that is synchronized against the system dsp clock. If unsure, set this to .
false - Length: If the generator is finite and you know the total length, report it here. While optional, exposing this property is valuable for editor tooling and scheduling.
You must expose matching capabilities in your implementation. A mismatch between and may cause unexpected behavior and will produce warnings in the Console.
GeneratorInstance.IRealtimeIAudioGeneratorIRealtimeConfiguring a generator
Generator configuration happens initially during construction, and additionally when the system changes configuration. This typically happens in response to a user action, such as changing the audio output device in the OS system settings or when connecting a pair of headphones. It is a two-step negotiation between the host (the owner of the generator) and the generator:
- The host suggests a preferred for the generator (sample rate, speaker layout, buffer size).
AudioFormat - The generator reports the it will actually use.
Setup
Some generators (e.g. when synthesizing audio) can adapt to many formats, while others (e.g. when playing back audio files) may only support the file’s native format. If a generator selects a different format than suggested by the host, it is the host’s responsibility to convert between formats when needed.
When creating child generators via , a parent can provide an optional as a suggestion to the child. Children should follow the suggestion when possible to minimize conversions.
ControlContext.AllocateGeneratorAudioFormatProcessing a generator
Generators are always processed within a mix cycle. For a description of mix cycles see the mix cycles subsection in the Scriptable processors concepts section.
Nested generators
Generators can be organized into hierarchical trees. In each tree, the root generator sits at the top and is responsible for invoking , , and on its immediate child generators. Each child, in turn, forwards those calls to its own children. This pattern enables complex structures such as mixers, blend containers, and randomized sequencers.
ConfigureUpdateProcessWhen a parent generator creates a child using , it might pass an optional suggested . The child generator should match the suggestion if possible to minimize format conversions, but can select a different format, if required.
ControlContext.AllocateGeneratorAudioFormatUse AudioClips as nested generators
AudioClipIAudioGeneratorAudioClip.CreateInstanceGeneratorInstanceRealtimeContextTo use an as a nested generator, call from within your generator's method. You must then call , , and at the appropriate times to manage the child instance's lifecycle.
AudioClipAudioClip.CreateInstanceCreateInstanceUpdateProcessDestroyusing UnityEngine;using UnityEngine.Audio;using Unity.IntegerTime;using static UnityEngine.Audio.ProcessorInstance;[CreateAssetMenu(fileName = "ClipPlayer", menuName = "Audio/Generators/Clip Player")]public class ClipPlayerGenerator : ScriptableObject, IAudioGenerator{ public AudioClip clip; public bool isFinite => clip != null && ((IAudioGenerator)clip).isFinite; public bool isRealtime => false; public DiscreteTime? length => clip != null ? ((IAudioGenerator)clip).length : null; public GeneratorInstance CreateInstance( ControlContext context, AudioFormat? nestedFormat, CreationParameters creationParameters) { if (clip == null) return default; // Create a child generator instance from the AudioClip var clipInstance = clip.CreateInstance(context, nestedFormat, creationParameters); return context.AllocateGenerator( new Realtime(clipInstance, clip), new Control(clipInstance), nestedFormat, creationParameters); } struct Control : GeneratorInstance.IControl<Realtime> { GeneratorInstance m_ClipInstance; public Control(GeneratorInstance clipInstance) { m_ClipInstance = clipInstance; } public void Configure( ControlContext context, ref Realtime realtime, in AudioFormat format, out GeneratorInstance.Setup setup, ref GeneratorInstance.Properties properties) { // Configure the child and use its setup context.Configure(m_ClipInstance, format); var childConfig = context.GetConfiguration(m_ClipInstance); setup = childConfig.setup; } public void Update(ControlContext context, Pipe pipe) { // Forward update to the child generator context.Update(m_ClipInstance); } public Response OnMessage(ControlContext context, Pipe pipe, Message message) { return Response.Unhandled; } public void Dispose(ControlContext context, ref Realtime realtime) { // Clean up the child generator context.Destroy(m_ClipInstance); } } struct Realtime : GeneratorInstance.IRealtime { GeneratorInstance m_ClipInstance; readonly bool m_IsFinite; readonly bool m_IsRealtime; readonly DiscreteTime? m_Length; public Realtime(GeneratorInstance clipInstance, IAudioGenerator source) { m_ClipInstance = clipInstance; m_IsFinite = source.isFinite; m_IsRealtime = source.isRealtime; m_Length = source.length; } public bool isFinite => m_IsFinite; public bool isRealtime => m_IsRealtime; public DiscreteTime? length => m_Length; public void Update(UpdatedDataContext context, Pipe pipe) { } public GeneratorInstance.Result Process( in RealtimeContext context, Pipe pipe, ChannelBuffer buffer, GeneratorInstance.Arguments args) { // Process the child generator to fill the buffer return context.Process(m_ClipInstance, buffer, args); } }}
Restrictions
You can only use persistent (imported) assets as generators. The following types of clips are not supported and will throw an exception:
AudioClip- Clips created at runtime with
AudioClip.Create - Clips obtained from
DownloadHandlerAudioClip - Clips recorded with the API
Microphone - Tracker module formats (,
.mod,.it,.s3m).xm
Additionally, once an has been used as a generator, and are permanently blocked for the lifetime of that clip instance:
AudioClipGetDataSetData- - Returns
AudioClip.GetDataand logs a warning.false - - Returns
AudioClip.SetDataand logs a warning.false
You can call on a generator clip. If you unload a clip that has active generator instances, this process marks those generators as finished and drains any remaining buffered audio data before it completes. Therefore, to prevent synchronization costs, only unload audio data when you are sure it's no longer in use.
AudioClip.UnloadAudioDataBest practices
Consider the following:
- Keep real‑time safe. Avoid allocations, locks, blocking I/O, logging, and throwing exceptions on the audio thread. Use stack-allocated or pooled buffers, and struct-based states for predictable performance.
Process - Beware of threading. Never call APIs from within
UnityEngine. Use pipes to synchronize state between the control and real-time part, and avoid any multithreaded communication that might yield indeterministic results.Process - Use Burst-compiled, struct-based code. Use simple value types and tight loops for improved performance. Leverage and organize data for optimal Burst auto-vectorization.
Unity.Mathematics - Match capabilities consistently. Ensure that the values for ,
isFinite, andisRealtimein yourlengthimplementation match with the values inIAudioGenerator.GeneratorInstance.IRealtime - Use the host format when you can. Adopting the host’s suggested minimizes additional resampling or channel mixing.
AudioFormat - Report the length when known. Providing an accurate property enables better waveform previews, scheduling, and progress indicators in the Editor.
length - Handle completion gracefully. When a finite generator reaches the end, return silence and signal completion so hosts can deallocate or transition.
- Validate externally received inputs. Clamp out-of-range parameters to valid ranges and handle mismatches in channel counts to prevent errors.