Lesson 43 - Routing Audio, Mixers and Effects

Tutorial Series: Introduction to Unity with C# Series

Previous Article  |  Next Article


Now that we know how to work with audio, let's do something a little bit more interesting by routing the audio to different mixer channels. That way we can apply different effects to those mixer channels. Before we get to that, let's add a little bit of music to our game. We're not actually going to use it yet. That will be the preview of the next lesson, but just so we don't forget, let's add it here first to our audio Music folder. Import New Asset. Grab the first 1 and 2 music loops. As we did with the audio clips in the previous lesson, AudioSource, and we'll drag MusicLoop1 here. We don't want it to play right now, so we'll just take that off, and remember to enable it in the next lesson. We'll enable loop, because it will always be looping. For the second loop, same thing. It might be a little loud, so let's reduce the volume here, around halfway, .45 for the first loop, and .55, it's a little louder, for the second loop.

For this lesson, what we need to do first is create a couple of mixers. We need two mixers because they'll have slightly different effects. In your case, you'd want to use two different mixers for very different effects, but in our case, what we'll want to do is route audio through one mixer that gets affected differently than audio routed through another mixer. Of course, if we choose not to affect certain audio clips, we just won't route them to any mixer at all. To add a mixer, let's create a folder here called, Mixer. We'll create two mixers. We'll have one called Main, and then another one called, Subs. Which actually has nothing to with bass subs, or anything like that. It's a sub-mixer, that's why I called it that. For the Main mixer we'll add the effect we want for it, we'll choose a low pass filter. We'll default it to 22,000 hertz. We'll give it some resonance. You'll see why a little later.

In the Master, for the Sub, we'll add an ordinary low pass filter, without resonance. This will basically have the effect of dampening, rather than with the resonance, it will have a suctioning sound when we'll engage into the PowerUp. To assign audio to the mixers, it's very simple. We simply go through each AudioSource, and we assign a mixer to each audio source, or no mixer at all if we'd want to bypass routing it through the mixer. For the Main Camera, the CrowdNoise loop is going to be routed through master, Main. The AirHorn is going to be through the Sub, we just want to muffle it. The music will also go through the main Master as well. For the CrowdCheer, we'll put that through the Subs. That's it. For the rest of the sounds, we'll bypass them so that they remain unaffected when we start applying the effect. We'll want to engage the effect by applying through the ZoomCam script. Open that up.

First we start with creating some public fields for our audio mixers. I almost forgot, you need to add a using reference. You could figure out which namespace the audio mixer belongs in, but here's a handy thing that Visual Studio does for you. It notices that there's a namespace reference that is missing up here, and if you hover over it, it says, "Show potential fixes: Control, and period." I'm going to hit that, and the first suggestion that shows up is, "Using UnityEngine.Audio" That should do the trick. There you go. That resolves that reference. Pretty handy with Visual Studio. We'll do the same for the Sub. So I don't forget, let's assign those right away. Just like that. Back in the script, we'll also need a couple of other fields to hold our low pass frequency in between the calls that reference, and the methods that reference it. Let's call that, "LPFrequencyMain" and, "float LPFrequencySub". We'll want the Start() method to initialize that.

They'll show you an interesting notation here with C#. You can assign more than one variable at a time on the same line of code. Different languages allow this, some don't. This is the way you do it in C#. LPFrequencyMain = .. And here's where the magic happens.. LPFrequencyMain = LPFrequencySub = 22,000 .. We'll want to set it. Just like that. It's kind of funny looking notation, but it works because essentially what we're doing is we're assigning 22,000 to this variable, and then we're assigning the value that variable holds to this variable. Other languages you can sometimes use a comma to separate between the variables, but in C# you just use the equal sign to get multiple assignments on one line.. Now we'll want to create a method called, LowPassFilter() That's going to be our effect. We'll need a bunch of parameters. Write all these in. We'll need a float called, endLimit. We'll make another one for, rate. We'll need an AudioMixer that will pass in, either the Main or the Sub, call it, "mixer". We'll want to reference the actual effect, call that, “effect". We'll have another one called, string “slider". Only one type declaration.

