Game Development Community

Enabling doppler effects in Torque

by Brett Fattori · 06/21/2003 (7:08 pm) · 11 comments

I decided that I wanted to implement doppler effects in Torque, and was going to simulate it using pitch shifting when I realized it already exists in the engine. The main issue isn't that it's not a part of OpenAL, it's that it isn't implemented. I noticed that the implementation of OpenAL already contains some doppler settings (in al.h):
/**
 * Knobs and dials
 */
ALAPI ALvoid	ALAPIENTRY alDistanceModel( ALenum value );
ALAPI ALvoid	ALAPIENTRY alDopplerFactor( ALfloat value );
ALAPI ALvoid	ALAPIENTRY alDopplerVelocity( ALfloat value );
The values, by default, are already set up for meters/second which is perfect for Torque! So, here we go on a short journey to enable doppler effects in Torque...

I can't stress this enough: BACK UP YOUR SOURCE BEFORE YOU TRY THIS!!! I know this is pretty obvious, but people will make changes and then blame me if it sends their project into buggy mayhem. So BACK UP YOUR SOURCE BEFORE YOU TRY THIS!!!

Did I mention that you should BACK UP YOUR SOURCE before adding any of this?

Quick Note: I did this with a May updated HEAD, not tested with 1_1_1 or 1_1_2, which appears to be short on some of the functional code necessary. I'm working on making a patch for HEAD that I can submit.

The actual problem boils down to Torque not supplying the velocity of an object to the OpenAL engine. We'll be making most of the modifications in audio.cc. I should say that I haven't verified this on anything other than Windows. If someone gets this working in Linux or Mac, let us all know what you've done.

Open up engine/platform/platformAudio.h and look for
void alxSourceMatrixF(AUDIOHANDLE handle, const MatrixF *transform);
Copy this and add an overloaded version right below it, so it looks like this:
void alxSourceMatrixF(AUDIOHANDLE handle, const MatrixF *transform);

// DK_911 - Overload for velocity
void alxSourceMatrixF(AUDIOHANDLE handle, const MatrixF *transform, const Point3F *velocity);
Next, just a few lines down you should see
// Listener
void alxListenerMatrixF(const MatrixF *transform);
Do the same as above (copy and paste) and write the overload to include velocity. It should look something like this when you are done
// Listener
void alxListenerMatrixF(const MatrixF *transform);

// DK_911 - Overload for velocity
void alxListenerMatrixF(const MatrixF *transform, const Point3F *velocity);
We're done with that, so save it and open up engine/audio/audio.cc. At the top, find
struct LoopingImage
{
Add a property to hold velocity. It should look like this:
struct LoopingImage
{
   AUDIOHANDLE             mHandle;
   Resource<AudioBuffer>   mBuffer;
   Audio::Description      mDescription;
   AudioSampleEnvironment *mEnvironment;
      
   Point3F                 mPosition;
   Point3F                 mDirection;
   Point3F                 mVelocity;  // DK_911 - velocity!

