docs
Systems
Presentation
AudioSystem
Key Components

Overview

The AudioSystem has three layers. AudioCommandFactory builds commands. AudioFlowController receives commands from the bus and passes them on. AudioManager and its helper classes do the work.

Main manager and coordinator classes

AudioManager

The central MonoBehaviour. A project subclass provides the language, volume preset, button handling, and apply-button handling.

Responsibilities:

  • Holds the singleton Instance, and keeps itself across scene loads.
  • Creates the AudioSourcePool and AudioClipLookup in Awake.
  • Routes incoming audio commands to playback, pause, stop, volume, and layered music methods.
  • Tracks music playback in AudioState records.
  • Applies volume presets through AudioMixerManager.
  • Forwards layered music calls to an ILayeredMusicPlayer.
  • Handles UI-domain events from ProcessEvent.

Serialized fields to set in the Inspector: AudioMixer, AudiosHolder, MusicHolder, LocalizedGroupAudiosHolder, PooledObjectsAudio, and PooledObjectsMusic.

Subclass requirements: GetCurrentLanguage, GetVolumePreset, HandleButtonClicked, and HandleApplyButtonClicked.

Setup order: SetAudioTweener and SetLayeredMusicPlayer are called by the project. Awake runs before those calls, so the pool and lookup exist when the first command arrives.

AudioFlowController

The bridge between the buses and AudioManager. A project subclass implements HandleCommand, typically by calling AudioManager.HandleCommand. The base class handles the Audio domain registration in FlowController.Init and the UI event subscription in its own Init.

It is registered through InitializationManager.RegisterSystems, using SafeRegister.

AudioMixerManager

A MonoBehaviour that wraps the Unity AudioMixer. It should be a child of the AudioManager GameObject. It exposes setters and getters for the Master, Music, and SFX parameters, in decibels.

AudioSourcePool

A plain class used by AudioManager. It creates pooled sources in its constructor, under the given parent transform. It does not decide when to play or stop; AudioManager makes those decisions.

AudioClipLookup

A plain class used by AudioManager. It builds the clip key lookup from the holder lists and resolves clips by key and language. It does not play audio.

AudioCommandFactory

A static class used by game code. It builds Audio-domain commands and sends them with BusUtility.Send. It does not hold any state.

Key event and data classes

  • AudioState: one record per tracked music clip. Created and updated by AudioManager. Read with GetAudioState.
  • VolumeMix: a set of SFX, Music, and Master values. Passed through commands and presets.
  • AudioConstants.AudioClipData: the per-clip settings: key, clip, volume, pitch, priority, loop, play on awake, and mixer group.
  • AudioClipHolder, LocalizedGroupAudioClipHolder, NonLocalizedGroupAudioClipHolder: ScriptableObjects that store clip data for lookup.
  • LocalizedAudioSlideGroup, LocalizedAudioVariant, AudioClipSubGroup: serializable data types that the holders use.
  • AudioParamKeys and ContractConstantsAudio: typed keys and string tokens for command parameters.
  • AudioCommandVerb, AudioScope, AudioSubject: enums that describe a command.

Commands do not carry events. Audio notifications from the playback layer, such as the clip-finished callback, are passed as Action<AudioClip> values. Those are stored in command parameters or passed as method arguments.

Handler and listener interfaces

  • IAudioTweener: fading contract. Implemented by the project.
  • ILayeredMusicPlayer: layered music backend contract. Implemented by the project.
  • ILayeredMusicDataStore: progress persistence contract. Implemented by the project.
  • IEventReceiver: implemented by AudioFlowController, so the initialization registry classifies it as an event receiver. The receiver is initialized before other sync systems.
  • ICommand: the command type AudioManager.HandleCommand accepts. It is defined outside this folder.

Interaction with dependency classes

  • AudioCommandFactory uses Command.Create, BusUtility.Send, and CoreParamKeys.ObjectValue. BusUtility.Send places the command on the CommandBus.
  • AudioFlowController gets the buses from ServiceLocator.GetBusRegistry, through FlowController and its own Init. It registers for the Audio domain through the base class and for UI events through the EventBus.
  • FlowController is the base class for AudioFlowController. Its Init registers the controller on the CommandBus.
  • InitializationManager creates the buses and runs Init on each registered system. AudioFlowController runs as an event receiver during the sync phase.
  • ParamKey<T> and Params carry typed values. AudioManager reads them with Has, Get, and TryGet.

Flow examples

Playing a sound effect with a callback:

  1. Game code calls AudioCommandFactory.PlayClipWithCallback with a clip key and a callback.
  2. The factory sends a Play command for the SFX subject, with the clip key and callback.
  3. AudioFlowController passes the command to AudioManager.HandleCommand.
  4. AudioManager calls PlayClip, then PlayAudio. It resolves the clip for the current language, gets a pooled source, and calls AudioPlaybackUtility.Play.
  5. When a non-looping clip ends, the callback runs with the clip, and the source is deactivated.

Restoring layered music:

  1. Game code calls AudioCommandFactory.RestoreMusicProgress.
  2. AudioManager calls RestoreMusicProgress, which calls LayeredMusicPlayer.RestoreProgress.
  3. The player reads progress from its data store, applies it, and starts playback.

Changing the volume preset:

  1. Game code calls AudioCommandFactory.SetVolume with a context, source, channel, and preset.
  2. AudioManager sets the context, then calls ApplyVolumePreset.
  3. If the source is Saved, the values come from GetVolumePreset. The mixer then receives the values for the selected channel through AudioMixerManager.

UI button events:

  1. The event bus delivers a UI event to AudioFlowController.OnEvent.
  2. AudioManager.ProcessEvent passes it to the UI handler.
  3. For a button click, HandleButtonClicked runs. The project implements it.