Thursday, March 28, 2013

Unity C# AudioManager Tutorial - Part 4 (Music, Pausing, & Voice Over)

This is part 4 of a 4 part tutorial to create an audio manager for unity. This tutorial focuses on playing music, pausing fx while keeping music playing, and controlling sound volume including fading.

Part 1 | Part 2 | Part 3 | Part 4

Playing Music

We'll need a couple more key concepts to play music, but the first is easy. We want music to loop, so we can just call the PlayLoop method with our music clip. In order to distinguish the music from other sounds playing, we will create a member variable m_activeMusic in the AudioManager that will hold a reference to this particular AudioSource. Unless otherwise noted, all code in this tutorial is in our AudioManager class.
    private AudioSource m_activeMusic;
    public AudioSource PlayMusic(AudioClip music, float volume) {
        m_activeMusic = PlayLoop(music, transform, volume);
        return m_activeMusic;
    }
Notice that no transform parameter is passed in to identify the music position. Here I am assuming that in game music always plays at its given volume, and is not affected by any object movement in the game. To achieve this, we can to attach the music sound to the transform of the AudioManager, which, as explained in part 1, is attached to the camera, giving it a constant distance from the audio listener of 0.

The PlayLoop method adds the music sound to our m_activeAudio, but with m_activeMusic we can track this particular sound special, which we will utilize in the next section, fx pause control.

Pause Control

Now that we have a list of active sounds in m_activeAudio, we can pause them on command. With our reference to the music, we can indicate that the pause function only pauses all active sounds that are NOT the music:
public void pauseFX() { 
    foreach (var audioClip in m_activeAudio) {
        try {
            if (audioClip.source != m_activeMusic) {
                audioClip.source.Pause();
            }
        } catch {
            continue;
        }
    }
}

public void unpauseFX() {
    foreach (var audioClip in m_activeAudio) {
        try {
            if (!audioClip.source.isPlaying) {
                audioClip.source.Play();
            }
        } catch {
            continue;
        }
    }
}
Fairly straight forward- when AudioManager.Instance.pauseFX() is called, the method cycles through each of the active sounds, and if the current iterator is not the music, it calls Unity's built in 'AudioSource.pause' function. The try/catch block is to catch any timing issues where the audio source is destroyed while this is iterating through the list. If there are any issues on a given element in the list, the method catches the error and simply skips that element, and next time we pause or unpause, the ClipInfo object that errored out on this pass should have been removed in our updateActiveAudio method from part 3.

When the unpauseFX function is called, it goes through the active sounds, and if any are not playing, it plays them. If the audio is not paused and not playing, Unity automatically resumes them from where they paused. If you have any issues where sounds are restarting unexpectedly, this is the place to check first.

Voice Overs

To make sure we can clearly hear a voiceover, we're going to make all sounds that are currently playing quieter while the voiceover is playing. Similar to music, we create a member variable that tracks the active voiceover that is playing. Also like music, we assume that the voiceover is full volume regardless of object positions in the world, so we attach the voice over to the AudioManager transform (the camera.) We're also going to need a volumeMod variable that holds the amount to reduce all the other sounds' volume, and set it low when the voice over is started.
private AudioSource m_activeVoiceOver;
private float m_volumeMod = 1.0f;
public AudioSource PlayVoiceOver(AudioClip voiceOver, float volume) {
    AudioSource source = Play(voiceOver, transform, volume);
    m_activeVoiceOver = source;
    m_volumeMod = 0.2f;
    return source;
}
Now that we have m_volumeMod, we can revisit our updateActiveAudio method to add volume modification to all sounds. Before we loop through the sounds, we will check if m_activeVoiceOver is set, and if it is not, we will set the volume mod to 1.0 (normal volume.) Otherwise, we will keep it's current value, which should be set at 0.2 (as in the PlayVoiceOver method above). Then, when iterating through the active audio, we will check if the audio is our m_activeVoiceOver, and if it is not, we will make the current volume its default volume multiplied by the volume modifier. This makes all sounds but the voice over affected by the volume mod:
private void updateActiveAudio() {
    var toRemove = new List<ClipInfo>();
    try {
        if (!m_activeVoiceOver) {
            m_volumeMod = 1.0f;
        }
        foreach (var audioClip in m_activeAudio) {
            if (!audioClip.source) {
                toRemove.Add(audioClip);
            } else if (audioClip.source != m_activeVoiceOver) {
                audioClip.source.volume = audioClip.defaultVolume * m_volumeMod;
            }
        }
    } catch {
       Debug.Log("Error updating active audio clips");
       return;
    }
    //cleanup
    foreach (var audioClip in toRemove) {
        m_activeAudio.Remove(audioClip);
    }
}
There you have it. You can extend this type of pausing/volume modification functionality to additional AudioSource variables if you have other types of sound events in your game. You can also use the concept behind this volumeMod variable to create an in-game master volume slider for the player.