Here's a different looking input parameter that we've seen before, I'll briefly describe in a minute. A prefix to the input parameter called, "ref". "ref float frequency". Get that out of the way so we have a little bit more visibility here. In the method, create a conditional, If (slider) .. We'll specify this input in the argument, for this method's call. If (slider == “SlideDown”) if it's slide down, emulating the functionality of sliding down the mixer channel.. We'll have another conditional in here, if (frequency > endLimit) Frequency = frequency * rate. Else, frequency = endLimit. Then frequency --. Part of the first conditional will have an else if now. Else if (slider == “SlideUp.") We'll have another, similar. Whoops, never did put in the termination semi-colon there. We'll put something very similar here, except, only with one modification. If (frequency < endLimit) And here, frequency++, increment the frequency. Finally, after all those conditionals, we'll access the mixer and attempt to set a parameter using our passed-in effect with the value of the frequency that we're also getting out of all these conditionals.

As for this conditional block, which is rather large and doesn't even look like it's doing things terribly different from one another, there's a handy little shortcut for this called a, ternary operator. We could use the ternary operator to replace the conditional block we just made with something a lot simpler, and all in one line. I'll explain how that works after we do it.? This block here we can simplify as follows, “frequency = (frequency > frequency endLimit) " That's the Ternary operator, the question mark. "frequency * rate: endLimit." This looks really funny but it's actually really simple once you learn how to read it. Otherwise, it acts just like the conditional block here. Let's go through this. How do you read this? You start with the question mark. You can read that as, “if”. Then you move here. If frequency is greater than end limit, then frequency equals .. And you jump over here .. Frequency * rate. Else, jump over there, frequency = endLimit. It's kind of funny if you forget the order in which to read this, but once you get the order of which to read it, believe me, you'll never forget. You'll use ternaries fairly frequently in your code. It just makes things a little bit easier to look at, eventually, once you understand it.

We can do the same thing here. Get rid of this block. Put in a ternary as well. Except for this one difference. That was less than. Now, here, where we engage the PowerUp, we'll call the LowPassFilter() method, and set these values, bypass that for now, that string input that we'll want to put in. The next one will be the slider value as a string. Say, "SlideDown." When we enter PowerUp, we're going to want to have the effect slide downwards, creating that suctioning sound here, for the Main mixer. Again, that ref keyword, which we'll explain in a second. We'll have something very similar for the Sub mixer as well, but slightly less of a strong effect. This goes all the way down to 600 hertz. For the frequency this will go down to 5,000, which will result in a little more gentle muffling. We'll want to pass in the AudMixerSub. Here we'll pass in the frequency for that. In the else clause, we'll want the imaginary slider to slide upwards. It's actually the frequency, which will be counting upwards. We do that by sliding back up to the largest value, 22,000 hertz. We can hear the highest of high frequencies.

Actually, let's make the rate here a bit slower so that it doesn't engage quite as fast. It’ll have a subtle effect, but since we can do that, why not? The rates for these. We'll have it slide back up to normal a little bit quicker. When we disengage PowerUp, it had that suctioning sound occurs just a little bit faster. Here we'll just say, 1.1. For this we'll say, "SlideUp". This ref keyword, we've got to explain this. The LPFrequencyMain is a value type, it's a float. Normally its value would be copied into the method, and holds no reference back to the original field. By using a ref keyword, we're basically saying, "Whatever changes happen to this value in the method, refer those changes back to the original variable supplied at the input argument." It temporarily makes the value type act like a reference type. That's we I used a ref keyword. Otherwise, the method is just going to be operating on a copy of the value, and it will never change the original variable, in this case, the LPFrequencyMain field. That's why we use that.

Now to make this all work, have our code communicate with the mixer/effects, is we'll have to have the effect parameter in the low pass method determine the mixer effect we want to apply the changes to. In order to do that, we need to first expose the parameter for this in the mixer. Sort of like how we did it with animator, if you remember back there in that lesson. To expose that parameter, let's go to the Master for the Main first. What we want to do is expose the cutoff frequency. That's what we're going to be effecting. Click on expose, and it has that little arrow there. We'll do the same for the Sub Master’s cutoff frequency. Expose that. Go to Window, Audio Mixer, and we can see this overview of our mixers. Here for the Main, we'll just rename this exposed parameter that we just exposed for the Main mixer. We'll call it, "LowPass". There you go. LowPass. In the Sub, we'll rename this to, "LowPassSub". So we can keep track of this a little better when we're writing in code. There you go, LowPassSub.

