Table of Contents

Integrating Listeners


A listener represents a microphone position in the game so 3D sounds can be assigned to the speakers to mimic a real 3D environment. This is done by mapping the game objects' positional information into the listener's coordinate system. This coordinate system is defined by a position vector, and front and top orientation vectors. It is therefore necessary to keep the listener's positional information up to date, otherwise sounds will be rendered through the wrong speakers.

Setting the Listener's Positioning Information

The AK::SoundEngine::SetListenerPosition() function is used to set the listener's position. This should be done every time any of the listener's position or orientation vectors change.

AkListenerPosition listenerPosition;

(... set the position and orientation members of listenerPosition here...)

AK::SoundEngine::SetListenerPosition( listenerPosition );

The AkListenerPosition structures hold the vectors that define the listener's location and orientation in the game's 3D space:

struct AkListenerPosition
    AkVector        OrientationFront;   // Orientation of the listener
    AkVector        OrientationTop;     // Top orientation of the listener
    AkVector        Position;           // Position of the listener
Note: The OrientationFront vector defines the direction that the listener's head is facing. It should be orthogonal to the OrientationTop vector, which defines the incline of the listener's head. For a human listener, one could think of the OrientationFront vector as the listener's nose (going away from the face), while the OrientationTop vector would be orthogonal to it, going between the listener's eyes and away from the chin.

Refer to X-Y-Z Coordinate System for information regarding how the X, Y and Z axes are defined in the Wwise sound engine.

The orientation vectors must be defined for the audio to be rendered properly. They cannot be zero vectors and should be unit vectors; if not they will be converted to unit vectors. They should be at right angles. If not, the front orientation vector will be adjusted.

AK::SoundEngine::SetListenerPosition() has a second, optional parameter, in_ulIndex. It represents the listener index, and should be a value between 0 and 7 (inclusive). Refer to Multiple Listeners for more information.

Note: The listener's position is updated at most once per frame. Even if multiple calls to the AK::SoundEngine::SetListenerPosition() function were made, only the last value will be considered when AK::SoundEngine::RenderAudio() is called.
Tip: If you are experiencing unexpected sound rendering, for example what was expected on the left speakers is actually heard on the right speakers, check the listener's positional information that is provided to the sound engine through the AK::SoundEngine::SetListenerPosition() function. You may try to set a known constant listener's position and check that the rendering is correct in that case to rule out any mixup in the X, Y, and Z axes. For more information about this, refer to X-Y-Z Coordinate System.

Multiple Listeners

In a single-player game where you always see only one point of view in the game, one listener is enough. However, if multiple players can play on the same system, or if multiple views are displayed at the same time, each view requires its own listener so audio is appropriately rendered for all of these views.

The main difficulty involved with implementing multiple listeners comes from the fact that the positioning of the sound sources doesn't always makes sense in relation to what players are seeing. This is mostly caused by a game using only a single set of speakers to reproduce a 3D environment for several players.

A simple representation of this problem is shown in the following figure. It is very hard to tell in which speakers the source should be played, because Listener 0 expects to hear the source in the left speaker while Listener 1 expects to hear it in the right one.


Figure: Two listeners hearing the same source in different speakers

The Wwise sound engine supports up to eight listeners, identified by indices 0 to 7. The following sections describe how the Wwise sound engine lets the programmer manipulate these listeners to achieve the expected behaviour.

Note: Multiple listener functionality affects sound objects set in 3D Game-defined mode. Objects set in 2D or 3D User-defined modes are not concerned since they are positionned in the various speakers independently from the position of the sources and listeners in the game environment.
Note: Everything related to multiple listeners is only available through game programmer implementation via the SDK. There are no special options in the Wwise authoring application to manage the in-game positioning of sources for multiple listeners.

Multiple Listeners: Capturing Sources

By default, all sources are assigned to listener 0 only, but developers can assign them to any of the 8 listeners. For each source, distance and cone attenuation are computed individually relative to each listener on which they are active.

Volume Attenuation Management

When multiple listeners capture a source, Wwise checks the volume of all channels for every listener and keeps the loudest volume for each of these channels.

Here's an example of two game listeners capturing the same source.

Listener 0
(Volume in dB)
Listener 1
(Volume in dB)
Final Volume for
the source
Front Left -6 -56 -6
Front Right -18 -15 -15
Center -54 -37 -37
Rear Left -7 -48 -7
Rear Right -54 -9 -9
LFE -96.3 -96.3 -96.3

LPF Attenuation Management

Here is how the sound engine computes the final low pass filter to apply on each source:

  1. For each listener on which this source is active
    1. Compute the LPF based on distance between the source and this listener
    2. Compute the LPF based on angle between the source and this listener
    3. Keep the highest of the two values
  2. Take the lowest value among all the listeners, and add the object's LPF (regular value and RTPC)

In the example detailed in the following table, the value for listener 0 is max( 10, 40 ) = 40, and the value for listener 1 is max( 50, 10 ) = 50. The lowest of the two is 40, which is then added to the object's value of 5 to produce the final value, 45:

Listener 0 Listener 1 Object Final LPF for
the source
Cone LPF Radius LPF Cone LPF Radius LPF
LPF 10 40 50 10 5 45

Assigning Game Objects to Listeners

By default, every game object instantiated in the game is assigned to listener 0 only. However, the game developer has the flexibility to dynamically assign or unassign any game object from any listener.

For example, here is a situation in which it is desirable for a listener to not hear a source despite being close enough to normally hear it.


