Skip to main content
gamedev.net
🔒 Locked 🎮 Unity

OpenAL Question

Started by dgreen02 Oct 12, 2005 at 9:17 PM 1 replies 3.3k views
Original Post
dgreen02
dgreen02
Hey guys, I've recently switched my game Gang War over from my DirectSound implementation, to use OpenAL. Everything works great, sounds great. There is one issue though...sometimes calls to alGenSources or alDeleteSources will fail...with the error "Invalid Name". Due to the large nature of the game environments and the large number of potential sound sources (vehicles in particular, I have 1000s in each city) that need to constantly emit sounds, I've come up with this method for handling the large number of potential sound sources. Basicly I'm creating a sphere around the camera and dynamically creating/deleteing the sound sources of the vehicle as it enters/leaves the shpere around the camera.

class CVehicle
{
 ....  
   
 private: 

  bool m_SoundActive; //If the sound source is active or not.
  ALuint m_SoundSource[1]; //Sound source for the vehicle.

  ...
}




void CVehicle::Update(void)
{
 ...

 //Entering sphere
 if(m_LOD <= VEHICLE_SOUND_DISTANCE_CUTOFF && m_SoundActive == false)
 {
  //Create source, bind source to engine sound, play sound, etc.
  m_SoundActive = true;
  alGenSources(1, m_SoundSource);
  if((error = alGetError()) != AL_NO_ERROR)
  {
   GameEngine.LogEntry("ERROR, failure to generate vehicle source : %s", alGetString(error));
   return;
  } //end of if

  alSourceStop(m_SoundSource[0]);
  alSourcei(m_SoundSource[0], AL_BUFFER, GameEngine.OpenAL.m_Buffers[VehicleTypes[m_VehicleType].m_PrimarySoundIndex]);
  alSourcei(m_SoundSource[0], AL_LOOPING, AL_TRUE);
  alSourcePlay(m_SoundSource[0]);
  alSourcef(m_SoundSource[0], AL_GAIN, GameVariableValues[34]);
  alSourcef(m_SoundSource[0], AL_ROLLOFF_FACTOR, 0.09);
 } //end of if

 //Already in sphere
 if(m_LOD <= VEHICLE_SOUND_DISTANCE_CUTOFF && m_SoundActive == true)
 {
  //Update source with new position, velocity, frequency, etc. 
  float sPosition[3] = { -m_Position.x, m_Position.y, -m_Position.z };
  float sVelocity[3] = { -m_MovementVector.x, m_MovementVector.y, -m_MovementVector.z }; 
  alSourcefv(m_SoundSource[0], AL_POSITION, sPosition);
  alSourcefv(m_SoundSource[0], AL_VELOCITY, sVelocity);   
  alSourcef(m_SoundSource[0], AL_PITCH, (m_RPM/m_Redline)*1.5);  
 } //end of if

 //Exiting sphere
 if(m_LOD > VEHICLE_SOUND_DISTANCE_CUTOFF && m_SoundActive == true)
 {
  //Delete sound source
  m_SoundActive = false;	 
  alSourceStopv(1, m_SoundSource);
  alDeleteSources(1, m_SoundSource);
  if((error = alGetError()) != AL_NO_ERROR)
  {
   GameEngine.LogEntry("ERROR, failure to delete vehicle source : %s", alGetString(error));
   return;
  } //end of if
 } //end of if

 //Already outside sphere
 if(m_LOD > VEHICLE_SOUND_DISTANCE_CUTOFF && m_SoundActive == false)
 {
  //Do nothing, the vehicle is too far away
 } //end of if
} //end of Update function


That code is inside the CVehicle::Update() member function of the vehicle class. This function handles the updates of the vehicles and is called every timestep. These are also the calls to alGenSources & alDeleteSources that fail occasionally...I noticed that some vehicles in game do not have engine sounds, which would make sense if the calls fail. I'm wondering if OpenAL allows such frequent creation/destruction of sound sources, or if there are any better methods to use when approaching this problem...I've only been using OpenAL for a few days, I've got a pretty good grasp of it, though information about this type of situation is somewhat hard to come by. I'd appreciate any advice, seasoned OpenAL programmers may have to offer :-D - Dan
Drew_Benton
Drew_Benton
Ideally, you will want to be dynamically creatin/destroying sound buffers rather than sources. The general aim is to create a set number of sources that your computer can support, which varies on hardware, then reuse those sources as needed. You can only generate/destroy so many sources before you cannot create anymore, even if you destroy them all. Because of this, you will definitly want to use the buffer instead, which you can create and destroy on will without any problems, assuming the hardware can handle it.