Fading Sound

As a final bonus feature, we can make the voice over modification fade the other sounds in and out instead of just snapping to the volume modification. This is a more minor trick, and it requires a few new variables, but it has a pretty significant effect, though subtle in execution.

First we'll have to add two new variables. A min volume variable to establish how low the volume should go during voice overs, and a voice over fade bool to indicate if we are currently fading for the voice over or not. We'll initialize these new variables in our Awake method.
private float m_volumeMod, m_volumeMin;
private bool m_VOfade; //used to fade to quiet for VO

void Awake(){
    Debug.Log("AudioManager Initializing");
    try {
        transform.parent = GameObject.FindGameObjectWithTag("MainCamera").transform;
        transform.localPosition = new Vector3(0, 0, 0);
    } catch {
        Debug.Log("Unable to find main camera to put audiomanager");
    }
    m_activeAudio = new List<ClipInfo>();
    m_volumeMod = 1;
    m_volumeMin = 0.2f;
    m_VOfade = false;
    m_activeVoiceOver = null;
    m_activeMusic = null;
}
We will update our Update method to gradually decrease the volumeMod to our min volume if our VOfade bool is true, and gradually increase the volumeMod to 1 if our VOfade bool is false. We will have the PlayVoiceOver method activate our VOfade bool, and have the updateActiveAudio method check the m_activeVoiceOver AudioSource and deactivate VOfade if it is null (if the voiceover is not currently active):
void Update() {
    //fade volume for VO
    if (m_VOfade && m_volumeMod >= m_volumeMin) {
        m_volumeMod -= 0.1f;
    } else if (!m_VOfade && m_volumeMod < 1.0f) {
        m_volumeMod += 0.1f;
    }
    updateActiveAudio();
}

public AudioSource PlayVoiceOver(AudioClip voiceOver, float volume){
    AudioSource source = Play(voiceOver, transform, volume);
    m_activeVoiceOver = source;
    m_VOfade = true;
    return source;
}

private void updateActiveAudio() { 
    var toRemove = new List<ClipInfo>();
    try {
        if (!m_activeVoiceOver) {
            m_VOfade = false;
        }
        foreach (var audioClip in m_activeAudio) {
            if (!audioClip.source) {
                toRemove.Add(audioClip);
            } else if (audioClip.source != m_activeVoiceOver) {
                audioClip.source.volume = audioClip.defaultVolume * m_volumeMod;
            }
        }
    } catch {
        Debug.Log("Error updating active audio clips");
        return;
    }
    //cleanup
    foreach (var audioClip in toRemove) {
        m_activeAudio.Remove(audioClip);
    }
}
Note that I am adjusting m_volumeMod by 0.1 each update. This struck a nice balance for me between having it happen quickly enough to still be effective, but slow enough to keep a smoothness in the transition.

Conclusion

You've reached the end of the tutorial! You should now have a straightforward audio manager that acts as a singleton, allows clip playing and control from anywhere, tracks active sound clips, pauses select sounds, fades select sounds, and is simple to modify for further functionality. I hope this provided insight into a way to more easily manage audio in your projects. If you have any questions or feedback, please leave it in the comments, or feel free to contact me at baheard@gmail.com. Special thanks to Herman Tulleken and Daniel Rodriguez for their related posts.

Friday, March 22, 2013

Unity C# AudioManager Tutorial - Part 3 (Control)

This is part 3 of a 4 part tutorial to create an audio manager for unity. This tutorial builds on the structure developed in part 1, expanding the functionality described in part 2. It describes new Play options, and how to manage tracked audio clips.

Part 1 | Part 2 | Part 3 | Part 4

Overloading Play

