Episode #593

Build an Ambient Noise Designer

Series: Learn AudioKit

21 minutes
Published on May 8, 2025

This video is only available to subscribers. Get access to this video and 592 others.

In this episode we will be working with three different types of noise (Pink Noise, White Noise, and Brownian Noise) and will blend them to create an ambient space. We'll then work with Panners to move the sound from left to right. Finally we'll add reverb, a stereo field limiter, and an auto-pan timer to have the sound sweep across the stereo field.

This episode uses Swift 6.0, Xcode 16.2.

# Ambient Noise Designer Tutorial

OK, in this video, we're going to build an ambient noise designer. So I've already created some boilerplate code in our content view to create this ambient noise view and have another tab here. And then over here in the ambient noise view class, I've got some boilerplate for our conductor. And it's just got a couple of buttons at the top.

And if we go down here and add a preview for this, let's make sure that it's got a navigation stack. And inside of there is an ambient noise view. The first thing I want to do is add a custom slider view, because this is going to have a whole lot of sliders. I'm going to just paste this in here. And then we can add a preview for that as well.

So this is going to be a slider view. It's got a title value and range. So here we can say dot constant. Actually, we can do state inside of a preview if we do at previewable state and var value. And say that's going to be a float equals 0.5. Say this is a binding to that value in the range of 0 to 1. And the idea of this is just to have some nice looking UI where we can see the value, we can see the label.

So we'll do padding there. So the next thing I want to do is add a group box, or add this inside of our group box. And we're going to have a group box for some different sliders. Then we're going to have another group box for the noise controls view. And so I think I'm just going to put that here, noise controls view. And that's going to need to take our conductor.

So let's add that next. We're going to say struct noise controls view is a view. It has a bindable var conductor, which is our noise conductor. Then in the body of this view, we're going to have another group box. This time we're going to have a label. And that label is going to be noise controls. And then inside of there, we'll have a VStack.

And then we're going to have a slider view here. And the title here is going to be the pink noise. And the designer that we're making is going to sort of blend pink noise, white noise, and brown noise, which are three different algorithms for generating noise. There's really good articles on Wikipedia about the differences here.

So the value for this is going to come from our conductor. So we're going to have some sort of binding to a pink noise volume. And then the range here is going to be from 0 to 1. And then we don't need anything for the on editing changed. So this pink noise is going to be a value on our conductor. So I could say pink noise like this and say that that's pink noise.

And this is going to come from audio kit and I think soundpipe audio kit and maybe audio kit EX. So this pink noise here has no volume. But I think it has an amplitude. So we could do that. And then we also want to control the pan for this. So I'm going to call that pink pan.

And so I also want to say pan. But this doesn't have panning built in. And because I want to control these three things kind of in the same way and have three different types of noise, what I could do is have another struct. This struct is going to be a pan volume node.

And then inside of here, we'll have a panner. And I'm going to make it a-- we're going to have a panner, which is a panner. And then we're going to have our node, which can be a node here. The pink noise is a node. And there's no sort of intermediate step that says that all these noise generators have the same super type. So I basically need anything that has this amplitude definition.

So if I then go to white noise, the white noise also has the same amplitude. And so does brown noise. And so what I want to do here is just introduce my own protocol called has amplitude. And then this is going to be an amplitude, which will be a CG float. Actually, we can make that an AU value, get set.

And then here, I'm going to make this generic over any n that is a node and it also has amplitude. And then this thing can be our n. OK, if we have that, then we can add a volume, which is an AU value. And here, we can set the-- on the didSet for this, we can set the node's amplitude equals to that volume.

And then here, we can have our pan value and do the same thing. Panner.pan equals pan. Node needs to be a var so that we can mutate it. And so now we have a generic pan volume node that we can add in here.

So what I'm going to do is this pink noise is going to be a pan volume node of type pink noise. And that means that I need to extend the pink noise struct to conform to that has amplitude. And I need to do the same thing here on-- I need to do the same thing here on brown noise-- let's see-- white noise and brown noise. And it's actually called Brownian noise. So that gives our conformance so that we can treat them all equally.