   ...
Next, locate the function void alxLoopSource3f and make it look like this:
void alxLoopSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat value1, ALfloat value2, ALfloat value3) 
{
   LoopingList::iterator itr = mLoopingList.findImage(handle);
   if(itr)
   { 
      switch(pname)
      {
         case AL_POSITION:
            (*itr)->mPosition.x = value1;
            (*itr)->mPosition.y = value2;
            (*itr)->mPosition.z = value3;
            break;

         case AL_DIRECTION:
            (*itr)->mDirection.x = value1;
            (*itr)->mDirection.y = value2;
            (*itr)->mDirection.z = value3;
            break;

         case AL_VELOCITY:  // DK_911 - Add velocity
            (*itr)->mVelocity.x = value1;
            (*itr)->mVelocity.y = value2;
            (*itr)->mVelocity.z = value3;
            break;
      }
   }
}
Then find void alxLoopGetSource3f and make it look like this:
void alxLoopGetSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat *value1, ALfloat *value2, ALfloat *value3) 
{
   LoopingList::iterator itr = mLoopingList.findImage(handle);
   if(itr)
   { 
      switch(pname)
      {
         case AL_POSITION:
            *value1 = (*itr)->mPosition.x;
            *value2 = (*itr)->mPosition.y;
            *value3 = (*itr)->mPosition.z;
            break;

         case AL_DIRECTION:
            *value1 = (*itr)->mDirection.x;
            *value2 = (*itr)->mDirection.y;
            *value3 = (*itr)->mDirection.z;
            break;

         case AL_VELOCITY:   // DK_911 - Add velocity
            *value1 = (*itr)->mVelocity.x;
            *value2 = (*itr)->mVelocity.y;
            *value3 = (*itr)->mVelocity.z;
            break;
      }
   }
}
Now we've added a way to set and retrieve velocity within looping audio sources. Next we need to supply the overloaded functions. Locate void alxSourceMatrixF and add the following overloaded version after it:
// sets the position and direction of the source
// overloaded to handle velocity of source - DK_911
void alxSourceMatrixF(AUDIOHANDLE handle, const MatrixF *transform, const Point3F *velocity)
{
   ALuint source = alxFindSource(handle);

   Point3F pos;
   transform->getColumn(3, &pos);

   Point3F dir;
   transform->getRow(1, &dir);


   if(source != INVALID_SOURCE)
   {
      // OpenAL uses a Right-Handed corrdinate system so flip the orientation vector
      alSource3f(source, AL_POSITION, pos.x, pos.y, pos.z);
      alSource3f(source, AL_DIRECTION, -dir.x, -dir.y, -dir.z); 
	  // DK_911 - Don't flip the velocity vector
      alSource3f(source, AL_VELOCITY, velocity->x, velocity->y, velocity->z); 
   }
   
   alxLoopSource3f(handle, AL_POSITION, pos.x, pos.y, pos.z);
   alxLoopSource3f(handle, AL_DIRECTION, dir.x, dir.y, dir.z);
   alxLoopSource3f(handle, AL_VELOCITY, velocity->x, velocity->y, velocity->z);
}
This is the overloaded version that allows us to set the velocity of a source audio object. Sources are anything that emits sound on a level. Your player is the listener (of which there is only one) which we'll need to overload next. Find void alxListenerMatrixF and put the following overloaded version after it:
/**   alListenerfv extension for use with MatrixF's
      Set the listener's position and orientation using a matrix
	  Overridden to allow velocity - DK_911
*/
void alxListenerMatrixF(const MatrixF *transform, const Point3F *velocity)
{
   Point3F p1, p2;
   transform->getColumn(3, &p1);
   alListener3f(AL_POSITION, p1.x, p1.y, p1.z);

   // Right-handed system needed here too?
   alListener3f(AL_VELOCITY, -velocity->x, -velocity->y, -velocity->z);
   
   transform->getColumn(2, &p1);    // Up Vector
   transform->getColumn(1, &p2);    // Forward Vector

   F32 orientation[6];
   orientation[0] = -p1.x; 
   orientation[1] = -p1.y; 
   orientation[2] = -p1.z; 
   orientation[3] = p2.x; 
   orientation[4] = p2.y; 
   orientation[5] = p2.z;

   alListenerfv(AL_ORIENTATION, orientation);
}
This is the overloaded version of the listener's routine. You'll notice that I used negative velocities. This was just a preference, and I doubt it is correct. For some reason, it made sounds that are audible to your player sound right.

Save audio.cc to complete the addition of everything necessary to get doppler shift effects working. For sanity's sake, at this point, I compiled my copy of audio.cc to make sure everything was added properly. Don't compile the project just yet, only this one file. You don't have to, but it helps before you go and make the next changes.

We now need to make the changes to objects that emit sounds. The actual objects that we'll be modifying are ShapeBase class objects. I'd suggest doing a search for alxSourceMatrixF in your project. Everywhere you see it, that derives from a ShapeBase should have the overloaded version of the function supplied. The following are a few places I changed it.

Open up engine/game/player.cc and locate the function void Player::updateWaterSounds(F32 dt). There are two instances of alxSourceMatrixF that look something like this:
alxSourceMatrixF(mMoveBubbleHandle, &getTransform());
Add &getVelocity() to the end of the function call so it looks like this:
alxSourceMatrixF(mMoveBubbleHandle, &getTransform(), &getVelocity());
Save player.cc and then open engine/game/projectile.cc and locate the function void Projectile::updateSound. Locate the reference to alxSourceMatrixF and then change it so it looks like this:
alxSourceMatrixF(mProjectileSound, &projectileMat, &vel);
Save, and then open engine/game/shapeBase.cc. Locate alxSourceMatrixF and change it to:
alxSourceMatrixF(sh, &getTransform(), &getVelocity());
Save, wash, rinse, repeat... As you can see, there are a few places where this occurs. You have to make sure that velocity is either supplied to you or that you can get ahold of the velocity vector.