Figure: L1 should not hear the source at all

In this figure, we see that it would be unlikely for L1 to hear the source because of the occlusion of the wall between them. For this scenario, the developer could temporarily un-assign the game object that plays S1 from L1. If you wish to use real occlusion, refer to Setting Obstruction and Occlusion.

The AK::SoundEngine::SetActiveListeners() function lets you specify, for a given game object, which listeners should be activated:

virtual AKRESULT AK::SoundEngine::SetActiveListeners(
    AkGameObjectID in_GameObjectID,             // Game object identifier
    AkUInt32 in_uListenerMask                   // Bitmask representing active listeners (LSB = Listener 0, set to 1 means active)
) = 0;

To specify which of the eight listeners are active for that game object, set the corresponding bits to 1 (active) or 0 (inactive) in the value passed as the second parameter. The least-significant bit (LSB) represents Listener 0:

// Activate only Listener 0 on this Game Object
AK::SoundEngine::SetActiveListeners( gameObject1, 0x00000001 );

// Activate Listeners 1, 2 and 3 on this Game Object (Listeners 0 and 4 to 7
// are deactivated for this Game Object)
AK::SoundEngine::SetActiveListeners( gameObject2, 0x0000000E );

Volume Offset and Spatialization

Spatialization is the positioning of 3D Game-Defined sounds across the various speakers based on the positions of those sounds relative to the listeners.

However, if the game is played by two players on a split screen, you might want to hear listener 0 (the first player) in the left speakers and listener 1 (the second player) in the right speakers, completely bypassing regular positioning of sounds across speakers based on their positions relative to each listener.

To give more control and flexibility, Wwise allows the game programmer to disable spatialization for a given listener, and optionally set custom volume offsets for each channel, thus specifying how the sounds captured by this listener will be heard in each speaker.

These settings can be modified for each listener by calling AK::SoundEngine::SetListenerSpatialization():

virtual AKRESULT AK::SoundEngine::SetListenerSpatialization(
    AkUInt32 in_uIndex,                         // Listener index (0: first listener, 7: 8th listener)
    bool in_bSpatialized,                       // Spatialization toggle (True : enable spatialization, 
                                                // False : disable spatialization)
    AkChannelConfig in_channelConfig,           // Channel configuration associated with volumes in_pVolumeOffsets. Ignored if in_pVolumeOffsets is NULL.
    AK::SpeakerVolumes::VectorPtr in_pVolumeOffsets = NULL  // Per-speaker volume offset, in dB. See AkSpeakerVolumes.h for how to manipulate this vector.
) = 0;

The first parameter is the listener index (from 0 to 7). The second parameter must be set to True to enable spatialization for this listener and False to disable it. Finally, the two last parameters represent a vector that contains the attenuation, in dB, for each channel on that listener. If in_bSpatialized is False, then it sets the volume for each channel, which are 0dB by default. If in_bSpatialized is True, it offsets the volume computed by default 3D game-defined positioning computation by a given amount for each channel.

The volume vector is tied to the channel configuration in_channelConfig. If in_channelConfig means 5.1, then the volume vector should have 6 values. Use functions defined in the AK::SpeakerVolumes::Vector namespace to manipulate it. The channel ordering corresponds to the channel mask bits defined in AkSpeakerConfig.h, except for the LFE which is always at the end.

For the example where two players use a split screen, the programmer could use the following code:

// Define speaker offsets using a 7.1 speaker setup (if platform supports it).
AkChannelConfig cfg;
cfg.SetStandard( AK_SPEAKER_SETUP_7_1 );

AkUInt32 uVectorSize = AK::SpeakerVolumes::Vector::GetRequiredSize( cfg.uNumChannels );
AK::SpeakerVolumes::VectorPtr vVolumes = (AK::SpeakerVolumes::VectorPtr)AkAlloca( uVectorSize );

vVolumes[0] = 0.f;      // Left
vVolumes[1] = -96.3f;   // Right
vVolumes[2] = -6.f;     // Center
vVolumes[3] = 0.f;      // Rear left
vVolumes[4] = -96.3f;   // Rear Right
vVolumes[5] = 0.f;      // Side left
vVolumes[6] = -96.3f;   // Side Right
vVolumes[7] = 0.f;      // LFE

AK::SoundEngine::SetListenerSpatialization( 0, false, cfg, vVolumes );

vVolumes[0] = -96.3f;   // Left
vVolumes[1] = 0.f;      // Right
vVolumes[2] = -6.f;     // Center
vVolumes[3] = -96.3f;   // Rear left
vVolumes[4] = 0.f;      // Rear Right
vVolumes[5] = -96.3f;   // Side left
vVolumes[6] = 0.f;      // Side Right
vVolumes[7] = 0.f;      // LFE

AK::SoundEngine::SetListenerSpatialization( 1, false, cfg, vVolumes );

If the bus in which sounds are routed has a channel configuration different than 7.1 per its user-defined channel configuration, the vector will be downmixed internally, using standard downmix recipes, before being applied to sounds.

To go back to regular spatialization, you would call:

// Enable regular spatialization on listeners 0 and 1
AK::SoundEngine::SetListenerSpatialization( 0, true );
AK::SoundEngine::SetListenerSpatialization( 1, true );

Volume Pipeline

The following figure exposes, in the order they are performed, the different operations made on every sources for each listener to compute the final volume in each speaker:


Figure: Volume Pipeline in the Wwise Sound Engine

See also: