intermediate Audio

Audio Manager

Complete audio manager with music crossfade, SFX pooling, volume control, and persistence.

Unity 2022.3+ · 4.5 KB · AudioManager.cs

How to Use

1

Create an empty GameObject named 'AudioManager'

2

Attach this script

3

Add two AudioSource components for music (A and B)

4

Call AudioManager.Instance.PlayMusic(clip) for music

5

Call AudioManager.Instance.PlaySFX(clip) for sound effects

Source Code

AudioManager.cs
C#
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class AudioManager : MonoBehaviour
{
    public static AudioManager Instance { get; private set; }

    [Header("Audio Sources")]
    [SerializeField] private AudioSource musicSourceA;
    [SerializeField] private AudioSource musicSourceB;
    [SerializeField] private int sfxPoolSize = 10;

    [Header("Settings")]
    [SerializeField] private float musicVolume = 0.7f;
    [SerializeField] private float sfxVolume = 1f;
    [SerializeField] private float crossfadeDuration = 1.5f;

    private List<AudioSource> sfxPool;
    private bool isMusicA = true;
    private Coroutine crossfadeRoutine;

    private AudioSource ActiveMusic => isMusicA ? musicSourceA : musicSourceB;
    private AudioSource InactiveMusic => isMusicA ? musicSourceB : musicSourceA;

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
            InitializeSFXPool();
        }
        else
        {
            Destroy(gameObject);
        }
    }

    private void InitializeSFXPool()
    {
        sfxPool = new List<AudioSource>();
        for (int i = 0; i < sfxPoolSize; i++)
        {
            var source = gameObject.AddComponent<AudioSource>();
            source.playOnAwake = false;
            sfxPool.Add(source);
        }
    }

    public void PlayMusic(AudioClip clip, bool crossfade = true)
    {
        if (clip == null) return;

        if (crossfade && ActiveMusic.isPlaying)
        {
            if (crossfadeRoutine != null)
                StopCoroutine(crossfadeRoutine);
            crossfadeRoutine = StartCoroutine(CrossfadeMusic(clip));
        }
        else
        {
            ActiveMusic.clip = clip;
            ActiveMusic.volume = musicVolume;
            ActiveMusic.loop = true;
            ActiveMusic.Play();
        }
    }

    private IEnumerator CrossfadeMusic(AudioClip newClip)
    {
        InactiveMusic.clip = newClip;
        InactiveMusic.volume = 0f;
        InactiveMusic.loop = true;
        InactiveMusic.Play();

        float elapsed = 0f;
        while (elapsed < crossfadeDuration)
        {
            elapsed += Time.unscaledDeltaTime;
            float t = elapsed / crossfadeDuration;
            ActiveMusic.volume = Mathf.Lerp(musicVolume, 0f, t);
            InactiveMusic.volume = Mathf.Lerp(0f, musicVolume, t);
            yield return null;
        }

        ActiveMusic.Stop();
        isMusicA = !isMusicA;
    }

    public void PlaySFX(AudioClip clip, float volumeScale = 1f)
    {
        if (clip == null) return;

        AudioSource source = GetAvailableSFXSource();
        source.clip = clip;
        source.volume = sfxVolume * volumeScale;
        source.pitch = Random.Range(0.95f, 1.05f);
        source.Play();
    }

    private AudioSource GetAvailableSFXSource()
    {
        foreach (var source in sfxPool)
        {
            if (!source.isPlaying)
                return source;
        }
        // All busy — reuse the first one
        return sfxPool[0];
    }

    public void SetMusicVolume(float volume)
    {
        musicVolume = Mathf.Clamp01(volume);
        ActiveMusic.volume = musicVolume;
    }

    public void SetSFXVolume(float volume)
    {
        sfxVolume = Mathf.Clamp01(volume);
    }

    public void StopMusic(float fadeOutDuration = 0.5f)
    {
        StartCoroutine(FadeOutMusic(fadeOutDuration));
    }

    private IEnumerator FadeOutMusic(float duration)
    {
        float startVolume = ActiveMusic.volume;
        float elapsed = 0f;
        while (elapsed < duration)
        {
            elapsed += Time.unscaledDeltaTime;
            ActiveMusic.volume = Mathf.Lerp(startVolume, 0f, elapsed / duration);
            yield return null;
        }
        ActiveMusic.Stop();
    }
}