In this post, we will learn how to do a simple audio manager. This manager will be able to play an audio loop track and different background tracks randomly.

Introduction

Audio loop track : A list of sounds that buckle when assembled.

We use this mechanism to load a small amount in memory. For example, if our audio track weigh 3MB and that we want to play it, 3MB are loaded in memory ! Now, if you cut this file in 10 parts, each part weigh only 0,3MB. You will just need to play the next part at the end of the current part. Easy !

cut-audio-loop-track

Background track : A list of sounds that can be play one shot.

Each background tracks have the same behavior and run at the same time.

background-audio-tracks

AudioClip / AudioSource

An AudioClip stores the audio file either compressed as ogg vorbis or uncompressed. AudioClips are referenced and used by AudioSources to play sounds.

http://docs.unity3d.com/ScriptReference/AudioClip.html

In your project, you may have several folders containing hundred of sounds. Thanks to Unity editor, you can bind each sounds as an AudioClip in the inspector. We will need to have an AudioSource to be able to play that clip.

Several options can be add in our manager like setting the volume, the pan or the delay between two sounds.

Here’s a simple implementation of our extended sound class :

using System;
using System.Collections.Generic;
using System.Linq;
using Assets.Scripts.Managers;
using UnityEngine;

namespace Assets.Scripts.Models
{
    [Serializable]
    public class ExtendedSounds
    {
        public string Name;

        public float VolumeMin = 0f;
        public float VolumeMax = 1f;
        public float Volume;

        public float PanMin = -1f;
        public float PanMax = 1f;
        public float Pan;

        public float DelayMin = 2f;
        public float DelayMax = 10f;
        public float Delay;

        private float _currentDelay;

        public List<AudioClip> Clips;
        private List<GameObject> _sources;

        public ExtendedSounds()
        {
            Clips = new List<AudioClip>();
            _sources = new List<GameObject>();
        }

        public void UpdateCurrentDelay(float delay)
        {
            _currentDelay += delay;
        }

        public bool CanPlay()
        {
            if (_sources != null && _sources.Count > 1 && _currentDelay >= Delay)
                return true;
            return false;
        }

        /// <summary>
        /// Play a sound randomly
        /// </summary>
        public void RandomPlay()
        {
            Volume = UnityEngine.Random.Range(VolumeMin, VolumeMax);
            Pan = UnityEngine.Random.Range(PanMin, PanMax);

            var soundIndex = UnityEngine.Random.Range(0, _sources.Count);
            var source = _sources.ElementAtOrDefault(soundIndex);
            if (source != null)
            {
                source.GetComponent<AudioSource>().pan = Pan;
                source.GetComponent<AudioSource>().volume = Volume;
                source.Play();
            }
            _currentDelay = 0;
        }

        public void AddSource(GameObject soundObject)
        {
            _sources.Add(soundObject);
        }
    }
}

This class allows us to drag & drop all the sounds we want and to edit their parameters in the inspector very easily. It can be use for audio loop track and for audio background tracks.

Manager

using System;
using System.Collections.Generic;
using System.Linq;
using Assets.Scripts.Models;
using UnityEngine;
using Random = UnityEngine.Random;

namespace Assets.Scripts.Managers
{
    [Serializable]
    public class SoundManager : MonoBehaviour
    {
        #region Properties

        public List<ExtendedSounds> BackgroundSounds;

        public ExtendedSounds LoopingClipSounds;

        private List<GameObject> _loopingSounds;
        private GameObject _currentLoopingSound;
        private int _currentLoopingSoundIndex;

        public bool IsLoopingSoundsActive;
        public bool IsBackgroundSoundsActive;

        public GameObject SoundPrefab;

        #endregion

        #region Initialization

        public void Awake()
        {
            if (_loopingSounds == null)
                _loopingSounds = new List<GameObject>();

            if (BackgroundSounds == null)
                BackgroundSounds = new List<ExtendedSounds>();
        }

        public void Start()
        {
            foreach (var loopingClipSound in LoopingClipSounds.Clips)
            {
                GameObject soundObject = Instantiate(SoundPrefab) as GameObject;
                if (soundObject != null)
                {
                    soundObject.GetComponent<AudioSource>().clip = loopingClipSound;
                    _loopingSounds.Add(soundObject);
                }
            }

            InitializeBackgroundSounds();
        }

        private void InitializeBackgroundSounds()
        {
            foreach (var backgroundSound in BackgroundSounds)
            {
                foreach (var clip in backgroundSound.Clips)
                {
                    GameObject soundObject = Instantiate(SoundPrefab) as GameObject;
                    if (soundObject != null)
                    {
                        soundObject.GetComponent<AudioSource>().clip = clip;
                        backgroundSound.AddSource(soundObject);
                    }
                }
            }
        }

        #endregion

        #region Update

        public void Update()
        {
            if (IsLoopingSoundsActive && _loopingSounds.Count > 0)
            {
                var needToPlay = false;

                // No current sound
                if (_currentLoopingSound == null)
                {
                    _currentLoopingSoundIndex = 0;
                    needToPlay = true;
                }
                else
                {
                    // Need to play the next sound
                    if (!_currentLoopingSound.GetComponent<AudioSource>().isPlaying)
                    {
                        needToPlay = true;
                        _currentLoopingSoundIndex++;

                        // Loop to the first item
                        if (_currentLoopingSoundIndex >= _loopingSounds.Count)
                        {
                            _currentLoopingSoundIndex = 0;
                        }
                    }
                }

                if (needToPlay)
                {
                    _currentLoopingSound = _loopingSounds.ElementAtOrDefault(_currentLoopingSoundIndex);
                    if (_currentLoopingSound != null)
                    {
                        _currentLoopingSound.GetComponent<AudioSource>().pan = LoopingClipSounds.Pan;
                        _currentLoopingSound.GetComponent<AudioSource>().volume = LoopingClipSounds.Volume;
                        _currentLoopingSound.Play();
                    }
                }
            }
            else
            {
                // Pause current sound
                if (_currentLoopingSound != null)
                    _currentLoopingSound.GetComponent<AudioSource>().Pause();
            }

            if (IsBackgroundSoundsActive)
            {
                foreach (var backgroundSound in BackgroundSounds)
                {
                    if (backgroundSound.Delay <= 0f)
                    {
                        backgroundSound.Delay = Random.Range(backgroundSound.DelayMin, backgroundSound.DelayMax);
                    }
                    else
                    {
                        backgroundSound.UpdateCurrentDelay(Time.deltaTime);

                        if (!backgroundSound.CanPlay())
                            continue;

                        backgroundSound.Delay = Random.Range(backgroundSound.DelayMin, backgroundSound.DelayMax);
                        backgroundSound.RandomPlay();
                    }
                }
            }
        }

        #endregion
    }
}
  • public GameObject SoundPrefab : A simple object with an AudioSource component attached.

It’s done, just create a new object in the scene and attach the script SimpleAudioManager.cs. Bind the prefab and your audio files.

Press play ;)