The next step is to create a play method that plays a sound on top of an existing object, so when the object moves, the sound moves with it. This is useful for things such as a car with a siren, or a character who is walking while they talk. This is another core method, and similar to our previous play method. Again, the concept is referenced from Daniel Rodriguez's helpful post. Except where specified otherwise, all methods in this section of the tutorial are within our AudioManager class.
    
public AudioSource Play(AudioClip clip, Transform emitter, float volume) {
    var source = Play(clip, emitter.position, volume);
    source.transform.parent = emitter;
    return source;
}
Note that instead of a Vector3 parameter, we pass in the GameObject Transform that is emitting the moving sound. This method calls the default play method, creating an object at the emitter position and playing the sound there, but it then attaches the transform of the AudioSource (which is shared by its GameObject) to the emitter object by making the emitter its parent. Now the sound will follow the emitter where it goes. Remember that the original Play method registers the new sound object with the m_activeSounds collection, and destroys the GameObject after the length of the sound has played.

Looping Sound

Since the original play method destroys the sound object after the length of the clip, to loop we need a new method that won't destroy it. It also has to set the audiosource "loop" property to true. I assumed that any sound that loops will have an emitter, but if you needed a looping sound that played in one position in space, you could just remove the line that sets the transform.parent. This method is almost identical to the original except for these changes.
public AudioSource PlayLoop(AudioClip loop, Transform emitter, float volume) {
    //Create an empty game object
    GameObject movingSoundLoc = new GameObject("Audio: " + loop.name);
    movingSoundLoc.transform.position = emitter.position;
    movingSoundLoc.transform.parent = emitter;
    //Create the source
    AudioSource source = movingSoundLoc.AddComponent<AudioSource>();
    setSource(ref source, loop, volume);
    source.loop = true;
    source.Play();
    //Set the source as active
    m_activeAudio.Add(new ClipInfo{source = source, defaultVolume = volume});
    return source;
}
The setSource method becomes valuable now, because if we want to change any default settings of all the AudioSources that are created through the AudioManager, we only have to change it in the setSource method, and it will updated both this and the original Play method.

Stopping the Looping Sound

Now we've got a looping sound going on endlessly, we need to be able to stop it! This is a simple method that requires you to track the sound you'll want to stop at start time, and pass it in as a parameter when you're ready to stop it.
public void stopSound(AudioSource toStop) {
    try {
        Destroy(m_activeAudio.Find(s => s.source == toStop).source.gameObject);
    } catch {
        Debug.Log("Error trying to stop audio source "+toStop);
    }
}
This highlights why it is useful to have all the Play methods return the AudioSource created. Here is an example of a new Init class using the loop and stop methods:

public class Init : MonoBehaviour {
    //testSound is public in order to set in the inspector
    public AudioClip testSound; 
    private AudioSource playingSound;

    void Start(){
        playingSound = AudioManager.Instance.PlayLoop(testSound, m_enemy.transform, 1)
    }

    void Update(){
        if(Input.GetKeyDown(KeyCode.Space)){
            AudioManager.Instance.stopSound(playingSound);
        }
    }
}
Note that since loop sounds are assigned to a parent on creation, if that parent is destroyed for any reason, the sound will be destroyed with it automatically.

Cleaning Up the Audio List

For the final section of this tutorial, I will describe the mechanism for cleaning up inactive ClipInfo objects from our m_activeAudio list. This method will also be key later to control the sounds dynamically.
private void updateActiveAudio() { 
    var toRemove = new List<ClipInfo>();
    try {
        foreach (var audioClip in m_activeAudio) {
            if (!audioClip.source) {
                toRemove.Add(audioClip);
            } 
        }
    } catch {
        Debug.Log("Error updating active audio clips");
        return;
    }
    //cleanup
    foreach (var audioClip in toRemove) {
        m_activeAudio.Remove(audioClip);
    }
}
The method creates an empty list of ClipInfo to remove. We can't remove them in the foreach loops since it would illegally modify the iterator. The method then goes through all the ClipInfos in m_activeAudio and checks each one's AudioSource. In the Play method the AudioSource was set to destroy after playing, so after the sound is finished, the AudioSource source of the ClipInfo will be null. If it is null, we add it to the toRemove list to be removed. After going through all active sounds, we go through all the toRemove ClipInfos and remove them from the m_activeAudio list. Since the ClipInfo list is not directly tied to the AudioSource it holds, this will work no matter how the AudioSource is destroyed (time or parent death.)

