Guidance / API reference
Occlusion Mixer Slots
Pre-built per-bus pools of mixer groups used as routing targets for SFX voices that need cutoff modulation. Replaces per-source AudioLowPassFilter (which clicked on Stop→Play biquad reset). See Manual chapter 14 for architecture and design rationale.
OcclusionLayout
Namespace: AudioSystem
Inheritance: ScriptableObject
Menu: Create > Audio System > Occlusion Layout
Editor window: Window > Audio System > Occlusion Layout
Generated layout describing which mixer groups serve as occlusion slots for each user-authored bus. Authored at edit time by the Occlusion Layout Builder; consumed at runtime by AudioManager to build per-bus slot pools.
One asset per project. Reference it from AudioManager ▸ Occlusion Layout.
Properties
public AudioMixer VoiceMixer { get; }
public IReadOnlyList<BusEntry> Buses { get; }
VoiceMixer is the mixer the layout was generated against. At runtime, AudioManager refuses to build slot pools if its assigned voiceMixer doesn’t match this — re-run the builder against the correct mixer.
Nested Classes
[Serializable]
public sealed class BusEntry
{
public AudioMixerGroup Bus; // user-authored bus (e.g. Footsteps)
public SlotEntry[] Slots; // generated occlusion slots under this bus
}
[Serializable]
public sealed class SlotEntry
{
public AudioMixerGroup MixerGroup; // the slot's mixer group (voice's outputAudioMixerGroup)
public string CutoffParam; // exposed parameter name for the slot's Lowpass cutoff
public SendEntry[] Sends; // per-ReverbSendBus send params, indexed by registry order
}
[Serializable]
public sealed class SendEntry
{
public string RoomId; // ReverbSendBus.BusName key
public string SendLevelParam; // exposed mixer parameter name for the Send level (dB)
}
OcclusionSlot
Namespace: AudioSystem
Inheritance: Plain public sealed class (not a Unity component)
One slot inside a per-bus pool. Voices borrow a slot at acquire time (AudioManager.GetVoice(container)) and return it on AudioManager.ReturnVoice(voice). While held, the voice’s outputAudioMixerGroup is set to the slot’s mixer group and any subsystem can modulate muffle by writing to the slot’s exposed parameters.
Fields
public readonly AudioMixerGroup MixerGroup; // voice routes through this
public readonly string CutoffParam; // exposed Lowpass cutoff (Hz)
public readonly int IndexInPool; // position inside the BusSlotPool
public readonly AudioMixerGroup OwningBus; // the user-authored bus this slot belongs to
public readonly Dictionary<string, string> SendLevelParams; // ReverbSendBus.BusName → exposed Send level param (dB)
All fields are readonly — the slot is immutable once authored. Modulating the slot at runtime means writing to the mixer via voiceMixer.SetFloat(slot.CutoffParam, hz) or voiceMixer.SetFloat(slot.SendLevelParams[roomId], levelDb).
SendLevelParams is empty when no ReverbSendBusRegistry was assigned at layout-generation time.
AudioManager Slot Pool API
The acquire/release surface lives on AudioManager. Most projects never call these directly — AudioManager.GetVoice(container) handles slot lifecycle automatically based on the container’s opt-in flags. The public API is exposed for diagnostics and custom routing scenarios.
Inspector Fields
| Field | Default | Description |
|---|---|---|
voiceMixer (VoiceMixer) |
null | The mixer asset hosting the occlusion slot groups. Must match OcclusionLayout.VoiceMixer. |
occlusionLayout (OcclusionLayout) |
null | The generated layout asset. Required for slot pools to build at startup. |
defaultOcclusionSlotsPerBus |
6 | Default slots-per-bus used by the Occlusion Layout Builder. Per-bus overrides live on the layout asset. |
occlusionRayCount (range 1–5) |
3 | Rays per voice per occlusion tick. 1 = binary, 3+ = partial occlusion via blocked fraction. |
occlusionRaySpreadMeters (range 0–2) |
0.5 | Width of the side-ray spread at the source end (meters). |
occlusionGainSmoothingSeconds |
0.06 | Tau for per-frame exponential smoothing of GainStack.OcclusionGain. |
occlusionCutoffSmoothingSeconds |
0.04 | Tau for per-frame exponential smoothing of the slot’s mixer cutoff parameter. |
occlusionUpdateInterval |
0.2 | Seconds between OcclusionUpdate ticks (raycast cadence). Per-frame smoothing handles the in-between. |
occlusionMask |
~0 | Physics layer mask used by the occlusion raycast. |
Properties
public AudioMixer VoiceMixer { get; }
public OcclusionLayout OcclusionLayout { get; }
public int DefaultOcclusionSlotsPerBus { get; }
public bool OcclusionSlotPoolsReady { get; }
OcclusionSlotPoolsReady is true once InitializeOcclusionSlotPools ran successfully at Awake — i.e. both voiceMixer and occlusionLayout are assigned, they agree on which mixer the layout targets, and at least one bus entry produced a slot pool.
Methods
AcquireOcclusionSlot
public OcclusionSlot AcquireOcclusionSlot(AudioMixerGroup bus)
Acquires a free slot from the pool owned by bus. Returns null if (a) no pool exists for the bus (the bus wasn’t in the layout), (b) the slot pool isn’t ready, or (c) all slots are in use (pool exhausted — voice plays with no low-pass and no reverb sends, though occlusion’s volume drop still applies; a throttled warning is logged once per second per bus). This method logs nothing for cases (a) and (b); GetVoice(container) warns once per bus for case (a) when an Occlusion Layout is assigned, since the layout is then out of date.
Caller is responsible for calling ReleaseOcclusionSlot exactly once. The voice pool’s ReturnVoice handles this for voices acquired via GetVoice(container).
ReleaseOcclusionSlot
public void ReleaseOcclusionSlot(OcclusionSlot slot)
Returns the slot to its pool. Resets the slot’s cutoff to 22 kHz and silences all reverb-send parameters so the next voice on this slot starts clean. Safe to call with null.
GetOcclusionSlotsInUse / GetOcclusionSlotCapacity
public int GetOcclusionSlotsInUse(AudioMixerGroup bus)
public int GetOcclusionSlotCapacity(AudioMixerGroup bus)
Diagnostic counters. Both return -1 if no pool exists for the given bus.
Example — runtime diagnostic HUD:
foreach (var bus in interestingBuses)
{
int inUse = AudioManager.Instance.GetOcclusionSlotsInUse(bus);
int cap = AudioManager.Instance.GetOcclusionSlotCapacity(bus);
GUI.Label(rect, $"{bus.name}: {inUse}/{cap} slots");
} From the Pepper.SFX 3.1 guides (API_REFERENCE.md). Something wrong or unclear? Tell me.