Finally, after you've made all the changes where alxSourceMatrixF appear, open up engine/game/game.cc and locate the function bool clientProcess. The last change we need to make is updating the listener's velocity. Find the section that looks like this:
if (GameGetCameraTransform(&mat, &velocity))
   {
      alxListenerMatrixF(&mat);
      alxListenerPoint3F(AL_VELOCITY, &velocity);
      collisionTest.collide(mat);
   }
If yours looks differen than this (ie: velocity isn't being supplied, you can simply change it to what you see above. Or you could enter the code below. It's your call...
if (GameGetCameraTransform(&mat, &velocity))
   {
      alxListenerMatrixF(&mat, &velocity);
      collisionTest.collide(mat);
   }
At this point, you should be done. You're going to need to do a CLEAN BUILD and then jump into the game. Hopefully you have something that will cause a doppler shift, as this effect isn't very interesting otherwise. I just set up the camera at a point away from my player and fired a missile at it. When I switched to the camera, the doppler effect was readily apparent as the missile approached and passed.

Oh, one other thing... I updated my system with the latest build of the Creative OpenAL SDK. Not sure if you'll want to do this... Again, it's your call. Let me know if you have problems, or improvements.

#1
06/21/2003 (7:23 pm)
Awesome, useful change - and a well written resource, too. Going to put up a patch file?
#2
06/21/2003 (8:10 pm)
Great resource. I'd try it on Linux, but Torque won't compile for me under Mandrake 9.1.
#3
06/21/2003 (10:32 pm)
2 questions

When i search for the 'alxSourceMatrixF' line it only appears in platformaudio.h

I know this is becuase it doesnt recognise the .cc extension (even though ive setup the registry as required. Even if i specifically state look for *.cc extensions it wont find them. How do you set it up to search through .cc files ?

Also in projectile.cc my update sound function looks like the following

void Projectile::updateSound(Point3F &pos, Point3F &vel, bool blah)
{
pos;
vel;
blah;
}

am i missing something ?
#4
06/22/2003 (10:10 am)
@Ben - I guess I could put up a patch file. I figured this best fit as a resource, but I'll see about getting a patch together.

@Ian - I use a second program (TextPad) to locate things I'm searching for in code. I don't trust the MS file search anymore. I guess I should say that I did this in a May version of HEAD. I don't know if this will work with 1_1_2 or 1_1_1.

Here's what I have in projectile.cc:
void Projectile::updateSound(Point3F &pos, Point3F &vel, bool blah)
{
   MatrixF	projectileMat(true);
   projectileMat.setPosition(pos);

   if (mProjectileSound) {
      // DK_911 - Implement velocity
      alxSourceMatrixF(mProjectileSound, &projectileMat, &vel);
      alxSourcef(mProjectileSound, AL_GAIN_LINEAR, 2.0);
   }
}
#5
06/23/2003 (9:43 am)
Where is mProjectileSound defined? I get an undeclared identifier error in projectile.cc.
#6
06/23/2003 (11:20 am)
You know what... I just realized something!! Projectiles don't have sound by default, I had to add it. :-P

Okay, I guess when I get home I'll add that to this resource as a bonus. I have missiles and other projectiles that make sound as they fly, so I attached a simple sound updater to the projectiles.

Sorry.. I tend to forget how much customization I've done to my engine so far!

EDIT: I haven't had a chance to update the resource. I think, however, that I'll just create a new one for adding sound to projectiles. Both will soon be submitted as patches, too...

- Cow
#7
07/01/2003 (9:53 am)
Projectile sound would be cool. Nothing like hearing a rocket wizz by your head :)
#8
04/05/2004 (7:43 am)
I sounds really neat .... perhaps would it be great if it was in the torque Standard (i mean in the latest update).
#9
01/08/2005 (1:25 pm)
WARNING: This resource does not appear to work with 1.3
#10
02/04/2007 (1:05 pm)
Works for me with 1.5. Few tweaks here and there to make it fit.
#11
12/06/2008 (10:49 am)
Has anyone looked into adding this to TGEA? I looked at it, and the code is completely different with the new SFX system. Is anyone able to make an attempt at it?

Thanks.