Lastly we have to call this in the AudioManager Update() method so that it will be called continuously.
void Update() {
    updateActiveAudio();
}

Next time...

We now have a working AudioManager that will play a sound at a location, play a sound at an emitter, play a looping sound, stop a looping sound, track all active sounds, and clean them up as they finish. In part 4, I will show how this setup allows for dynamic centralized control, including implementing music, pausing non-music sounds, global volume control, and select volume control (for things like voice overs.)

Friday, March 15, 2013

Unity C# AudioManager Tutorial - Part 2 (Play)

This is part 2 of a 4 part tutorial to create an audio manager for unity. This tutorial focuses on creating the main methods that will play sounds, as will as tracking and managing active sounds.

Part 1 | Part 2 | Part 3 | Part 4

AudioManager - ClipInfo Object

We will first create a ClipInfo object as a new data type to hold all the relevant data about each sound that is handled by the audio manager. Right now it contains only variables for the sound clip to play and its initial volume, but it could be expanded to hold any information you want to keep track of, such as the time the clip started playing, or how many times it has looped. We will add a member variable List of this ClipInfo to our AudioManager to track all audio playing, and we will update our Awake method to initialize this List.
public class AudioManager : Singleton<AudioManager> {
    class ClipInfo
    {
       //ClipInfo used to maintain default audio source info
       public AudioSource source { get; set; }
       public float defaultVolume { get; set; }
    }

    private List<ClipInfo> m_activeAudio;

    void Awake(){
       m_activeAudio = new List<ClipInfo>();
       
       [see previous tutorial]
    }
}
First note that the ClipInfo class is inside the audio manager. This allows it to be accessed only by the audio manager, hiding the data. You can read the rules on nesting classes here.

Second, notice that ClipInfo holds the AudioSource directly, ignoring the GameObject that the source must be attached to.

Lastly, we create a 'defaultVolume' property so that we can change the volume of the audio source dynamically, but remember what volume it was created at. More on this in part 4.

The Play Method

For the core functionality of this AudioManager, I’ve referenced Daniel Rodriguez for the basic idea (I also recommend his post on setting up managers for your unity projects.) The Play method of the AudioManager will:
  1. Create a new GameObject.
  2. Set its AudioSource to be our given sound clip.
  3. Play the AudioSource.
  4. Destroy the object.
The beauty of this is that we can play a given audio clip at any time by writing AudioManager.Instance.Play(parameters). We don’t have to worry about creating the AudioManager since it is a singleton, we don’t have to worry about creating and destroying the accompanying GameObjects because everything is handled within the method.
    public AudioSource Play(AudioClip clip, Vector3 soundOrigin, float volume) {
       //Create an empty game object
       GameObject soundLoc = new GameObject("Audio: " + clip.name);
       soundLoc.transform.position = soundOrigin;

       //Create the source
       AudioSource source = soundLoc.AddComponent<AudioSource>();
       setSource(ref source, clip, volume);
       source.Play();
       Destroy(soundLoc, clip.length);
       
       //Set the source as active
       m_activeAudio.Add(new ClipInfo{source = source, defaultVolume = volume});
       return source;
    }
For sound data, the method takes an audio clip and a volume value as parameters, but could be extended to take other parameters to modify the sound, such as pitch.

The other parameter here is the Vector3 that indicates the position of the sound. This is where the sound is playing from, and uses Unity’s built in sound management to determine the panning and volume of the sound based on the position of the audio listener component in your scene and the rolloff for the audio source (more in rolloff in a little bit.)

This isn’t a long method, and it is the most critical one, so I will step through the code.
   //Create an empty game object
   GameObject soundLoc = new GameObject("Audio: " + clip.name);
   soundLoc.transform.position = soundOrigin;
This creates an empty GameObject in the world. When running the game, this will show up in the inspector as “Audio: [whatever your clip name is as listed in the Unity assets]”. It then moves this newly created object to the Vector3 position passed in.

Next we attach an audio source to the fresh GameObject we just created:
   //Create the source
   AudioSource source = soundLoc.AddComponent<AudioSource>();
   setSource(ref source, clip, volume);
   source.Play();
   Destroy(soundLoc, clip.length);