And so now I've got a pan volume node of pink noise. I'm going to create it like this. And then we can do the same thing here. This is going to be white noise. And then change to n. This is going to be brown noise. OK. This needs to have the-- that actually doesn't need to be there because then we can just-- we can initialize this with our node type.

So that's going to be n. We're not going to have the panner. We are going to have the volume and the pan settings. So we're going to set the volume equal to 0 and then the pan equal to, let's say, 0.5. Actually, no, pan is 0 as well. AU value equals 0. And the reason is this will start off at quiet or muted. And then the pan will start off as dead center.

So the pan value is going to be in a range of negative 1 to 1, where negative 1 is fully on the left channel and 1 is fully on the right channel. So now we assign the node. Here we create a new panner whose input is that node. Then we assign our volume and pan. And then we have to set the initial values because the didSets will not be set accordingly.

So here we're going to say self.node.amplitude equals volume so that we get our initial volume. And self.panner.pan equals pan. OK, so now we've got these. And this pan volume node is going to have a pink noise inside of it. This one is going to be white noise. And this one will be brownie noise. [TYPING] And let's just make that unlabeled for brevity.

OK, so now we've got our three different nodes there. Our conductor, conductor here, and then now-- OK, so now we've got pink noise, pink panning. [TYPING] Now we can do the same thing here for the white noise. I'm going to change this to white. [TYPING] We'll do the same thing here and change this to brown.

OK, so now we have our noise controls view. And if we take a look at our preview, now we have our noise controls. And we've got our play button at the top. And then we've got a section at the bottom for some other controls. Let's take a look at the documentation for panning, because I thought that it was from negative 1 to 1, but I could be wrong. It is from negative 1 to 1. OK, so that's great. Value of negative 1 is hard left. Value of 1 is hard right, and 0 is center.

But if I take a look at the range for what we have here, this actually needs to be negative 1 to 1 so that it shows up in the middle. OK, there we go. Much better. And then what we need to do is when we hit play on this, we need to start the conductor. So in our conductor, in the didSet here, if we are supposed to be playing, then we're going to call start. Otherwise, we're going to call stop.

OK, a couple of other things that we are going to need here. One is our-- I want to add a reverb. So this is going to be a basic reverb. I also want to create a stereo field limiter. And we'll explain what that is in a little bit. And then finally, I want to create the audio engine and then the mixer. This I can create just like that, and same thing for the mixer.

OK, in the init for this, we're-- going to then first create the stereo field limiter. And that is going to take the mixer as its input. Now, the stereo field limiter is going to have an allowed width. This is basically like how much do we allow the stereo field to expand to its hard left and hard right values. And so even though these might be panned left or right, the limiter is going to sort of narrow that space.

So at a value of 0, you're going to have no stereo fields. And so everything's just going to come straight down the center. And at a value of 1, you're going to have a full stereo spread. Actually, I said that in reverse because I want the slider to behave that way. But this one is actually limiting. So a value of 1 will limit the stereo field to just be mono.

Then we need to create our reverb. And the reverb is going to take the stereo field limiter as its input. Then we're going to set the stereo field limiter to stereo field limiter. We can say mixer.addinput. And we're going to add our pink noise panner. And same thing here for the white noise and then also for the brown noise.

So now we've got our mixer has its inputs. Our engine's output is going to be the reverb. And then finally, I'm going to configure some default values here. So we'll say private func configure defaults. Then here, we're going to set the reverb's dry/wet mix to something kind of small. The thing about this reverb is it's going to take these inputs and it's going to sort of blend them together. So it's going to make it feel less stereo.

And I think what you could end up doing is have a reverb on each channel and then pan that. But for now, I'm just going to keep this reverb at a low level. And then our stereo field limiter's amount is going to start at 1, so fully limited. OK, we can change this to be a let so that this can continue.

And now we've built the noise conductor and set all the defaults. We've set our engine's output. Now we need to start all of the noise generators. So we'll say node.start. We will do that same thing for white and brown. And then we can say try engine.start. Here, I'm going to leave all of the nodes started and just stop the engine here. And then if I am auto panning, I'm going to also turn that off. We're not dealing with the auto panning just yet, but we will in a moment.

OK, the last thing we need to do is add those two sliders for the reverb and the stereo width and all that. So the first one we're going to do is the stereo width. And I'm just going to paste in that value. Stereo width here is just going to be a binding that's the inverse, 1 minus the stereo field limiter. Let's do stereo field limiter. And then we also need one for the reverb. And I'm also going to set one for this auto pan rate.

So the reverb is going to control the dry/wet mix. And then we're going to have an auto pan rate from 0 to 10. And that means I need to add another value here, a value that's going to be auto pan rate. And we're going to start that off at 1.

OK, so now we have everything, all of our UI configured. I just want to add a little bit of padding to this VStack here to give a little bit of breathing room. And then we can hit Play. And I'm going to just run the application, because sometimes the preview doesn't want to actually play audio.

Again, this is going to sound a lot better in headphones so that you can actually perceive the stereo. But if I turn up pink noise, you can hear kind of a soft noise. White noise is quite a bit harsher. And it's also higher up in frequency. And brown noise is much lower in frequency and kind of more mellow. And I think a blend of these can kind of make you sound like you're on an airplane. [AIRPLANE ENGINE]

OK, now let's check out the panning. So I'm just going to leave pink noise on. And we're going to pan to the left. And we don't really hear anything. And that's because the stereo width is turned off. So if I turn the stereo width all the way up-- [AIRPLANE ENGINE] Now we get the stereo effect with everything on the left. If I turn the white noise with the white noise on the right. And if I adjust the stereo width, they sort of move to the center and then back to the sides again. OK, and we can do the same thing with the brown one.

So what I want to do next is turn on this Auto Pan feature. So to do that, we're going to set up in our conductor an Auto Pan timer. So that's going to be Auto Pan timer. Then we're going to have an Enable Pan or Auto Pan. This is going to take the depth. The depth is sort of how much do we want to allow this to go to the edges. And we can set this depth to 1, which is going to be the maximum. So basically, maximum spread-- you could also change this to 0.5 if you wanted to say only go-- don't go all the way to the edge, but go 50%.

So we're going to keep track of a variable here called Phase. And then if we have any existing Auto Pan timer, we need to invalidate it. Then we're going to set it to a new value. This is going to be timer.scheduledTimer with timeInterval, repeats true, and a block. So the timeInterval, I'm going to use 0.016. So this is going to be essentially 60 times per second. It repeats. And then for our block, we can just use the trailing closure syntax. It's true.

And then here, we're going to do weak self. We don't need the timer. Inside of here, we're going to guard let self, else return. And now we want to calculate the new phase based on a sine wave at a different position. So what we're going to say is for the phase, this is going to increment by 0.01 times our Auto Pan rate. And that's how many times per second.

Then we're going to be able to set the pinkNoisePan to be a sine of that phase times the depth. We're going to do this again for the whiteNoise and for the brownNoise. The only difference is now we're going to add two here and four here. And so this is going to let them move at different intervals. So basically, this one's going to have a four-second interval. This one's going to have a two-second interval. This one will have a one-second interval. That is if everything is set at-- if the Auto Pan timer is set at one, that's what's going to end up happening here. So every four seconds will sort of align again.

So that is the Auto Pan. And then we'll have a disableAutoPan function, which is just going to set the Auto Pan timer dot invalidate. And then Auto Pan timer equals nil. Now we can go up to this Auto Pan, add a didSet to this. And if we are supposed to be panning, then we will enable Auto Pan. Otherwise, we will disable Auto Pan.

OK, let's give this a run. OK, we'll go ahead and start it. I will trigger some blend of noise, increase the stereo width, and then we will enable Auto Pan. And we can scroll down and change the rate. We can limit the stereo field if we need to. And there we have an ambient noise designer.

There's probably a lot more that you could do to this, maybe add some background sounds like rain drops or people talking in a coffee shop or whatever and just sort of have something on loop. But that's an interesting noise designer that we were able to whip together pretty quickly.