Skip to main content

Audio System

OpenMon's audio system is a pool-based 2D audio engine built on Unity's AudioMixer. It is split into two layers: the Foundation layer (OpenMon.Foundation.Audio) provides the generic playback engine, and the Core layer (OpenMon.MCE.Audio) adds game-specific volume settings and persistence.

Architecture Overview

ClassLayerPurpose
AudioManagerFoundationSingleton that owns an AudioSource pool and plays/stops/fades audio clips
AudioLibraryFoundationScriptableObject registry mapping every AudioClip to its AudioMixerGroup
AudioReferenceFoundationLightweight serializable struct that references a clip by name
AudioSettingsManagerCoreReads/writes the player's volume preferences and applies them to the mixer
AudioConfigurationCoreSerializable data class holding Master, Music, and FX volume levels
AudioInstallerFoundationZenject ScriptableObjectInstaller that binds IAudioManager and IAudioLibrary
AudioSettingsInstallerCoreZenject installer that binds AudioSettingsManager and the AudioMixer

AudioManager

AudioManager is a singleton (Singleton<AudioManager>) that implements IAudioManager. It manages a dynamic pool of AudioSource components on its own GameObject. When you request playback the manager either reuses a free source from the pool or creates a new one on demand.

Playing Audio

// Simple one-shot playback
AudioManager.Instance.PlayAudio(myAudioReference);

// Looping BGM with fade-in
AudioManager.Instance.PlayAudio(bgmReference, loop: true, fadeTime: 1.5f);

// Custom pitch and volume
AudioManager.Instance.PlayAudio(sfxReference, pitch: 1.2f, volume: 0.8f);

Each call resolves the AudioReference name through the AudioLibrary, retrieves the clip and its mixer group, then assigns them to a pooled AudioSource. When fadeTime > 0, the manager runs a coroutine that lerps the source volume from 0 to the target over the specified duration.

Stopping Audio

// Instant stop
AudioManager.Instance.StopAudio(bgmReference);

// Fade out over 2 seconds, then stop
AudioManager.Instance.StopAudio(bgmReference, fadeTime: 2f);

// Emergency stop-all (e.g., abort battle)
AudioManager.Instance.StopAllAudios();

Global Mute / Unmute

These coroutines operate on the MasterVolume parameter of the AudioMixer, allowing you to mute the entire game output without stopping individual sources. This is useful for scene transitions and pause screens.

// Fade the master bus to silence over 1 second
StartCoroutine(AudioManager.Instance.MuteAllAudios(fadeTime: 1f));

// Restore to the previous volume
StartCoroutine(AudioManager.Instance.UnmuteAllAudios(fadeTime: 1f));

Pool Lifecycle

The pool is self-managing. Every frame, CheckAudioSourceAvailability() scans all sources. Any source that has stopped playing is marked as available and its clip reference is cleared. If no other source is still using that clip, the library is notified via FreeAudioAsset() to allow future addressable-based unloading.

AudioLibrary

AudioLibrary is a ScriptableObject asset (create via OpenMon > Audio > Audio Library) that acts as the central audio registry. It stores a SerializableDictionary<AudioClip, AudioMixerGroup> that maps every clip in the game to the mixer group it should be routed through (e.g., BGM, SFX, ME).

AudioLibrary (ScriptableObject)
+-- "battle_bgm_01" -> BGM MixerGroup
+-- "ui_confirm" -> SFX MixerGroup
+-- "level_up_jingle" -> ME MixerGroup
+-- "cry_001" -> SFX MixerGroup
...

At edit time, the library caches flat lists of clip names, clip references, and mixer groups for fast O(n) lookup by name. The context menu command Cache Audio References rebuilds these caches after you add or remove clips.

Editor Utility

The importer pipeline can register clips automatically:

audioLibrary.AddAudioIfItsNotIn(clip, mixerGroup);

AudioReference

AudioReference is a simple serializable struct with a single string Audio field. It matches the clip name as registered in the AudioLibrary. This decouples serialized scene/prefab data from direct clip references, which means you can reorganize audio files without breaking inspector references.

[Serializable]
public struct AudioReference
{
public string Audio; // Must match a clip name in AudioLibrary
}

Use AudioReference fields on your MonoBehaviours, ScriptableObjects, or CommandGraph nodes to reference audio by name.

AudioSettingsManager

AudioSettingsManager is the game-specific volume controller. It reads AudioConfiguration from the persistent configuration system and applies three volume levels to the AudioMixer:

  • MasterVolume -- controls overall game volume
  • MusicVolume -- controls the BGM mixer group
  • FXVolume -- controls sound effects and jingles

Volume Conversion

Human perception of loudness is logarithmic. The system uses AudioUtils to convert between the linear 0--1 slider range and the decibel scale expected by Unity's AudioMixer:

// Linear (0-1) -> Decibels (for AudioMixer.SetFloat)
float db = AudioUtils.LinearVolumeToLogarithmicVolume(0.5f); // ~ -6 dB

// Decibels -> Linear (for UI sliders)
float linear = AudioUtils.LogarithmicVolumeToLinearVolume(-6f); // ~ 0.5

Persisting Settings

Volume preferences are saved via the IConfigurationManager system. When the player adjusts a slider in the options screen, the UI calls:

audioSettingsManager.SetAudioConfiguration(new AudioConfiguration
{
MasterVolume = masterSlider.value,
MusicVolume = musicSlider.value,
FXVolume = fxSlider.value
});

This writes the config to disk and immediately applies the new values to the mixer. On startup, AudioSettingsManager.Construct() is called via Zenject injection and applies the saved settings on the next frame.

Dependency Injection Setup

Both installers are ScriptableObject-based Zenject installers added to your scene's SceneContext:

  1. AudioInstaller -- binds IAudioManager (from AudioManager.Instance), IAudioLibrary (from the AudioLibrary asset), and the AudioMixer.
  2. AudioSettingsInstaller -- binds AudioSettingsManager as a lazy singleton and injects the AudioMixer into it.
SceneContext
+-- AudioInstaller (references AudioLibrary asset + AudioMixer)
+-- AudioSettingsInstaller (references AudioMixer)

Using Audio in CommandGraph

Audio playback can be triggered from NPC event scripts (CommandGraph). Use the audio-related command nodes to play BGM, SFX, or jingles during cutscenes and dialog sequences. The nodes reference audio via AudioReference fields, which resolve through the same AudioLibrary at runtime.

Summary

Want to...Use...
Play a sound effectAudioManager.Instance.PlayAudio(ref)
Loop background musicAudioManager.Instance.PlayAudio(ref, loop: true)
Crossfade to new BGMStop old with fadeTime, play new with fadeTime
Mute during scene loadMuteAllAudios(fadeTime) / UnmuteAllAudios(fadeTime)
Register a new clipAdd it to the AudioLibrary asset and assign a mixer group
Adjust player volumeAudioSettingsManager.SetAudioConfiguration(config)
Convert slider to dBAudioUtils.LinearVolumeToLogarithmicVolume(value)