After creating the AudioSource, we pass a reference of it to ‘setSource’, which I will outline shortly. Basically this method takes the values passed in and applies them to the AudioSource of our newly created GameObject. We start the AudioSource playing, then call the Destroy method on the new GameObject, passing clip.length as the parameter of how long to wait before destroying the object. Since clip.length is the time it takes to play the clip, this will destroy our AudioSource GameObject immediately after finishing the sound. This is essentially like playing a one shot of an AudioClip, but since it is an AudioSource, we can do more things with the clip, such as changing it’s volume while it is playing, or pausing it, which we will take advantage of later.

Lastly, we register this audio source with the AudioManager as a ClipInfo object which we created earlier. This way the AudioManager tracks the sound and can handle it as it is playing.
   //Set the source as active
   m_activeAudio.Add(new ClipInfo{source = source, defaultVolume = volume});
   return source;
Since ‘source’ and ‘defaultVolume’ are public properties of our ClipInfo object, we can assign them using object initializers (using the curly braces), which allows us to avoid the need for an explicit constructor. We add the new ClipInfo object to our AudioManager member variable List m_activeAudio, which maintains references to all the sounds that are playing.

setSource Method

For the final part of this section of the tutorial, we will create the “setSource” method referenced above in the “Play” method. This method sets the properties of our new audio source.
private void setSource(ref AudioSource source, AudioClip clip, float volume) {
   source.rolloffMode = AudioRolloffMode.Logarithmic;
   source.dopplerLevel = 0.2f;
   source.minDistance = 150;
   source.maxDistance = 1500;
   source.clip = clip;
   source.volume = volume;
}
The AudioSource is sent in as a ‘ref’, so that all changes made to the AudioSource parameter passed in will be kept after this method is finished and our ‘Play’ method resumes.

Rolloff mode is the way that Unity makes the sound decrease in volume as its distance from the AudioListener in the scene increases. In the inspector you can see a graph that models this.

The Linear mode means that the sound decreases at an equal rate as you get further from the listener. The Logarithmic mode means that the sound decreases sharply as you begin to move away from the listener, but the farther you get away from the listener, the less the sound decreases. I opted for Logarithmic sound because it more closely matches the way you hear sounds in reality- a noise made at 10ft away will sound much louder than the same noise at 100ft away, but a noise at 500ft away will sound only slightly louder than the same noise at 800ft away.

The dopplerLevel is related to how the sound is affected by movement (like sirens on a passing ambulence.) I set this to .2 because I found that the default value of 1 can create strange sound anomalies when rotating the camera quickly.

minDistance and maxDistance are important, particularly for Logarithmic rolloff.

minDistance is how far the sound must be from the listener before the volume starts to decrease. This is especially important because if this is set to its default 0, the sound will start to drop off sharply at any distance from the listener. This means that a sound made at 1ft away will be significantly louder than a sound made at 2 ft away, which just does not feel right in-game. For Tumbleweed, a distance of 150 seems to be working for now- all sounds less than 150 game ‘units’ away will play at max volume, and after that the sound dropoff will begin.

maxDistance is important because it determines both the maximum distance at which a sound will play, as well as the curve of sound dropoff. With logarithmic rolloff, you can make this number really huge, and sounds that play in the far distance and middle distance will still sound natural.

Finally, we set the sound clip to our clip, and the default volume to our volume.

We covered a lot here. If set up correctly, you should now be able to play any AudioClip by calling “AudioManager.Instance.Play(AudioClip, Vector3, float)” with your desired clip, position, and volume.

Here is a my project setup and in-game hierarchy in Unity so far:

Next time...

In part 3 of the AudioManager tutorial, I will cover placing moving sounds on moving objects, pausing sounds, and changing volumes.

Tuesday, March 12, 2013

Unity C# AudioManager Tutorial - Part 1 (Setup)

This is part 1 of a 4 part tutorial to create an audio manager for Unity 3d. This tutorial focuses on the initial setup of the singleton and audiomanager classes.

Part 1 | Part 2 | Part 3 | Part 4

Handling Sound in Unity

Default Unity sound control is clumsy. GameObjects can only have one AudioSource as a component. AudioClips are inflexible. In these posts, I describe an audio manager that is simple to use, easy to organize, and adds versatility to the Unity sound tools, building on its existing structure. This includes playing audio clips dynamically with full control, tracking sounds that are currently playing, pausing select sounds, and controlling volume dynamically. This tutorial assumes you have a basic understanding of sound handling in Unity, check out the manual for an overview.

