[Tutorial] Audio Manager

Hi all :slight_smile:

It’s been awhile, but recently I wrote a really handy audio manager script, and I decided to make a tutorial on how you can create the same thing, and use it in your game.

First I think it’s important to mention the pros and cons about the script:

Pro’s:

  • Dynamically create audio objects that automatically handle creating the audio object, playing the audio, and destroying the object after the audio has finished
  • Seamless cross fading of music from one scene to another

Con’s:

  • Requires static fields for reference
  • Creates objects at world position (0,0,0), so it needs an audio listener at that position also

You can access the tutorial from the following links:

AudioManagerTutorial.pdf
AudioManagerTutorial.unitypackage

If you wanna skip the tutorial and have the full example, here is the link to it:

AudioManager/AudioManagerTutorial(Full).unitypackage

I have used the script in my last 2 games successfully, so I realIy believe you will find this useful.

If you have any comments, questions or concerns, please let me know.

-Raiden

Awesome raiden, thanks for the tutorial and for sharing, helped me a lot of my sound problem management :smile:

Very nice tutorial and script, thanks a lot!

It works fine when using pure C#, but since my other scripts are JS, I had trouble calling it using:

AudioHelper.CreatePlayAudioObject (sfxLerpzScream);

I got “Unknown identifier: ‘AudioHelper’”.

However, I placed the AudioHelper.cs in a folder called “Plugins” in the root of the Assets folder, which makes it compile the C# first, and JS will have access to it, and it works perfectly.

I hope this helps someone with the same problem as me! :slight_smile:

Oh yeah, one more thing. I got an error on line 63:

           return true;

“Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration.”

I commented the code, and it seems to work anyhow. I have however not tried looping or fading sounds, but I don’t see what this return should do. I’d be happy to know though!

Great Job nicmar! Thanks for sharing that.

-Raiden

Hi Raiden, thanks :slight_smile:

I tried with the fading thing, and had a little problems with the naming of the object, animation, and get this error:

Basically it’s something with the GameSceneManager.cs. In your tutorial, you used IntroSceneManager, but I replaced the variables for the gamescene, like this:

BaseManager.globalGameMusic = AudioHelper.CreateGetFadeAudioObject
(BaseManager.instance.gameMusic, true, BaseManager.instance.fadeClip, "AudioFadeAnimation");

It used to say “Audio-Intromusic” in the end, but I didn’t understand where that came from.

Actually I don’t have multiple levels/scenes but I just wanted to play a music, and possible add more music later, but the error is confusing me. What did I miss? :slight_smile:

Hi nicmar, it looks like you are possibly missing the animation clip added to the exposed field in BaseManager, make sure your drag and drop the AudioFade clip in to the exposed field, this is the only way an Animation Component will be added to the game object created for your audio.

Hope this helps you out.

-Raiden

Thanks, that was it! However, I got the message “The AnimationClip ‘AudioFadeAnimation’ used by the Animation component ‘AudioFadeAnimation’ must be marked as Legacy.” but I figured it out by Googling that I had to change “Animation Type” from 2 to 1.

Not sure what it actually means, but it seems to work. :slight_smile:

Hi Raiden,

I’m back, with some new questions and errors - I hope you can help me. Here are my questions :slight_smile:

  1. When I start Unity, I get this message:
    “Animation clip ‘AudioFadeAnimation2’ is not retargetable. Animation clips used within the Animator Controller need to have Muscle Definition set up in the Asset Importer Inspector”
    I renamed what you called ‘AudioFade’. The error message disappears when I run though. Is this something bad?

  2. Can I change the pitch of the GameMusic, and nothing else? I want the music to get faster and faster as the game goes on. I see I can use audio.pitch, but I’m not sure how to do it on the baseManager, and not affect the other sounds that are playing.

  3. Also I want to be able to mute it at certain points, but I guess it’s the same but using audio.volume instead.

  4. Also the game music fades in at start, but i can’t seem to control how it fades in. There was this code, but changing 0.25f doesn’t seem to do anything:

StartCoroutine (AudioHelper.FadeAudioObject (BaseManager.globalGameMusic, 0.25f));

Note: I only have one scene, so I won’t be needing switching between scenes and fading, however I want the Game Music to loop indefinitely.

Thank you for any help, it’s really appreciated!

Hi nicmar, it seems as though your creating an animation clip with an animator controller as well. This is a pretty old tutorial, before Mecanim, so I recorded a small video to show how the animation clip was created for the audio fade:

That should correct question #1, as for question 2, the tutorial does not support changing the pitch, the is set default at 1.0f, which is no change in the pitch of the sound. Check out the video and basically you could do the same for the pitch instead of the volume, and with some small changes to code, you can essentially alter the pitch of the audio.

