Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

[Tutorial] Audio Manager

Discussion in 'Community Learning & Teaching' started by raiden, Nov 27, 2013.

  1. raiden

    raiden

    Joined:
    Feb 8, 2009
    Posts:
    333
    Hi all :)

    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
     
  2. olie8

    olie8

    Joined:
    Sep 12, 2014
    Posts:
    3
    Awesome raiden, thanks for the tutorial and for sharing, helped me a lot of my sound problem management :D
     
  3. nicmar

    nicmar

    Joined:
    Oct 25, 2014
    Posts:
    14
    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:
    Code (JavaScript):
    1. 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! :)
     
  4. nicmar

    nicmar

    Joined:
    Oct 25, 2014
    Posts:
    14
    Oh yeah, one more thing. I got an error on line 63:
    Code (CSharp):
    1.            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!
     
  5. raiden

    raiden

    Joined:
    Feb 8, 2009
    Posts:
    333
    Great Job nicmar! Thanks for sharing that.

    -Raiden
     
  6. nicmar

    nicmar

    Joined:
    Oct 25, 2014
    Posts:
    14
    Hi Raiden, thanks :)

    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:

    Code (CSharp):
    1. BaseManager.globalGameMusic = AudioHelper.CreateGetFadeAudioObject
    2. (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? :)
     
  7. raiden

    raiden

    Joined:
    Feb 8, 2009
    Posts:
    333
    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
     
  8. nicmar

    nicmar

    Joined:
    Oct 25, 2014
    Posts:
    14
    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. :)
     
  9. nicmar

    nicmar

    Joined:
    Oct 25, 2014
    Posts:
    14
    Hi Raiden,

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

    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:
    Code (CSharp):
    1. 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!
     
  10. raiden

    raiden

    Joined:
    Feb 8, 2009
    Posts:
    333
    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.

    Code (CSharp):
    1. // over loads
    2.     public static GameObject CreateGetFadeAudioObject(AudioClip aClip) {
    3.         return CreateGetFadeAudioObject(aClip, true);
    4.     }  
    5.     public static GameObject CreateGetFadeAudioObject(AudioClip aClip, bool dLoop) {
    6.         return CreateGetFadeAudioObject(aClip, dLoop, null);
    7.     }
    8.     public static GameObject CreateGetFadeAudioObject(AudioClip aClip, bool dLoop, AnimationClip fadeClip) {
    9.         return CreateGetFadeAudioObject(aClip, dLoop, fadeClip, "ReturnedAudioPlayObject");
    10.     }
    11.     // Additions for fading pitch
    12.     public static GameObject CreateGetFadeAudioObject(AudioClip aClip, bool dLoop, AnimationClip fadeClip, AnimationClip fadePitch) {
    13.         return CreateGetFadeAudioObject(aClip, dLoop, fadeClip, fadePitch, "ReturnedAudioPlayObject");
    14.     }
    15.    
    16.     // creates/returns a dynamic Audio object to position and plays in the world with loop
    17.     public static GameObject CreateGetFadeAudioObject(AudioClip aClip, bool dLoop, AnimationClip fadeClip, AnimationClip fadePitch, string objName)
    18.     {
    19.         // instance a new gameobject
    20.         GameObject apObject = new GameObject(objName);
    21.         // position the object in the world
    22.         apObject.transform.position = Vector3.zero;
    23.         // add our DontDestroyOnLoad
    24.         DontDestroyOnLoad(apObject);      
    25.         // add an AudioSource component
    26.         apObject.AddComponent<AudioSource>();
    27.         // return this script for use
    28.         AudioSource apAudio = apObject.GetComponent<AudioSource>();
    29.         // initialize some AudioSource fields
    30.         apAudio.playOnAwake = false;
    31.         apAudio.rolloffMode = AudioRolloffMode.Linear;
    32.        
    33.         apAudio.loop = dLoop;      
    34.         apAudio.clip = aClip;
    35.         apAudio.volume = 1.0f; // default
    36.        
    37.         if (fadeClip != null)
    38.         {
    39.             Animation apAnim = apObject.AddComponent<Animation>();
    40.             apAnim.AddClip(fadeClip, fadeClip.name);
    41.             apAnim.AddClip(fadePitch, fadePitch.name);
    42.             apAnim.clip = fadeClip;
    43.             apAnim.playAutomatically = false;
    44.         }
    45.            
    46.        
    47.         // return our AudioObject
    48.         return apObject;
    49.     }
    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:

    Code (CSharp):
    1. // 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)
    2.     public static IEnumerator PitchAudioObject(GameObject aObject, float pitchSpeed)
    3.     {
    4.         Animation apAnim = aObject.GetComponent<Animation>();
    5.         AudioSource aSource = aObject.GetComponent<AudioSource>();
    6.  
    7.  
    8.         // we are not a fade audio object
    9.         if (apAnim == null)
    10.         {
    11.             // we simply destroy the object and return
    12.             if (pitchSpeed <= 0)
    13.             {
    14.                 Destroy (aObject);
    15.             }
    16.  
    17.             // we are a positive playing sound, so just play it
    18.             if (pitchSpeed > 0 && aSource != null)
    19.             {
    20.                 aSource.Play ();
    21.             }
    22.  
    23.             return true;
    24.         }
    25.  
    26.         // animation clip is default to pitch out (1 to 2), these will look reveresed but they are correct
    27.         if (pitchSpeed < 0)
    28.         {
    29.             apAnim[apAnim.clip[1].name].time = apAnim[apAnim.clip[1].name].length;
    30.         }
    31.         else
    32.         {
    33.             apAnim[apAnim.clip[1].name].time = 0;
    34.         }
    35.        
    36.         // set our speed
    37.         apAnim[apAnim.clip[1].name].speed = pitchSpeed;
    38.        
    39.         // play the audio
    40.         if (aSource.isPlaying == false)
    41.         {
    42.             aSource.Play();
    43.         }      
    44.        
    45.         // play the fade
    46.         apAnim.Play();
    47.    
    48.         // yield the length of the clip
    49.         if (fadeSpeed < 0)
    50.         {
    51.             while(apAnim.isPlaying) { yield return new WaitForEndOfFrame(); }
    52.            
    53.             Destroy (aObject);
    54.         }  
    55.     }
    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:

    Code (CSharp):
    1. {
    2.         int cnt = 0;
    3.  
    4.         foreach (var at in GameObject.FindObjectsOfType(typeof(AudioSource)) as AudioSource[])
    5.         {
    6.             at.volume = (audioVolumes.Count > 0) ? audioVolumes[cnt] : 1.0f;
    7.             cnt++;
    8.         }
    9.     }
    10.  
    11.     public static void DisableAllAudio()
    12.     {
    13.         audioVolumes = new List<float>();
    14.  
    15.         foreach (var at in GameObject.FindObjectsOfType(typeof(AudioSource)) as AudioSource[])
    16.         {
    17.             audioVolumes.Add(at.volume);
    18.  
    19.             at.volume = 0.0f;
    20.         }
    21.     }
    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
     
  11. codeedward

    codeedward

    Joined:
    Dec 19, 2014
    Posts:
    93
    Thank You @raiden, Your code is very helpful! :)