Guidance / API reference
AudioExtensions & Utilities
AudioExtensions
Namespace: AudioSystem
Type: Static utility class
Extension Methods
public static AudioVoice PlayAtPosition(this AudioContainer container, Vector3 position)
public static AudioVoice PlayAttached(this AudioContainer container, GameObject target)
public static AudioVoice PlayWithVolume(this AudioContainer container, float volumeScale, Vector3 position = default)
All three return the primary voice. PlayWithVolume multiplies volumeScale into every voice the play started, on top of volume randomization and any Switch/Blend outer volume.
Example:
myContainer.PlayAtPosition(explosionPos);
myContainer.PlayAttached(player);
myContainer.PlayWithVolume(0.5f, transform.position);
Conversion Methods
public static float DbToLinear(float db) // 10^(db/20)
public static float LinearToDb(float linear) // 20 * log10(linear), or -80 if linear <= 0
public static float CentsToPitch(float cents) // 2^(cents/1200)
Example:
float linear = AudioSystem.AudioExtensions.DbToLinear(-6f); // ~0.5
float db = AudioSystem.AudioExtensions.LinearToDb(0.5f); // ~-6dB
float pitch = AudioSystem.AudioExtensions.CentsToPitch(100f); // 1.059 (1 semitone up)
Unity 6.3 and newer has its own
UnityEngine.AudioExtensions. In a script with bothusing UnityEngine;andusing AudioSystem;, the short nameAudioExtensionsis ambiguous there and won’t compile (error CS0104). WriteAudioSystem.AudioExtensions, as above, or addusing AudioExtensions = AudioSystem.AudioExtensions;at the top of the file. The extension methods (PlayAtPositionand the others) aren’t affected.
ListenerUtil
Namespace: AudioSystem
Type: Static utility class
public static Vector3 GetPosition() // the listener's position (zero if none)
public static bool TryGetPosition(out Vector3 position)
public static Transform Get() // the primary point's Follow transform, or the scene listener
public static void Set(Transform listener) // registers a point that follows it; null removes it
public static void Invalidate() // removes Set's point; the next lookup searches again
Description: Where the listener is, for Pepper’s distance-based features (voice importance, occlusion rays, the Furthest steal, multi-position picks). With a listening point registered, the primary point (AudioManager.SetPrimaryListeningPoint, else the first) is the listener and nothing is searched for. With none, the scene is searched for an AudioListener with a one-time warning; a found listener is kept until destroyed, and while there is none the search runs at most once a frame. Set registers a point, which a point added with AudioManager.AddListeningPoint takes priority over; if its transform is destroyed, the search takes over again. Get returns null for a point placed with SetPose: use GetPosition.
AudioListeningPoint
Namespace: AudioSystem
Type: class (created by AudioManager.AddListeningPoint)
public Transform Follow { get; set; } // read the pose from this transform; null = SetPose
public Vector3 Position { get; }
public Quaternion Rotation { get; }
public Vector3 Velocity { get; } // measured each step, unless SetVelocity was called
public float Weight { get; set; } // 0-1: how much it counts, with Spatial Mode = Pepper
public float PanWidth { get; set; } // 0 mono … 1 stereo, with Spatial Mode = Pepper
public bool IsRegistered { get; }
public bool IsPrimary { get; } // 3.1.0-dev.8
public void SetPose(Vector3 position, Quaternion rotation) // ignored while Follow is alive
public void SetVelocity(Vector3 metresPerSecond) // from then on, not measured
public void ClearVelocity() // measure again, from where it is (a camera cut)
Velocity feeds Approach Speed and Pepper’s Doppler. ClearVelocity (3.1.0-dev.7) goes back to measuring and starts again from the point’s place, so a camera cut isn’t read as speed; the last velocity stays until a new one is measured. Pointing Follow at another transform does the same (3.1.0-dev.8), keeping a velocity given with SetVelocity.
Description: Where the game listens from. While Follow is set and alive, the point reads that transform; if it is destroyed, the point is removed (IsRegistered false), with one warning a session (3.1.0-dev.8; it used to stay where it last was). Set the pose in Update or LateUpdate: Pepper’s step runs after every LateUpdate. With the Audio Manager’s Spatial Mode = Unity, Pepper listens from the primary point (IsPrimary; see Listening Point Methods); with Pepper, every point counts by its Weight (a sound’s level is the largest weight × its attenuation from a point) and PanWidth (0 gives the sounds it hears no pan). Changing Follow, Weight or PanWidth eases Pepper’s levels over 100 ms.
BeatScheduler
Namespace: AudioSystem
Inheritance: MonoBehaviour
File: RunTime/Utilities/BeatScheduler.cs
Description: Fires an AudioEvent on a tempo grid (BPM) whose rate can be changed at runtime without affecting pitch. Each beat is a fresh one-shot post of the event asset (AudioEvent.Post with the beat’s grid time as its scheduled start), not a time-stretch of a continuous clip. Timing is anchored to AudioSettings.dspTime so cadence is stable under frame-rate fluctuation.
Use cases: heartbeats, metronomes, ticking clocks, pulsing ambiences, sonar pings — any rhythmic SFX whose rate must vary independently of pitch.
Inspector Fields
| Field | Type | Description |
|---|---|---|
audioEvent (Inspector: Audio Event) |
AudioEvent |
The AudioEvent asset posted each beat. Also the Event property. |
emitter |
GameObject |
Optional emitter used for the posted event’s source and position. Defaults to the BeatScheduler’s own GameObject. |
bpm |
float (1–600) |
Beats per minute. Also writable at runtime via the Bpm property. |
maxCatchUpBeatsPerFrame |
int (1–16) |
Cap on OnBeat callbacks per Update if the scheduler falls behind (frame hitch, very high BPM). Audio always plays at most ONCE per Update regardless of this value — overdue audio beats are dropped to prevent voice-stacking thump. Only affects OnBeat consumers that count beats for game logic (every 4th beat trigger X), keeping them synchronized with the grid across hitches. |
playOnEnable |
bool |
Start scheduling automatically when enabled. |
fireImmediatelyOnStart |
bool |
Fire the first beat on the next frame (true) or wait one interval (false). |
Properties
public AudioEvent Event { get; set; } // The event posted each beat (the Audio Event field)
public float Bpm { get; set; } // Clamp-guarded, min 1. Setter applies to the next beat onward.
public bool IsRunning { get; }
public double BeatInterval { get; } // 60 / Bpm, in seconds
Methods
public void StartScheduling(); // Safe to call while already running (no-op).
public void StopScheduling(); // Stops firing. Does not stop voices already posted.
public void ResyncPhase(); // Reset phase: next beat fires at now + BeatInterval.
Events
public event System.Action OnBeat; // Fired on the main thread on every beat, after the event is posted.
Exceptions thrown by subscribers are caught and logged — they will not break the scheduler.
Example
using UnityEngine;
using AudioSystem;
public class StressHeartbeat : MonoBehaviour
{
[SerializeField] private BeatScheduler heartbeat;
[SerializeField] private float calmBpm = 60f;
[SerializeField] private float panicBpm = 160f;
[Range(0f, 1f)] public float stress;
private void Update()
{
heartbeat.Bpm = Mathf.Lerp(calmBpm, panicBpm, stress);
}
}
Notes
- Frame-level accuracy, not sample-level. Actual playback lands on the next audio callback (a few ms after the frame). For sample-accurate musical grid scheduling, use
AudioSource.PlayScheduleddirectly. - BPM changes apply to the next beat. The beat already queued is not rewound — this is deliberate so tempo ramps sound natural.
- Multiple schedulers are independent. Two
BeatSchedulercomponents at the same BPM are not phase-locked to each other. - See also: Manual §10.4 Beat-Scheduled SFX and Cookbook → Rhythmic SFX.
IRTPCListener (Interface)
public interface IRTPCListener
{
void OnRTPCChanged(string parameter, float value);
}
Description: Implement this interface to receive notifications when a parameter’s global value changes (SetRTPC, SetParameter, and each frame of a transition or global ramp), by name and synchronously. Emitter and play values don’t notify. Register via AudioManager.Instance.RegisterRTPCListener(). Nested in AudioManager (AudioManager.IRTPCListener).
From the Pepper.SFX 3.1 guides (API_REFERENCE.md). Something wrong or unclear? Tell me.