Sponsored By

OpenAL's EFX

We take a look at the EFX system in a practical use situation.

Allen Danklefsen, Blogger

November 12, 2010

5 Min Read
Game Developer logo in a gray background | Game Developer

OpenAL’s EFX System, an alternative to EAX

Environmental Audio Extensions (EAX) are a set of effects that are widely used in games by audio to help create environmental immersion. This gives the developers the ability to make it sound like players are in an airplane hangar, cave, underwater, etc; all without having to do custom sounds for each of these environments, such as a gunshot noise in an FPS game. EAX required having special hardware that enabled hardware acceleration of sound.

With EAX deprecated from today’s uses in DirectSound / DirectSound3D (Window’s Vista and later stopped supporting it), OpenAL’s EFX opens the door for the same capabilities as EAX.

Why use EFX if I can just use EAX?

Developers can use EFX instead of EAX because there is no hardware component required as there was with EAX. EFX still offers high-quality environmental audio sound effects. This will give a better experience to more end users. Beyond providing environmental effects for groups of sounds in an area, you can also setup filters and effects for each of your sounds. These will give you even more unique sounds throughout your game. For example, you can give sounds low-pass / high-pass filters, echo / pitch shift, reverb, morph effects, and more.

So how can we use this in a real environment?

Let’s think of a multiplayer shooting game in a town. In the town you are able to take your character in and out of small town homes, warehouses, swimming pools and the streets. Now let’s think of two weapons that could generate effects: a grenade and a rusty knife.

While walking into a building to get your next frag, a window shatters and a grenade is tossed through. The two players in the individual environments -- you in the building and the guy who threw the grenade from outside -- should hear two very different effects.

An easy way to do this is have your objects be able to go into Sound ‘Zones’ throughout your world. These zones can encompass your 3d EFX area where all sounds in that zone have the same settings. Your outdoor zones will have a lower sound air absorption rate, increasing the echo time outside, while the indoor zones will have a higher sound absorption rate. This will make your bounce of the grenade much more pronounced indoors, and the explosion much more bang for your buck outdoors.

Lucky for you, you saw the grenade coming, ran outside and jumped into the swimming pool. Bringing back your counter-strike training, you and the other guy pull out your knives. With every hit of the dagger your character makes a sound as they’ve been hit; this can be one of the 5 random sounds you have outside the pool. With the EFX overlay in the pool, these sounds can now be muffled. This gives you the ability to re-use effects, and still have them sound different.

How this is achieved

Some basic understanding of how OpenAL works is necessary. I will not go into initializing your base sound objects.

First we’ll make our reverb properties. EAX and EFX share the same values, so it’s nice if you are in a position to switch over between the two:

//For the sound environments in the example

EAXREVERBPROPERTIES propsToSet[3] = {REVERB_PRESET_ALLEY,

REVERB_PRESET_DUSTYROOM

REVERB_PRESET_UNDERWATER,

}

 

//For the two sounds in the example

Sound oHitSound[5];

Sound oGrenadeSound;

 

// our local player

Vec vPlayerPosition;

 