Singleton

First off, we need the singleton. For this, I’ve referenced the article 50 tips for working with unity. I recommend reading this in its entirety- you don’t have to agree with everything, and you don’t have to absorb every detail, but it’s good think about these things!

The premise of the singleton is that you have a single object which can be accessed at any time, without explicit instantiation, and you know that the instance you are accessing is the sole instance of the singleton object. It works through a static method which calls the stored instance of the object. Inside the method, the singleton checks if the instance exists and returns it. If it does not exist, it creates one and returns this. From the outside, though, it seems as if the single instance is always available.

The great benefit to this for our AudioManager is we do not have to manage a GameObject for it for each scene. We simply have each sound clip to play call the AudioManager, and if the instance doesn’t exist, the singleton creates one, and if it already exists, the singleton uses the existing one, so there is no threat of creating multiple audio manager instances in one scene, and we don’t have to concern ourselves with its existence.

using UnityEngine;
using System.Collections;

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour {
    protected static T instance;

    //Returns the instance of this singleton
    public static T Instance {
        get {
            if (instance == null) {
                instance = (T)FindObjectOfType(typeof(T));
                if (instance == null) {
                    GameObject container = new GameObject();
                    container.name = typeof(T)+"Container";
                    instance = (T)container.AddComponent(typeof(T));
                }
            }
            return instance;
        }
    }
}
The T listed represents a generic type, which allows you to create singletons of various types by inheriting this class. In the context of this post, though, we will only be using it for our AudioManager.

Also note that the Singleton inherits from MonoBehaviour, which allows us to implement any of its subclasses as components on GameObjects.

You can google the Unity command “DontDestroyOnLoad” to see how to make a GameObject persistent across scenes, but I am not putting it in this base class since we may not want all derived classes persistent across scenes. For example, in our AudioManager, we do not need a persistent GameObject.

AudioManager - Initialization

To begin, we will initialize our new AudioManager class using Awake(). We use this instead of Start() because we want to make sure our AudioManager is available first thing in the scene, in case there are any sounds that start playing immediately (like music.)

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class AudioManager : Singleton<AudioManager> {
    void Awake() {
        Debug.Log("AudioManager Initializing");
        try {
            transform.parent = GameObject.FindGameObjectWithTag("MainCamera").transform;
            transform.localPosition = new Vector3(0, 0, 0);
        } catch {
            Debug.Log("Unable to find main camera to put audiomanager");
        }
    }
}
We start by creating a message logging the AudioManager initialization. Next, we try to attach the AudioManager to the main camera, placed within a try/catch block so that we can log any issue finding the main camera or attaching this object. This may be something worth playing around with if your AudioListener isn’t always on your main camera, or you don’t tag your game camera with the “MainCamera” tag, but for Tumbleweed this works.

Setting the ‘transform.parent’ property of the object makes it appear under the parent object in the scene hierarchy while running the scene. It helps organize your hierarchy, but more importantly, all transforms (movement) of the parent GameObject will also apply to its children (and its children’s children.) This way, the AudioManager will stick to the camera like a cat to a tree. Now we can attach certain sounds (like music) to the AudioManager itself, and know that it will stay on the camera, which means it will not change volume or pan position unless we explicitly instruct it to.

Initializing AudioManager in a scene:
  1. Any script makes a call to AudioManager.Instance (eg var AM = AudioManager.Instance).
  2. The Singleton base class checks for an instance of AudioManager.
  3. Not finding an instance, the Singleton creates a new AudioManager GameObject and attaches the AudioManager component (script.)
  4. The AudioManager script Awake() method searches for the main camera (with the “MainCamera” tag.)
  5. Finding it, it attaches itself to the main camera by making the camera its parent.
Now we have an AudioManagerContainer GameObject with an AudioManager component, and it is a child of the MainCamera. At this point, if you have set these classes up correctly, you should be able to call this in your scene, though it does not do anything yet. You need a GameObject in the scene with the "MainCamera" tag. If you write "var AM = AudioManager.Instance" on the Start() method of any object in your scene, you should receive a Console message that reads "AudioManager Initializing." If you successfully receive this message, you are ready to move on to part 2.

Next time...

In part 2, I will create a Play method, and will discuss some of the controllable attributes of sounds in Unity.