// over loads
    public static GameObject CreateGetFadeAudioObject(AudioClip aClip) {
        return CreateGetFadeAudioObject(aClip, true);
    }   
    public static GameObject CreateGetFadeAudioObject(AudioClip aClip, bool dLoop) {
        return CreateGetFadeAudioObject(aClip, dLoop, null);
    }
    public static GameObject CreateGetFadeAudioObject(AudioClip aClip, bool dLoop, AnimationClip fadeClip) {
        return CreateGetFadeAudioObject(aClip, dLoop, fadeClip, "ReturnedAudioPlayObject");
    }
    // Additions for fading pitch
    public static GameObject CreateGetFadeAudioObject(AudioClip aClip, bool dLoop, AnimationClip fadeClip, AnimationClip fadePitch) {
        return CreateGetFadeAudioObject(aClip, dLoop, fadeClip, fadePitch, "ReturnedAudioPlayObject");
    }
   
    // creates/returns a dynamic Audio object to position and plays in the world with loop
    public static GameObject CreateGetFadeAudioObject(AudioClip aClip, bool dLoop, AnimationClip fadeClip, AnimationClip fadePitch, string objName)
    {
        // instance a new gameobject
        GameObject apObject = new GameObject(objName);
        // position the object in the world
        apObject.transform.position = Vector3.zero;
        // add our DontDestroyOnLoad
        DontDestroyOnLoad(apObject);       
        // add an AudioSource component
        apObject.AddComponent<AudioSource>();
        // return this script for use
        AudioSource apAudio = apObject.GetComponent<AudioSource>();
        // initialize some AudioSource fields
        apAudio.playOnAwake = false;
        apAudio.rolloffMode = AudioRolloffMode.Linear;
       
        apAudio.loop = dLoop;       
        apAudio.clip = aClip;
        apAudio.volume = 1.0f; // default
       
        if (fadeClip != null)
        {
            Animation apAnim = apObject.AddComponent<Animation>();
            apAnim.AddClip(fadeClip, fadeClip.name);
            apAnim.AddClip(fadePitch, fadePitch.name);
            apAnim.clip = fadeClip;
            apAnim.playAutomatically = false;
        }
           
       
        // return our AudioObject
        return apObject;
    }

Notice how we have added a new parameter “AnimationClip fadePitch” to pass through the methods, so now you can add a fading pitch animation clip to your animation component of your audio game object.

The final piece is very similar to FadeAudioObject, except we will add a new method in AudioHelper called: PitchAudioPbject like so:

// fade our AudioSource object based on speed (> 0 fades volume up, < 0 fades volume out, == 0 assumes the sound is playing and just destroys it)
    public static IEnumerator PitchAudioObject(GameObject aObject, float pitchSpeed)
    {
        Animation apAnim = aObject.GetComponent<Animation>();
        AudioSource aSource = aObject.GetComponent<AudioSource>();


        // we are not a fade audio object
        if (apAnim == null)
        {
            // we simply destroy the object and return
            if (pitchSpeed <= 0)
            {
                Destroy (aObject);
            }

            // we are a positive playing sound, so just play it
            if (pitchSpeed > 0 && aSource != null)
            {
                aSource.Play ();
            }

            return true;
        }

        // animation clip is default to pitch out (1 to 2), these will look reveresed but they are correct
        if (pitchSpeed < 0)
        {
            apAnim[apAnim.clip[1].name].time = apAnim[apAnim.clip[1].name].length;
        }
        else 
        {
            apAnim[apAnim.clip[1].name].time = 0;
        }
       
        // set our speed
        apAnim[apAnim.clip[1].name].speed = pitchSpeed;
       
        // play the audio
        if (aSource.isPlaying == false)
        {
            aSource.Play();
        }       
       
        // play the fade
        apAnim.Play();
   
        // yield the length of the clip
        if (fadeSpeed < 0)
        {
            while(apAnim.isPlaying) { yield return new WaitForEndOfFrame(); }
           
            Destroy (aObject);
        }   
    }

So basically when you add the to AudioHelper, you would call it similar to fading an audio object, except like so:

StartCoroutine (AudioHelper.PitchAudioObject (BaseManager.globalGameMusic, 0.05f));

This should give a very small upwards change in pitch over time.

For question #3: You can add a couple other methods to AudioHelper that will manage turning on and off all audio volume like so:

{
        int cnt = 0;

        foreach (var at in GameObject.FindObjectsOfType(typeof(AudioSource)) as AudioSource[])
        {
            at.volume = (audioVolumes.Count > 0) ? audioVolumes[cnt] : 1.0f;
            cnt++;
        }
    }

    public static void DisableAllAudio()
    {
        audioVolumes = new List<float>();

        foreach (var at in GameObject.FindObjectsOfType(typeof(AudioSource)) as AudioSource[])
        {
            audioVolumes.Add(at.volume);

            at.volume = 0.0f;
        }
    }

When your ready to mute your volume, simply call: AudioHelper.DisableAllAudio(); and when you want to turn it back on, call: AudioHelper.EnableAllAudio();

For question #4: You can contra the speed by changing the -0.25 to a different number, for example to fade out faster, change the -0.25f to -0.5f, or even -1, to fade volume up, change it to a positive higher number like: 0.75f

Sorry for th elate reply, and I hope that helps you out.

-Raiden

Thank You @raiden , Your code is very helpful! :slight_smile: