The Complete Guide to OpenAL with C++ Part 3: Positioning Sounds

Title of the article: The Complete Guide to OpenAL with C++: Positioning Sounds

In the first article we learned about OpenAL, setting it up, and playing some sound files. The second article taught us the much needed ability to stream large files from disk. For this third article, we’ll cover what’s needed to position your sound effects within your game’s world. This is also called spatial sound.

What is Spatial Sound? What is Positioning Sound?

Let’s set a scene. You’ve made yourself a really cool 3D, minecraft-like game. The worlds are big and expansive and there’s all sorts of things players can do in it, you’re really quite proud of what you’ve made so far. There’s already sound effects in the game, one of those sounds is a beast that comes out of the dirt. It was a hard sound-effect to make, you recorded your dog growling using your phone and you even layered in some hiccups from yourself.

The sound is really creepy. Really suits this beast enemy you’ve made.

While play-testing one day, you find yourself in a grassy plain, shooting laser beams at clouds to see if the new effect of dissipating clouds is working and looking as expected. Then it happens, one of the beasts is spawning and coming out of the ground. You hear your sound effect, it’s so good.

But wait. Where is it? It sounds like it should be right in front of you. But it’s not! Damn! You must of screwed up the rendering code or maybe it can’t get through the dirt? While you’re thinking up all the things that could go wrong, you’re suddenly attacked!

Aha! So it is there. But it’s not being rendered correctly. You start to move your mouse to see if another angle works and that’s when you figure it out. It spawned behind you.

You couldn’t tell, because the audio gave you no reason to think it was behind you. This is where spatial positioning will help.

I’ll make a note here, that technically, you could generate several sound files of the same sound. Each of these you could apply some spatial filters from Audacity or something, and work out in the game which sound file to use at which time.

This will work for the simplest of games only. Because what do you do if the sound effect needs to play while it moves past the character. That’s where the software approach of OpenAL makes a lot of sense: you create the sound once, and the game audio library handles the effects in real time.

Don’t worry, even low-end machines can handle almost all audio tasks for a game.

How to Position Audio in OpenAL

There’s really only two things you need to do.

Firstly, set your listener position to wherever your main character is in your world. Secondly, set your source position/s to wherever those sounds are in your world. If you just want to test things our, grab the code from the previous article, and just change the position of the source. To make things easy, here’s a very simple “tone” sound that you can use.

Tone sound

If you’re having trouble hearing the difference, I’d suggest you get yourself some headphones. If you have 5.1 or 7.1 surround speakers setup, you’ll get even better results.

Here’s the code change:

ALuint source; alCall(alGenSources, 1, &source); alCall(alSourcef, source, AL_PITCH, 1); alCall(alSourcef, source, AL_GAIN, 1.0f); /* here's the change, position is X, Y, Z */ alCall(alSource3f, source, AL_POSITION, -100, 0, 0); alCall(alSource3f, source, AL_VELOCITY, 0, 0, 0); alCall(alSourcei, source, AL_LOOPING, AL_FALSE); alCall(alSourcei, source, AL_BUFFER, buffer);
Code language: C++ (cpp)

It’s really that simple. However, there are a lot of different parameters that will affect this. Of course, for a super simple 2D game, you can get away with just using the above.

What units is the position measured in?

None. Technically, OpenAL just thinks of it as “units”. What this means is that you need to adjust some of the parameters in OpenAL to account for the units you’re using.

This might seem confusing, and needlessly complicated, but think of it this way. OpenAL calculates the attenuation of a sound, based on the relative distance units of the source and listener. It’s your job to tweak the parameters to exaggerate this attenuation effect until the source sounds like it’s far enough away relative to your games world.

This allows you, the game programmer, to simply use the objects 3D world position as the position of the source in OpenAL; you don’t need to think about it. Then, you just tweak until it sounds right, and now everything will sound right.

Big and Little Sounds
Keep in mind that you likely still need to adjust the gain of sources manually depending on the sound. What I mean by this is that an explosion.wav file and a whisper.wav file will generally have the same overall amplitude (for example -1.0dB). Just positioning a whisper and an explosion in OpenAL will only result in them sounding like they’re coming from that direction, but the whisper will sound way to loud, or the explosion will sound way too quiet.

You need to set their gain relative to each other. For example, an explosion might have a gain of 1.0, and the whisper something like 0.1. After they’ve been positioned, and attenuation has been accounted for by OpenAL, the explosion may have a final effective gain of 0.04 (because it was positioned far, far away), and the whisper still with 0.1 because it was positioned right next to the listener.

Remember, the sound files should all be the best dynamic range (amplitude) available. It’s within OpenAL that you set the relative gain.

There is also an AL_SOURCE_RELATIVE parameter in sources that can be set to true or false. This tells OpenAL that the source’s position should be considered “relative” to the listeners position. Basically, instead of the source being at position 10.0, 1.0, -3.0 it’s actually 10.0 to the right of the listener, 1.0 up, and 3.0 in front.

What is Attenuation?

You can think about the distance-calculated gain as a curve. The further the distance, the smaller the gain.

Attenuation is that Curve.

How is the Attenuation Calculated?

