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
| Class | Layer | Purpose |
|---|---|---|
AudioManager | Foundation | Singleton that owns an AudioSource pool and plays/stops/fades audio clips |
AudioLibrary | Foundation | ScriptableObject registry mapping every AudioClip to its AudioMixerGroup |
AudioReference | Foundation | Lightweight serializable struct that references a clip by name |
AudioSettingsManager | Core | Reads/writes the player's volume preferences and applies them to the mixer |
AudioConfiguration | Core | Serializable data class holding Master, Music, and FX volume levels |
AudioInstaller | Foundation | Zenject ScriptableObjectInstaller that binds IAudioManager and IAudioLibrary |
AudioSettingsInstaller | Core | Zenject 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:
- AudioInstaller -- binds
IAudioManager(fromAudioManager.Instance),IAudioLibrary(from theAudioLibraryasset), and theAudioMixer. - AudioSettingsInstaller -- binds
AudioSettingsManageras a lazy singleton and injects theAudioMixerinto 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 effect | AudioManager.Instance.PlayAudio(ref) |
| Loop background music | AudioManager.Instance.PlayAudio(ref, loop: true) |
| Crossfade to new BGM | Stop old with fadeTime, play new with fadeTime |
| Mute during scene load | MuteAllAudios(fadeTime) / UnmuteAllAudios(fadeTime) |
| Register a new clip | Add it to the AudioLibrary asset and assign a mixer group |
| Adjust player volume | AudioSettingsManager.SetAudioConfiguration(config) |
| Convert slider to dB | AudioUtils.LinearVolumeToLogarithmicVolume(value) |