With the way OpenAL works, its easiest if you can create the effects up front, and then switch to them as needed. For the first part we need to make the effect we want:

   bool MonarchSE_ReverbEffectInfo::CreateEffect()

   {

      MonarchSE_ALErrorChecker::GetInstance()->ErrorCheck("CreateEffect Clearing error code");

      if(mCurrentEffectSlotID != 0 && alEffecti != NULL)

      {

         alEffecti(mCurrentEffectSlotID, AL_EFFECT_TYPE, AL_EFFECT_REVERB);

         if(!MonarchSE_ALErrorChecker::GetInstance()->ErrorCheck("CreateEffect MonarchSE_ReverbEffectInfo Changing Type, if error occurs, not supported"))

         {

            SetEffect(AL_REVERB_DENSITY,  mDensity);

            SetEffect(AL_REVERB_DIFFUSION,mDiffusion);

            SetEffect(AL_REVERB_GAIN,mGain);

            SetEffect(AL_REVERB_GAINHF,mGainHF);

            SetEffect(AL_REVERB_DECAY_TIME, mDecayTime);

            SetEffect(AL_REVERB_DECAY_HFRATIO,mDecayHFRatio );

            SetEffect(AL_REVERB_REFLECTIONS_GAIN,mReflectionsGain );

            SetEffect(AL_REVERB_REFLECTIONS_DELAY,mReflectionsDelay);

            SetEffect(AL_REVERB_LATE_REVERB_GAIN,mLateReverbGain);

            SetEffect(AL_REVERB_LATE_REVERB_DELAY,mLateReverbDelay);

            SetEffect(AL_REVERB_AIR_ABSORPTION_GAINHF,mAirAbsortionGainHF);

            SetEffect(AL_REVERB_ROOM_ROLLOFF_FACTOR,mRoomRolloffFactor);

            SetEffect(AL_REVERB_DECAY_HFLIMIT,mDecayHFLimit);

            return MonarchSE_ALErrorChecker::GetInstance()->ErrorCheck("CreateEffect Setting properties");

         }

      }

      return false;

   }

Note for all of these values, you are able to pull these from OpenAl’s EFX Structure:

typedef struct

{

 float flDensity;

 float flDiffusion;

 float flGain;

 float flGainHF;

 float flGainLF;

 float flDecayTime;

 float flDecayHFRatio;

 float flDecayLFRatio;

 float flReflectionsGain;

 float flReflectionsDelay;

 float flReflectionsPan[3];

 float flLateReverbGain;

 float flLateReverbDelay;

 float flLateReverbPan[3];

 float flEchoTime;

 float flEchoDepth;

 float flModulationTime;

 float flModulationDepth;

 float flAirAbsorptionGainHF;

 float flHFReference;

 float flLFReference;

 float flRoomRolloffFactor;

 int iDecayHFLimit;

}

 

You do this for each of the three zones. And store the mCurrentEffectSlotID for each.

You will need an auxiliary slot next: this is where the effects get played through.

alGenAuxiliaryEffectSlots(1, &mAuxiliaryEffectSlotID);

alAuxiliaryEffectSloti(mAuxiliaryEffectSlotID, AL_EFFECTSLOT_AUXILIARY_SEND_AUTO, AL_TRUE);

 

For simplicity sake in your update loop check to see if you are in a corresponding zone.

Pseudo-code Example:

{

For each zone

if(IsPlayerInZone(Zone[x], vPlayerPosition))

MakeSoundsPlayThroughEffectZone(mAuxiliaryEffectSlotID[x]

}

 

This would turn out to make all of your effects play through the effect slot. You would just call:

alAuxiliaryEffectSloti(mAuxiliaryEffectSlotID, AL_EFFECTSLOT_EFFECT, mCurrentEffectSlotID);

Then when the sounds in that zone need to be played you make it play through that aux slot by the following setting:

alSource3i(oGrenadeSound.SourceID, AL_AUXILIARY_SEND_FILTER, mAuxiliaryEffectSlotID, 0, AL_FILTER_NULL);

This sound will then be played through the auxiliary slot with the effect that was attached.

Once you realize that ‘Sound’, ‘Effect’, and ‘Aux Slot’ are all separate objects that need to bind together, this process is easier.

Ongoing development

OpenAL doesn’t move fast in their ongoing implementations, but the system does provide a nice framework for improving upon the baseline code and implementing any of your own dsp effects. Some community developers have done this already, with OpenAL Soft leading the pack for this. Making the switch from EAX to EFX is a small change for a big gain.

Few links for further reading:

http://en.wikipedia.org/wiki/Environmental_audio_extensions

http://climpxwss01.creativelabs.com/developer/Wiki/OpenAL%20SDK%20for%20Windows.aspx

http://en.wikipedia.org/wiki/Effects_Extension

Read more about:

Blogs
Daily news, dev blogs, and stories from Game Developer straight to your inbox

You May Also Like