3D Sound Tutorial - Firelight Technologies Pty, Ltd.

# 3D SOUND AND GAME CODING

Introduction

3D Sound in FMOD is accomplished through a various different API's which are handled transparently to you through one unified interface. These are DirectSound 3D with EAX 2.0 and EAD 3.0/HD support, or FMOD fallback software 3D stereo engine.
It is recommended when you see an API function highlighted as a link, that you check the API reference for more detail.

Hardware Support

Allocating samples with FSOUND_HW3D is the key to getting your sounds to use 3D hardware acceleration.
You do this when loading wav, raw, mp3, etc samples, through the following function.
- FSOUND_Sample_Load(int index, const char *name_or_data, unsigned int inputmode, int offset, int length); Bitwise OR in the FSOUND_HW3D flag into the mode parameter of the loading function.
  If you have the correct hardware, the sound will be loaded into hardware mode.
  If not, the sample will be loaded in software mode, transparently to the programmer, but it will not be accelerated or take advantage of advanced 3d algorithms and EAX.
  Here is an example of a wav file attempting to be loaded as a 3D Hardware buffer.
  FSOUND_Sample_Load(FSOUND_FREE, "engine.wav", FSOUND_HW3D | FSOUND_LOOP_NORMAL, 0, 0);
  Or you can force it into software by not specifying the FSOUND_HW3D flag
  FSOUND_Sample_Load(FSOUND_FREE, "engine.wav", FSOUND_LOOP_NORMAL, 0, 0);
  Or you can have it as a 2D sound (2d sounds are not affected by 3d functions, and are rendered in software), using the FSOUND_2D flag.
  FSOUND_Sample_Load(FSOUND_FREE, "engine.wav", FSOUND_2D | FSOUND_LOOP_NORMAL, 0, 0);

Typical game loop

This would be a typical example of a game audio loop.
do
{

UpdateGame(); // here the game is updated and the sources would be moved with FSOUND_SetAttributes
FSOUND_3D_Listener_SetAttributes(listenerpos, listenervel, fx, fy, fz, tx, ty, tz); // update 'ears'
FSOUND_Update(); // needed to update 3d engine, once per frame

} while (gamerunning);
Most games usually take the position,velocity and orientation from the camera's vectors and matrix.

Velocity parameter

Velocity is only required if you want doppler effects. Otherwise you can pass NULL to both FSOUND_3D_Listener_SetAttributes and FSOUND_3D_SetAttributes for the velocity parameter, and no doppler effect will be heard
This is stressed over and over again, but can't be stressed enough. It is important that the velocity passed to FMOD is METERS PER SECOND and NOT meters per FRAME.
What does this mean? Use proper velocity vectors from physics code etc, and don't just subtract last frames position from the current position, as this is affected by framerate. (ie the higher the framerate the smaller the position deltas, and therefore smaller doppler effects, which is incorrect)
If the only way you can get the velocity is to subtract this and last frame's position vectors, then remember to time adjust them from meters per frame back up to meters per second.
This is done simply by scaling the difference vector obtained by subtracting the 2 position vectors, by one over the frame time delta.
Here is an example
velx = (posx-lastposx) * 1000 / timedelta;
velz = (posy-lastposy) * 1000 / timedelta;
velz = (posz-lastposz) * 1000 / timedelta;
timedelta is the time since the last frame in milliseconds. This can be obtained with functions such as timeGetTime().
So at 60fps, the timedelta would be 16.67ms. if the source moved 0.1 meters in this time, the actual velocity in meters per second would be
vel = 0.1 * 1000 / 16.67 = 6 meters per second.
Similarly, if we only have half the framerate of 30fps, then subtracting position deltas will gives us twice the distance that it would at 60fps (so it would have moved 0.2 meters this time)
vel = 0.2 * 1000 / 33.33 = 6 meters per second still! phew!.

Orientation and coordinate systems

Getting the correct orientation set up is essential if you want the source to move around you in 3d space.
FMOD Uses a left handed coordinate system, (x = right, y = up, z = forwards), which is the same as DirectSound3D and A3D
If you use a different coordinate system, then you will need to flip certain axis or even swap them around inside the call to FSOUND_3D_Listener_SetAttributes.
Take the right handed coordinate system, where Z points backwards, or comes out of the screen at you. To convert this to FMOD coordinate system simply negate the Z coordinate of the listener up and forward vector.
Just think RIGHT, UP, FORWARDS for each element of a vector (ie x,y,z), and map whatever axis fit your model into this, to generate the 2 vectors that points forwards and up.

Channel resource management and low end cards

Some soundcards only support 4 or 8 3D hardware channels, whereas other soundcards support 32 and 96 hardware 3D channels.
What do you do in your game? only play 4 3D hardware based sounds at once to meet the base spec? Not likely. It would be ridiculous to limit yourself to these 4 channels when you have the potential of other high end cards that can do nearly 100 hardware channels at once.
The solution is to use FSOUND_SetMinHardwareChannels. This function is called once before calling FSOUND_Init.
What this allows you to do is assume a number of channels will either ALL be played in hardware, or NONE in hardware (therefore in software).
It basically says 'if the card doesn't have n number of channels, then mix all sounds in software'. This allows you to assume that you have say 16 sounds to be playing at once, and if the hardware has at least that many channels available, it will play them all in hardware. If you come across a card that only supports 4 hardware channels at once, then it fails the criteria and your sounds are played all in software.
The best thing about this is NO PlaySound's fail because of running out of channels on unexpected cards! You can guarantee a certain number of channels available, and if the hardware is there, then all the better!

Use priorities!

FMOD's priority system is there to be used. If you trigger lots of sounds on a limited number of channels, some might be more important than others.
If you were doing a first person shooter, the player's gunshot sound would be one of the most important sounds. So it should have a high priority set with FSOUND_SetPriority. Other less important sounds would be rejected if the number of allocated channels became full, and that gunshot needed to be heard!