Going back to our method call here, we can reference these. We’ll want this to reference the LowPass exposed parameter. Then this will reference LowPassSub. Same thing here. Whenever that method runs, just imagine it through the. What we pass in here, it will eventually get set through this parameter. That's what we're passing in there. It will set that exposed parameter in the mixer, and we'll set it with that float frequency value that will go there. Here, let's test it out, let's see how it runs. We'll hear a suctioning sound when we engage in PowerUp, and then it will suction back out to normal when we disengage PowerUp. Keep an eye on here when we engage PowerUp for the Main mixer. Watch how it changes. That pretty much does it for audio. We've looked at quite a bit. In the next lesson, we'll focus on a bunch of different elements, like score keeping, bonuses, and so on. I'll see you there.

Related Articles in this Tutorial:

Lesson 1 - Who This Course is For

Lesson 2 - What to Expect from this Course

Lesson 3 - Installation and Getting Started

Lesson 4 - Starting the First Project

Lesson 5 - Prototype Workflow

Lesson 6 - Basic Code Review

Lesson 7 - Game Loop Primer

Lesson 8 - Prototyping Continued

Lesson 9 - C# Fundamentals and Hello World

Lesson 10 - Variables and Operations

Lesson 11 - Variables and Operations Continued

Lesson 12 - Floats, Bools and Casting

Lesson 13 - If Statement Conditionals

Lesson 14 - If Statements Continued

Lesson 15 - Complex Evaluations and States

Lesson 16 - Code Syntax vs. Style

Lesson 17 - Variable Scope

Lesson 18 - Object-Oriented Programming Intro

Lesson 19 - OOP, Access Modifiers, Instantiation

Lesson 20 - Object Containment and Method Returns

Lesson 21 - "Has-A" Object Containment

Lesson 22 - "Is-A" Inheritance Containment

Lesson 23 - Static Fields and Methods

Lesson 24 - Method Inputs and Returns

Lesson 25 - Reference vs. Value Types

Lesson 26 - Introduction to Polymorphism

Lesson 27 - Navigating the Unity API

Lesson 28 - Applying What You've Learned and Refactoring

Lesson 29 - Constructors, Local Variables in the Update Method

Lesson 30 - Collecting Collectibles, Items and Powerups

Lesson 31 - Spawning and Managing Prefab Powerups

Lesson 32 - Implementing Powerup State Logic

Lesson 33 - Displaying Text, OnGUI, Method Overloading

Lesson 34 - Referencing Instantiated GameObjects, Parenting

Lesson 35 - Understanding the Lerp Method

Lesson 36 - Creating Pseudo Animations in Code

Lesson 37 - Understanding Generic Classes and Methods

Lesson 38 - Animations Using SpriteSheets and Animator

Lesson 39 - Working with Arrays and Loops

Lesson 40 - Debugging Unity Projects with Visual Studio

Lesson 41 - Camera Movement and LateUpdate

Lesson 42 - Playing Audio Clips

Lesson 43 - Routing Audio, Mixers and Effects

Lesson 44 - Adding Scoring Mechanics and Enhancements

Lesson 45 - Scene Loading and Game Over Manager

Lesson 46 - Understanding Properties

Lesson 47 - Controller Mapping and Input Manager

Lesson 48 - Understanding Enums

Lesson 49 - Dealing with Null References

Lesson 50 - Handling Variable Framerates with time.DeltaTime

Lesson 51 - Preparing the Project for Final Build

Lesson 52 - Final Build and Project Settings

Lesson 53 - Introduction to the Unity Physics Engine

Lesson 54 - Understanding FixedUpdate vs. Update

Lesson 55 - Movement Using Physics

Lesson 56 - Attack Script and Collision Events with OnCollisionEnter2D

Lesson 57 - Projectiles and Stomping Attack

Lesson 58 - Parallax Background and Scrolling Camera

Lesson 59 - Infinitely Tiling Background Sprites

Lesson 60 - OOP Enemy Classes

Lesson 61 - OOP Enemy Classes Continued

Lesson 62 - Trigger Colliders and Causing Damage

Lesson 63 - Multi-Dimensional Arrays and Procedural Platforms

Lesson 64 - Finishing Touches

Lesson 65 - Series Wrap


Please login or register to add a comment