This depends on the Distance Model that you use. There’s several available to use in OpenAL:

  1. AL_NONE means that no attenuation is calculated. You can position sources and the listener wherever you want, nothing will change based on the distance.
  2. AL_INVERSE_DISTANCE is much like the clamped version, except unclamped (duh). See the clamped model below for more info
  3. AL_INVERSE_DISTANCE_CLAMPED is the Interactive Audio Special Interests Group’s “Interactive 3D Audio Rendering Guidelines: Level 2.0”. You can access their website here to see the guidelines, but you need to create a free account. This is basically a sound attenuation model that all the audio for games people agreed is a good for games; think of it as the equivalent of SIGGRAPH for graphics. This is the default distance model, and probably the one you want to use for games.
  4. AL_LINEAR_DISTANCE is basically removing the curve from the graph and replacing it with a straight line. It’s completely unrealistic, but you might find use for it in applications that aren’t based on games or virtual worlds.
  6. AL_EXPONENT_DISTANCE now the curve is an exponential curve.
  7. AL_EXPONENT_DISTANCE_CLAMPED same as above but with clamped values.

All of these are explained in a little more detail, along with their actual formulas, in the OpenAL Specification.

Wait, what’s “gain”?
It’s just how loud the sound is. It’s a measure of how much the volume and amplitude will be increased/descreased. Values above 1.0 will make it louder than it was before, below 1.0 will make it quieter. The “value it was before” is after the source gain is applied. So if I set the source to have a gain of 0.5, this is the initial value used before it’s run through the Distance Models.

How do I set the Distance Model used by OpenAL?

With a single call to alDistanceModel using one of the above enumerated values. Keeping in mind our helpful error checking code from the first article:

if(!alCall(alDistanceModel, AL_INVERSE_DISTANCE_CLAMPED)) { std::cerr << "ERROR: Could not set Distance Model to AL_INVERSE_DISTANCE_CLAMPED" << std::endl; return 0; }
Code language: C++ (cpp)

Remember, the default value used by OpenAL is AL_INVERSE_DISTANCE_CLAMPED, so unless you need to change it, you never have to call this function.

Parameters used in OpenAL Attenuation

Okay; so now you know how to position your sources and listeners, and you know that the “units” used in OpenAL don’t mean anything so you have to exaggerate the attenuation effect until it sounds right. But how do you do that? You change some parameters.

First, let’s take a look at the calculation of attenuation under the AL_INVERSE_DISTANCE_CLAMPED distance model. We’ll look at only this one because it’s the default and the one most suited for video game applications.

Code language: C++ (cpp)

What this tells you in that the final distance between the listener and source (attenuated gain is calculated per-source) is clamped between a minimum of AL_REFERENCE_DISTANCE and AL_MAX_DISTANCE. Once this clamped distance is determined, the gain is calculated by dividing the AL_REFERENCE_DISTANCE by the sum of the AL_REFERENCE_DISTANCE and the difference in distance multiplied by the AL_ROLLOFF_FACTOR.

To me, this all sounds confusing. We know that attenuation is just a function/curve to determine the gain over distance so the best way to show this is by plotting a few different sets of parameters side by side.

First, let’s just keep track of the default values:

ParameterDefault Value
AL_MAX_DISTANCEMax of 32-bit float, 340282340000000000000000000000000000000.0

How does AL_ROLLOFF_FACTOR affect attenuation?

Here’s the AL_INVERSE_DISTANCE_CLAMPED distance model applied with various AL_ROLLOFF_FACTOR values.

How the AL_ROLLOFF_FACTOR affects the gain attenuation in OpenAL
How AL_ROLLOFF_FACTOR affects the attenuated gain

As you can see, the rolloff factor is changing how quickly the sound starts to fade out to nothing. It’s hard to see in the graph, but the values at distance 100 aren’t zero, they’re still gradually fading away (most apparent with rolloff of 0.1).

So, if the scale of your game world is larger, you would simply use a larger roll-off factor. In other words: you’d make each “unit” of position cause the sound to fade away faster. If you world scale is smaller, a smaller rolloff factor is used so that things need to be really far away for there to be a pronounced change in gain.

How does AL_REFERENCE_DISTANCE affect attenuation?

Here’s the same model again for different AL_REFERENCE_DISTANCE values.

How the AL_REFERENCE_DISTANCE affects the gain attenuation in OpenAL
How AL_REFERENCE_DISTANCE affects the attenuated gain

An important thing to note is that in the clamped model here, the AL_REFERENCE_DISTANCE determines when the attenuation actually starts. Any distance less than AL_REFERENCE_DISTANCE does not have the gain changed. This is most noticable in the 2.0 value above (note that GnuPlot sometimes doesn’t give a completely horizontal line like it should).

What should be apparent here is that the overall curve is pretty much the same, it’s just the starting point that changes.

How does AL_MAX_DISTANCE affect attenuation?

How the AL_MAX_DISTANCE affects the gain attenuation in OpenAL
How AL_MAX_DISTANCE affects the attenuated gain

This one is a little different because the curves 100% match up. But you can see that the AL_MAX_DISTANCE is creating a floor at which the gain can get no lower.

Combining these parameters

One final graph to show how editing the three of these parameters can give you very good control over how the gain is attenuated in OpenAL.

It might seem a little strange to use these parameters and change them a lot. But remember, these can be useful. Have you ever played a game in which characters on the other side of a small room are talking to each other and you can’t really hear them properly until you go over to them?

That’s not really realistic, right? A normal conversation taking place on the other side of a loungeroom doesn’t require you to walk over there. This is where you need to adjust the parameters above dependent on the situation in the game. Realistically, you need to adjust these parameters quite a lot, all the time.

That’s it for positioning audio in OpenAL. It’s really very very simple, and you can get away with a lot by doing nothing but setting the position of your sources and listeners.

In the next article we’ll cover moving audio sources and the doppler effect.

Leave a Comment