So what you will want to do is something along these lines if possible, I'm not sure if this approach will work well for your game, since it has a rather large scale, but it's worth a shot:

1. Create an array of X sources. Starting out, you could try using 32 and see if that works for your game. I know most computers should be able to handle that, but some can do less, and others more, like 64. You can wory about finding the *right* value for any computer later on. What you will want to do is then do the alGenSources on that array, creating it.

2. Create an array of X integers. This will be used to track the current states of each of the sources. I choose to do this because it's a lot more computatively inexpensive to check the state of a source once, then store that result in an integer rather than continously call the alSourceState function, for the same state multiple times. You will not need the accuracy of having the *currentest* state, because of the next step.

3. In your main loop, you will want to update the status of all the states and store those results. You will loop though each state and call the alSourceState, then handle what you want to store in the array of ints. Because you are doing this at the beginning of one game cycle, and you use those results thoughout, it will be accurate enough to meet your needs while providing an effective performance solution. Even in that respect, you can set up a thread probabally that only updates the sounds X times a second instead if you wanted to as well, further reducing CPU usage for that aspect. It is something that you will need to play with. (Assuming you can use threads with OpenAL functions - thread safe functions)

4. Now, when you want to play a sound in any entity, you generate the new buffer as you have it now (except now you have sources), so just make that quick change. However, you will need to send the buffer to a source, so you will need to make a function, such as GetNextFreeSource, that will loop though the array of source states and check to see which one is not being used. That is the main reason why I have the #2 above, since you will probabaly call GetNextFreeSource *a lot*, it's important not to be calling that alSourceState [wink].

5. Now on a good day, you have your source for you buffer yo play in, and you are done. However, there may be a slight delay between sounds, or if all sources are being used up. If this happens you have two chocies, you can either make it so when you try to get a free source, and it returns -1 for failure, you can jsut drop the sound altogether. You could perhaps add a flag to your sounds to know if you *should* drop that sound or not, based on importance. In that case, you would then need to setup a queue system so your sounds will play with the next free source is avaliable. Once again, it will depend on your system, if you have sounds that seem to really fall out of sychronization, then you will probabaly not want to do that queuing system. If you had a queuing system, you would check it at the start of the update function and immediatly play those sounds to get them out.

That's about it really. It all will depend on how your large environment works out. If you have any questions about that feel free to ask! Below are some threads that you might also want to take a look into extra reading.



Here's a few threads to take a look at:
Link 1
Link 2

And also if you stream sounds, make sure to check these out:
Link 3
Link 4

If you haven't checked these tutorials out, they are good reading for some general information:
DevMaster.Net

I know I had a thread somewhere that explains it all, but I can't find it right now (the search does work a lot better now!)
dgreen02
dgreen02
Hey Drew - Thanks a lot for the help! I read over your post and went back and got everything working perfectly.

I had to redesign a few things, but it didn't take too long, and everything's rock solid now...and a lot faster too I'm sure.

I didn't know there was a limit to the # of sources OpenAL would let you create/destroy before it starts crapping out, thanks for the tip!

I was using a system similar to what you described for my other dynamic sounds...gunshots, explosions, etc. I had a counter that just looped around an array starting back at 0 when it hit the end of the array...that method wouldn't work for looping sounds, but worked fine for short gunshots, or crashes. So I had to create this special case for the looping vehicle engine sounds, also I need to keep track of them so I can adjust their frequency/position which is less important for an "impulse-like" sound, a gunshot for instance.

I was trying to find a state-query command, -

alGetSourcei(Source[0], AL_SOURCE_STATE, &State);

- is what I needed. For some reason I must have missed it in the documentation, I've got an excellent grasp of it now though :-D

I do as you suggested and update an array with the states of each source once a frame, then reference it using a GetFreeSource() function. I now use this system for all dynamic sounds. Though I have 4 other sources deticated to playing ambient sounds & music, as well as 2D sounds like GUI sounds and stuff.

24 is my magic limit to the number of dynamic sources, that seems like a pretty safe limit, and it works just fine in-game. Should hold up even in dense situations, as I'm very stingy about what sounds even get played.

I was thinking about doing it this way from the start...I should have, I was doing something similar in my previous DirectSound implementation.

Well, thank you very much for your detailed response...it's working great now. It's easy to see why you're the highest rated person on GameDev ;-). Thanks again.

- Dan

[Edited by - dgreen02 on October 13, 2005 5:43:48 PM]

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.