suggestions on c++ game design with openAL most welcome

View: New views
7 Messages — Rating Filter:   Alert me  

suggestions on c++ game design with openAL most welcome

by Pingwah Leronz :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Some parts of this message have been removed. Learn more about Nabble's security policy.
First of all, hello everyone :)
 
 
I'm working on a c++ project that's moving into the early stages of audio design and facing what Im guessing would be a fairly common problem, and wondering if anyone has any advice or input about the best way to approach it.
 
For those interested -
Let's say we have a car
class classCar()
{
    public:
    classCoords coordsPosition;
    classCoords coordsVelocity;
    float fltFuel;
    ...etc.
 
};
 
where classCoords is simply
coords
{
  public:
  float x, y, z;
 
};
 
and Cars are to be able to emit several sounds at once , an ambient engine roar, horn beeps, drivers yelling etc.
Also there's to be literally millions of cars (OK, in the project itself , they're not cars :) ), so a method of culling sound emitters from the set needs to be there. 
Criteria for adding and removing Cars from the audio set is simply proximity to a viewing position, and the cars themselves are stored in something like
 
 
multimap <classCoord*, classCar*> mapCars;
 
(again, they're not cars- let's say the track consists of many discrete positions, each of which can contain many cars).
 
I've been experimenting with two methods of handling this - the first with a global controller:
 
Method 1
---------------------------------
class classAudioController()
{
     int MAX_SOUNDS = 150;
     multimap <classCar*, ALuint> mapSounds;     //where ALuint here is a ref to the standard openal source[MAX_SOURCES]
 
     //functions
     void AddCar(classCar* newCar, ALuint* newAudioSource);
     bool AssignBufferToSource(ALuint *intSource, ALbyte* filename);
     void UpdateAllPositionsAndVelocities();
     void UpdateCarsPositionAndVelocity(classCar* findCar);
     etc.
 
 
};
 
 
and also by a more object oriented
 
Method 2
----------------------------------
classCar
{
   //position, fuel, health etc.
   //....
   classAudioSource *soundSource[MAX_SOURCES];   //remember, each car can emit many simultaneous sounds
 
 
}
 
where classAudioSource is
{
   ALuint intSource
 
   //funcs
   bool AssignBufferToThis(ALbyte *filename);
   void UpdatePosition(float x, float y, float z);
   void Play();
   void Stop();
   void PitchMod(float fltMod);
   void GainMod(float fltMod);
   void SetLooping(bool boolLooping);
   
   etc.
 
}
 
 
 
With Method 1, Cars are added and removed from the emitters map on movement events, In method 2 classAudioSources are triggered to Play/Stop based on listener proximity , again on movement events.
I'm running into problems with both, mainly from unfamiliarity.
 
If anyone here has input into handling large sets of sound sources in a good OO-heavy way, or can point me to any reading that does I'd be massively grateful.
 
Thanks,
Pingwah
 
 
 
 
 


Find car news, reviews and more Looking to change your car this year?
_______________________________________________
Openal mailing list
Openal@...
http://opensource.creative.com/mailman/listinfo/openal

Re: suggestions on c++ game design with openAL most welcome

by Space Ship Traveller :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hello,

I have a number of comments for you.

Firstly, looking at your code I would suggest you review your naming conventions.

"class classCar" is not a conventional name. If it suits you, that is fine, but it seems like you've got an incredibly verbose naming scheme which many people will find hard to look at.

A good scheme is to have UpperCase class names which are typically common or proper nouns. Secondly, variables don't need to include their type in the name. This is generally considered tedious and unmaintainable. For example, fltFuel might change to dblFuel. But now you have to change a lot of code. There are lots of information regarding different kinds of coding conventions available on the internet.

Also, you have made all your member variables public. This is also considered harmeful, for example if you change the implementation or storage of fltFuel, this now becomes a project wide change. The better option is to rely on encapsulation which generally means you have getters/setters for key values related to the object you are trying to represent. For example, rather than having setFuel(...) you might have a function addFuel(...) which carries out logic such as checking the maximum fuel that can be added, etc, which in my experience reduces the complexity of code elsewhere.

Looking at the solutions you are proposing - consider what best models what you are trying to represent.

For example, if you have multiple cars, each car has an engine, therefore each engine produces a sound. We can consider this the ideal world. In computer programming we need to use tricks to achieve adequate performance. So, it might be the case that you have up to 5 sources dedicated to engine sounds, and then choose the 5 engines closest to the "camera" for actually playing audio. If you have more than "x" sources close to the "camera" it may become impossible for the listener to identify individual sounds anyway, so we can limit to 5 without any loss of apparent quality.

I would question your use of a multimap for storing cars. It seems incredibly complex and performance heavy.

Consider the use of a simple std::vector. This is very fast and has guaranteed O(1) performance for all operations if you are not concerned about order. To remove an element from a vector simply use std::swap and pop:

/// Provides an efficient way to erase elements from a std::vector
template <typename t>
bool eraseElementAtIndex (IndexT index, std::vector<t> & array)
{
if (array.size() == (index+1)) {
array.pop_back();
return false;
} else {
array[index] = array.back();
array.pop_back();
return true;
}
}

So we can achieve very quick storage with std::vector.

If you need to do space querying, for a small number of cars (< 20) brute force algorithm will be fine, such as simply iterating through the list  and choosing the closest cars, etc.

Finally in terms of structuring the OpenAL code the best option is to look at keeping audio completely separate from your "model".

Try to avoid things like:
     int MAX_SOUNDS = 150;

This generally considered bad practice (there are specific cases where it is okay, but generally use std::vector for this kind of allocation).

We should consider the importance of two kinds of sounds:
- Sound that caused by specific event that occurred (crash sound).
- Sound that has been started and stopped by a particular object (engine sound).

These two kinds of sounds can be managed differently. For example, often with sounds based of event, my experience is that we can't tell the difference between single source and multiple source in typical cases as long as the sound is short - for example, explosion sound. So in this case you can simply have a single source which is allocated when the game starts. This is very convenient. Alternatively you can allocate a list of sources and use them in a round robin fashion if you need more realism or the sound is longer.

For a sound that is continuous we either have a single source (i.e. music) or multiple sources (i.e. multiple engines). This case is slightly more complex especially if we separate audio function from object model (class Car). In this case, the easiest solution is to have the audio source as part of the class Car. This is the simplest solution and means that each car has its own audio source. But this isn't efficient for a large number of cars, and increases coupling between audio and your simulation which if it is running as a server / client, obviously you don't want the server making all sorts of sounds or sound simply may not be available.

Therefore, I recommend you look at the following structure:

using boost::shared_ptr;

class Car {
public:
Coord getPosition ();
};

class CarsController {
typedef shared_ptr<Car> CarPtr;
std::vector<CarPtr> m_cars;

std::vector<CarPtr> findCarsClosestToPoint (Coord c, unsigned maxCars);
void update (float dt);
};

class AudioSource
{
   ALuint intSource
 
   //funcs
   bool AssignBufferToThis(ALbyte *filename);
   void UpdatePosition(float x, float y, float z);
   void Play();
   void Stop();
   void PitchMod(float fltMod);
   void GainMod(float fltMod);
   void SetLooping(bool boolLooping);
   
   etc.
 
}

class CarAudioController {
// OpenAL mixer, etc

struct CarSource {
CarPtr car;
AudioSource source;
}
std::vector<CarSource> m_engineSources;

void playEngineSound (CarsController * carsController) {
// Step 1: Update source positions / stop sources that are no longer relevant, build a std::set of cars currently active
// Step 2: Query for cars that are close to the listener
// Step 3: For all these cars, check if they are currently in the set of active cars. If not, add it and start the appropriate engine sound.
// Step 4: Profit.
}
};

In terms of implementation, playEngineSound could implement a round robin scheme that is updated at set time intervals - depending on RPM, distance, etc you can update or stop the sound. There are many many options and this pseudo code is not well developed, but it should give you an idea of how to separate out the code. There are many ways to do this kind of separation - another way is using a delegate.

I also wrote an article you might find interesting/useful:


I hope something here helps. The question you asked has a lot of possible solutions. Out of the methods you listed below, Method 2 is probably more in the right direction.

Kind regards,
Samuel

On 2/06/2009, at 1:59 PM, Pingwah Leronz wrote:

First of all, hello everyone :)
 
 
I'm working on a c++ project that's moving into the early stages of audio design and facing what Im guessing would be a fairly common problem, and wondering if anyone has any advice or input about the best way to approach it.
 
For those interested - 
Let's say we have a car
class classCar()
{
    public:
    classCoords coordsPosition;
    classCoords coordsVelocity;
    float fltFuel;
    ...etc.
 
};
 
where classCoords is simply
coords
{
  public:
  float x, y, z;
 
};
 
and Cars are to be able to emit several sounds at once , an ambient engine roar, horn beeps, drivers yelling etc.
Also there's to be literally millions of cars (OK, in the project itself , they're not cars :) ), so a method of culling sound emitters from the set needs to be there.  
Criteria for adding and removing Cars from the audio set is simply proximity to a viewing position, and the cars themselves are stored in something like
 
 
multimap <classCoord*, classCar*> mapCars;
 
(again, they're not cars- let's say the track consists of many discrete positions, each of which can contain many cars).
 
I've been experimenting with two methods of handling this - the first with a global controller:
 
Method 1
---------------------------------
class classAudioController()
{
     int MAX_SOUNDS = 150;
     multimap <classCar*, ALuint> mapSounds;     //where ALuint here is a ref to the standard openal source[MAX_SOURCES]
 
     //functions
     void AddCar(classCar* newCar, ALuint* newAudioSource);
     bool AssignBufferToSource(ALuint *intSource, ALbyte* filename);
     void UpdateAllPositionsAndVelocities();
     void UpdateCarsPositionAndVelocity(classCar* findCar);
     etc.
 
 
};
 
 
and also by a more object oriented
 
Method 2
----------------------------------
classCar
{
   //position, fuel, health etc.
   //....
   classAudioSource *soundSource[MAX_SOURCES];   //remember, each car can emit many simultaneous sounds
 
 
}
 
where classAudioSource is 
{
   ALuint intSource
 
   //funcs
   bool AssignBufferToThis(ALbyte *filename);
   void UpdatePosition(float x, float y, float z);
   void Play();
   void Stop();
   void PitchMod(float fltMod);
   void GainMod(float fltMod);
   void SetLooping(bool boolLooping);
   
   etc.
 
}
 
 
 
With Method 1, Cars are added and removed from the emitters map on movement events, In method 2 classAudioSources are triggered to Play/Stop based on listener proximity , again on movement events.
I'm running into problems with both, mainly from unfamiliarity.
 
If anyone here has input into handling large sets of sound sources in a good OO-heavy way, or can point me to any reading that does I'd be massively grateful.
 
Thanks,
Pingwah
 
 
 
 
 


Find car news, reviews and more Looking to change your car this year? _______________________________________________
Openal mailing list
Openal@...
http://opensource.creative.com/mailman/listinfo/openal


_______________________________________________
Openal mailing list
Openal@...
http://opensource.creative.com/mailman/listinfo/openal

Re: suggestions on c++ game design with openAL most welcome

by Jason Daly :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Some parts of this message have been removed. Learn more about Nabble's security policy.
Pingwah Leronz wrote:

and Cars are to be able to emit several sounds at once , an ambient engine roar, horn beeps, drivers yelling etc.
Also there's to be literally millions of cars (OK, in the project itself , they're not cars :) ), so a method of culling sound emitters from the set needs to be there. 
Criteria for adding and removing Cars from the audio set is simply proximity to a viewing position, and the cars themselves are stored in something like

Wow.  Millions is a lot of cars (or whatever :-) ).

You're obviously not going to be able to mix and play that many sounds in real time, so there will be some culling done.  You've got the right approach in either case, in that you're allocating buffers and dynamically assigning them to sources.  Either way would work, but the second method is probably closer to the "better" way.  I've referred to this as "virtualized sources", because you effectively are representing lots of logical sources and then assigning real sources to them dynamically.

Listener proximity is often an adequate metric for determining which sounds should be auralized.  In my case, each update cycle I actually compute the effective gain (using the attenuation formula from the spec) for each virtual source, and then sort them by effective gain.  I also throw in a user-defined "priority" metric (picked from LOW, NORMAL, HIGH, and ALWAYS_ON).  The sounds with ALWAYS_ON priority are guaranteed sources, regardless of effective gain (obviously, you can't have too many of these). First, each virtual source is checked to see if it is in a playing state (it is removed from further processing if not).  Next, the sounds are sorted by priority, then by effective gain.  Finally, real sources are assigned to the sounds at the top of the list.

This method is probably a bit too expensive for you if you really have to deal with millions of virtual sources.  You might need to add in an extra layer of cheap, coarse level processing to deal with culling out most of the sources before you get to a method as fine-grained as this.  This might involve some kind of spatial subdivision, where you ignore sounds that lie outside of some radius, or something like that.

You might also consider a form of clustering.  This technique pre-mixes sounds that lie some distance away, but are roughly in the same direction.  Say you have three sources to the northeast,  two to the west and five to the south.  You could effectively auralize all of these virtual sources using only three real sources.  You simply pre-mix the sound from the first three into one buffer, the next two into a second buffer, and the last five into a third buffer, and then play those three buffers on three real sources.

In your case, I'm thinking you might not get enough of a soundfield by just picking, say, the sixteen closest sources.  You might try pre-mixing some number of sources that are moderately distant into a single, non-directional "ambient" buffer.  This might better represent the soundfield you'd really hear in that kind of situation.  An easy way to make a non-directional buffer is to mix the sounds into a stereo buffer, with the same data in both left and right channels.

That's all I can think of right now.  Hope this helps!

--"J"



 
 
multimap <classCoord*, classCar*> mapCars;
 
(again, they're not cars- let's say the track consists of many discrete positions, each of which can contain many cars).
 
I've been experimenting with two methods of handling this - the first with a global controller:
 
Method 1
---------------------------------
class classAudioController()
{
     int MAX_SOUNDS = 150;
     multimap <classCar*, ALuint> mapSounds;     //where ALuint here is a ref to the standard openal source[MAX_SOURCES]
 
     //functions
     void AddCar(classCar* newCar, ALuint* newAudioSource);
     bool AssignBufferToSource(ALuint *intSource, ALbyte* filename);
     void UpdateAllPositionsAndVelocities();
     void UpdateCarsPositionAndVelocity(classCar* findCar);
     etc.
 
 
};
 
 
and also by a more object oriented
 
Method 2
----------------------------------
classCar
{
   //position, fuel, health etc.
   //....
   classAudioSource *soundSource[MAX_SOURCES];   //remember, each car can emit many simultaneous sounds
 
 
}
 
where classAudioSource is
{
   ALuint intSource
 
   //funcs
   bool AssignBufferToThis(ALbyte *filename);
   void UpdatePosition(float x, float y, float z);
   void Play();
   void Stop();
   void PitchMod(float fltMod);
   void GainMod(float fltMod);
   void SetLooping(bool boolLooping);
   
   etc.
 
}
 
 
 
With Method 1, Cars are added and removed from the emitters map on movement events, In method 2 classAudioSources are triggered to Play/Stop based on listener proximity , again on movement events.
I'm running into problems with both, mainly from unfamiliarity.
 
If anyone here has input into handling large sets of sound sources in a good OO-heavy way, or can point me to any reading that does I'd be massively grateful.
 
Thanks,
Pingwah
 
 
 
 
 


Find car news, reviews and more Looking to change your car this year?


_______________________________________________
Openal mailing list
Openal@...
http://opensource.creative.com/mailman/listinfo/openal

RE: suggestions on c++ game design with openAL most welcome

by Pingwah Leronz :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Some parts of this message have been removed. Learn more about Nabble's security policy.
Thanks Samuel,
As for the bad class design /lack of encapsulation, oh yes definitely - I'd just posted up dummy classes like this, going public for simplicity's sake.

I've also gone with the multimap as the program's main data store as I need to access objects by their position - the gameworld is a 3 dimensional grid, with objects having absolute positions.  Initially I used a 3d array - AbstractObject[x][y][z] , but given i'm working with a large number of potential objects, using a predefined array like this eats up a *lot* of memory.
A multimap using one of my Coords classes , which contains x,y,z as the key allows me to quickly access members, by position, in a dynamically sized manner, and given multimaps are sorted by key, and the .find(key) func is a fairly quick Binary search, this method seems to suit.  I'm not sure you can do this with vectors? that is, quickly access a member based on one of its atributes, without iterating through them all to check?

The boost shared pointer gives me some ideas, ta. I didn't know about it.
>Out of the methods you listed below, Method 2 is probably more in the right direction.

...do you mean Method 1?  If im reading your sample code correctly, it's more like a global container of some structure which links a pointer to the object, with its associated sound source, or list of sources? 

Thanks,
Pingwah









CC: openal@...
From: space.ship.traveller@...
To: pingwah_leronz@...
Subject: Re: [Openal] suggestions on c++ game design with openAL most welcome
Date: Tue, 2 Jun 2009 21:11:38 +1200

Hello,

I have a number of comments for you.

Firstly, looking at your code I would suggest you review your naming conventions.

"class classCar" is not a conventional name. If it suits you, that is fine, but it seems like you've got an incredibly verbose naming scheme which many people will find hard to look at.

A good scheme is to have UpperCase class names which are typically common or proper nouns. Secondly, variables don't need to include their type in the name. This is generally considered tedious and unmaintainable. For example, fltFuel might change to dblFuel. But now you have to change a lot of code. There are lots of information regarding different kinds of coding conventions available on the internet.

Also, you have made all your member variables public. This is also considered harmeful, for example if you change the implementation or storage of fltFuel, this now becomes a project wide change. The better option is to rely on encapsulation which generally means you have getters/setters for key values related to the object you are trying to represent. For example, rather than having setFuel(...) you might have a function addFuel(...) which carries out logic such as checking the maximum fuel that can be added, etc, which in my experience reduces the complexity of code elsewhere.

Looking at the solutions you are proposing - consider what best models what you are trying to represent.

For example, if you have multiple cars, each car has an engine, therefore each engine produces a sound. We can consider this the ideal world. In computer programming we need to use tricks to achieve adequate performance. So, it might be the case that you have up to 5 sources dedicated to engine sounds, and then choose the 5 engines closest to the "camera" for actually playing audio. If you have more than "x" sources close to the "camera" it may become impossible for the listener to identify individual sounds anyway, so we can limit to 5 without any loss of apparent quality.

I would question your use of a multimap for storing cars. It seems incredibly complex and performance heavy.

Consider the use of a simple std::vector. This is very fast and has guaranteed O(1) performance for all operations if you are not concerned about order. To remove an element from a vector simply use std::swap and pop:

/// Provides an efficient way to erase elements from a std::vector
template <typename t>
bool eraseElementAtIndex (IndexT index, std::vector<t> & array)
{
if (array.size() == (index+1)) {
array.pop_back();
return false;
} else {
array[index] = array.back();
array.pop_back();
return true;
}
}

So we can achieve very quick storage with std::vector.

If you need to do space querying, for a small number of cars (< 20) brute force algorithm will be fine, such as simply iterating through the list  and choosing the closest cars, etc.

Finally in terms of structuring the OpenAL code the best option is to look at keeping audio completely separate from your "model".

Try to avoid things like:
     int MAX_SOUNDS = 150;

This generally considered bad practice (there are specific cases where it is okay, but generally use std::vector for this kind of allocation).

We should consider the importance of two kinds of sounds:
- Sound that caused by specific event that occurred (crash sound).
- Sound that has been started and stopped by a particular object (engine sound).

These two kinds of sounds can be managed differently. For example, often with sounds based of event, my experience is that we can't tell the difference between single source and multiple source in typical cases as long as the sound is short - for example, explosion sound. So in this case you can simply have a single source which is allocated when the game starts. This is very convenient. Alternatively you can allocate a list of sources and use them in a round robin fashion if you need more realism or the sound is longer.

For a sound that is continuous we either have a single source (i.e. music) or multiple sources (i.e. multiple engines). This case is slightly more complex especially if we separate audio function from object model (class Car). In this case, the easiest solution is to have the audio source as part of the class Car. This is the simplest solution and means that each car has its own audio source. But this isn't efficient for a large number of cars, and increases coupling between audio and your simulation which if it is running as a server / client, obviously you don't want the server making all sorts of sounds or sound simply may not be available.

Therefore, I recommend you look at the following structure:

using boost::shared_ptr;

class Car {
public:
Coord getPosition ();
};

class CarsController {
typedef shared_ptr<Car> CarPtr;
std::vector<CarPtr> m_cars;

std::vector<CarPtr> findCarsClosestToPoint (Coord c, unsigned maxCars);
void update (float dt);
};

class AudioSource
{
   ALuint intSource
 
   //funcs
   bool AssignBufferToThis(ALbyte *filename);
   void UpdatePosition(float x, float y, float z);
   void Play();
   void Stop();
   void PitchMod(float fltMod);
   void GainMod(float fltMod);
   void SetLooping(bool boolLooping);
   
   etc.
 
}

class CarAudioController {
// OpenAL mixer, etc

struct CarSource {
CarPtr car;
AudioSource source;
}
std::vector<CarSource> m_engineSources;

void playEngineSound (CarsController * carsController) {
// Step 1: Update source positions / stop sources that are no longer relevant, build a std::set of cars currently active
// Step 2: Query for cars that are close to the listener
// Step 3: For all these cars, check if they are currently in the set of active cars. If not, add it and start the appropriate engine sound.
// Step 4: Profit.
}
};

In terms of implementation, playEngineSound could implement a round robin scheme that is updated at set time intervals - depending on RPM, distance, etc you can update or stop the sound. There are many many options and this pseudo code is not well developed, but it should give you an idea of how to separate out the code. There are many ways to do this kind of separation - another way is using a delegate.

I also wrote an article you might find interesting/useful:


I hope something here helps. The question you asked has a lot of possible solutions. Out of the methods you listed below, Method 2 is probably more in the right direction.

Kind regards,
Samuel

On 2/06/2009, at 1:59 PM, Pingwah Leronz wrote:

First of all, hello everyone :)
 
 
I'm working on a c++ project that's moving into the early stages of audio design and facing what Im guessing would be a fairly common problem, and wondering if anyone has any advice or input about the best way to approach it.
 
For those interested - 
Let's say we have a car
class classCar()
{
    public:
    classCoords coordsPosition;
    classCoords coordsVelocity;
    float fltFuel;
    ...etc.
 
};
 
where classCoords is simply
coords
{
  public:
  float x, y, z;
 
};
 
and Cars are to be able to emit several sounds at once , an ambient engine roar, horn beeps, drivers yelling etc.
Also there's to be literally millions of cars (OK, in the project itself , they're not cars :) ), so a method of culling sound emitters from the set needs to be there.  
Criteria for adding and removing Cars from the audio set is simply proximity to a viewing position, and the cars themselves are stored in something like
 
 
multimap <classCoord*, classCar*> mapCars;
 
(again, they're not cars- let's say the track consists of many discrete positions, each of which can contain many cars).
 
I've been experimenting with two methods of handling this - the first with a global controller:
 
Method 1
---------------------------------
class classAudioController()
{
     int MAX_SOUNDS = 150;
     multimap <classCar*, ALuint> mapSounds;     //where ALuint here is a ref to the standard openal source[MAX_SOURCES]
 
     //functions
     void AddCar(classCar* newCar, ALuint* newAudioSource);
     bool AssignBufferToSource(ALuint *intSource, ALbyte* filename);
     void UpdateAllPositionsAndVelocities();
     void UpdateCarsPositionAndVelocity(classCar* findCar);
     etc.
 
 
};
 
 
and also by a more object oriented
 
Method 2
----------------------------------
classCar
{
   //position, fuel, health etc.
   //....
   classAudioSource *soundSource[MAX_SOURCES];   //remember, each car can emit many simultaneous sounds
 
 
}
 
where classAudioSource is 
{
   ALuint intSource
 
   //funcs
   bool AssignBufferToThis(ALbyte *filename);
   void UpdatePosition(float x, float y, float z);
   void Play();
   void Stop();
   void PitchMod(float fltMod);
   void GainMod(float fltMod);
   void SetLooping(bool boolLooping);
   
   etc.
 
}
 
 
 
With Method 1, Cars are added and removed from the emitters map on movement events, In method 2 classAudioSources are triggered to Play/Stop based on listener proximity , again on movement events.
I'm running into problems with both, mainly from unfamiliarity.
 
If anyone here has input into handling large sets of sound sources in a good OO-heavy way, or can point me to any reading that does I'd be massively grateful.
 
Thanks,
Pingwah
 
 
 
 
 


Find car news, reviews and more Looking to change your car this year? _______________________________________________
Openal mailing list
Openal@...
http://opensource.creative.com/mailman/listinfo/openal



Make ninemsn your homepage! Get the latest news, goss and sport
_______________________________________________
Openal mailing list
Openal@...
http://opensource.creative.com/mailman/listinfo/openal

RE: suggestions on c++ game design with openAL most welcome

by Pingwah Leronz :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Some parts of this message have been removed. Learn more about Nabble's security policy.
Clustering seems a damn good idea!
A question though - would the process of mixing many sources into one be pretty much as operation intensive as just playing the sources themselves?  Maybe not, given that I'll only have to mix them each time an object moves.  I'll have to experiment with this.

thanks,
Pingwah




Date: Tue, 2 Jun 2009 11:11:52 -0400
From: jdaly@...
To: pingwah_leronz@...
CC: openal@...
Subject: Re: [Openal] suggestions on c++ game design with openAL most welcome

Pingwah Leronz wrote:

and Cars are to be able to emit several sounds at once , an ambient engine roar, horn beeps, drivers yelling etc.
Also there's to be literally millions of cars (OK, in the project itself , they're not cars :) ), so a method of culling sound emitters from the set needs to be there. 
Criteria for adding and removing Cars from the audio set is simply proximity to a viewing position, and the cars themselves are stored in something like

Wow.  Millions is a lot of cars (or whatever :-) ).

You're obviously not going to be able to mix and play that many sounds in real time, so there will be some culling done.  You've got the right approach in either case, in that you're allocating buffers and dynamically assigning them to sources.  Either way would work, but the second method is probably closer to the "better" way.  I've referred to this as "virtualized sources", because you effectively are representing lots of logical sources and then assigning real sources to them dynamically.

Listener proximity is often an adequate metric for determining which sounds should be auralized.  In my case, each update cycle I actually compute the effective gain (using the attenuation formula from the spec) for each virtual source, and then sort them by effective gain.  I also throw in a user-defined "priority" metric (picked from LOW, NORMAL, HIGH, and ALWAYS_ON).  The sounds with ALWAYS_ON priority are guaranteed sources, regardless of effective gain (obviously, you can't have too many of these). First, each virtual source is checked to see if it is in a playing state (it is removed from further processing if not).  Next, the sounds are sorted by priority, then by effective gain.  Finally, real sources are assigned to the sounds at the top of the list.

This method is probably a bit too expensive for you if you really have to deal with millions of virtual sources.  You might need to add in an extra layer of cheap, coarse level processing to deal with culling out most of the sources before you get to a method as fine-grained as this.  This might involve some kind of spatial subdivision, where you ignore sounds that lie outside of some radius, or something like that.

You might also consider a form of clustering.  This technique pre-mixes sounds that lie some distance away, but are roughly in the same direction.  Say you have three sources to the northeast,  two to the west and five to the south.  You could effectively auralize all of these virtual sources using only three real sources.  You simply pre-mix the sound from the first three into one buffer, the next two into a second buffer, and the last five into a third buffer, and then play those three buffers on three real sources.

In your case, I'm thinking you might not get enough of a soundfield by just picking, say, the sixteen closest sources.  You might try pre-mixing some number of sources that are moderately distant into a single, non-directional "ambient" buffer.  This might better represent the soundfield you'd really hear in that kind of situation.  An easy way to make a non-directional buffer is to mix the sounds into a stereo buffer, with the same data in both left and right channels.

That's all I can think of right now.  Hope this helps!

--"J"



 
 
multimap <classCoord*, classCar*> mapCars;
 
(again, they're not cars- let's say the track consists of many discrete positions, each of which can contain many cars).
 
I've been experimenting with two methods of handling this - the first with a global controller:
 
Method 1
---------------------------------
class classAudioController()
{
     int MAX_SOUNDS = 150;
     multimap <classCar*, ALuint> mapSounds;     //where ALuint here is a ref to the standard openal source[MAX_SOURCES]
 
     //functions
     void AddCar(classCar* newCar, ALuint* newAudioSource);
     bool AssignBufferToSource(ALuint *intSource, ALbyte* filename);
     void UpdateAllPositionsAndVelocities();
     void UpdateCarsPositionAndVelocity(classCar* findCar);
     etc.
 
 
};
 
 
and also by a more object oriented
 
Method 2
----------------------------------
classCar
{
   //position, fuel, health etc.
   //....
   classAudioSource *soundSource[MAX_SOURCES];   //remember, each car can emit many simultaneous sounds
 
 
}
 
where classAudioSource is
{
   ALuint intSource
 
   //funcs
   bool AssignBufferToThis(ALbyte *filename);
   void UpdatePosition(float x, float y, float z);
   void Play();
   void Stop();
   void PitchMod(float fltMod);
   void GainMod(float fltMod);
   void SetLooping(bool boolLooping);
   
   etc.
 
}
 
 
 
With Method 1, Cars are added and removed from the emitters map on movement events, In method 2 classAudioSources are triggered to Play/Stop based on listener proximity , again on movement events.
I'm running into problems with both, mainly from unfamiliarity.
 
If anyone here has input into handling large sets of sound sources in a good OO-heavy way, or can point me to any reading that does I'd be massively grateful.
 
Thanks,
Pingwah
 
 
 
 
 


Find car news, reviews and more Looking to change your car this year?



Find your next place with Ninemsn property Looking for a place to rent, share or buy this winter?
_______________________________________________
Openal mailing list
Openal@...
http://opensource.creative.com/mailman/listinfo/openal

Parent Message unknown Re: suggestions on c++ game design with openAL most welcome

by Space Ship Traveller :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


Maybe I should clarify a few things.

By Method 2, I mean encapsulating the audio class. I think whatever solution you end up with, having this encapsulated will make things easier in the long run - regardless if you use a controlling class.

I think that having a controlling class is one solution depending on the complexity, but in terms of design Method 2 was probably in the "right direction". It is very simple and will be easy for you to get up and running quickly. It won't be good for millions of objects though, sorry I missed that part. Using a controller class to optimise things is going to be required, and I would recommend you look at using a oct-tree for your space partitioning requirements, it is by far the simplest solution with regards to the kind of functionality you are looking for.

An alternative option is to use a sparse array of some sort - i.e. it looks like it has x, y, z dimensions but doesn't allocate all the memory up front. Another option is a sphere tree, which can be slightly better for moving objects (or so I've heard).

Kind regards,
Samuel

On 3/06/2009, at 1:53 PM, Pingwah Leronz wrote:

Thanks Samuel,
As for the bad class design /lack of encapsulation, oh yes definitely - I'd just posted up dummy classes like this, going public for simplicity's sake.

I've also gone with the multimap as the program's main data store as I need to access objects by their position - the gameworld is a 3 dimensional grid, with objects having absolute positions.  Initially I used a 3d array - AbstractObject[x][y][z] , but given i'm working with a large number of potential objects, using a predefined array like this eats up a *lot* of memory.
A multimap using one of my Coords classes , which contains x,y,z as the key allows me to quickly access members, by position, in a dynamically sized manner, and given multimaps are sorted by key, and the .find(key) func is a fairly quick Binary search, this method seems to suit.  I'm not sure you can do this with vectors? that is, quickly access a member based on one of its atributes, without iterating through them all to check?

The boost shared pointer gives me some ideas, ta. I didn't know about it.
>Out of the methods you listed below, Method 2 is probably more in the right direction.

...do you mean Method 1?  If im reading your sample code correctly, it's more like a global container of some structure which links a pointer to the object, with its associated sound source, or list of sources?  

Thanks,
Pingwah





_______________________________________________
Openal mailing list
Openal@...
http://opensource.creative.com/mailman/listinfo/openal

Re: suggestions on c++ game design with openAL most welcome

by Jason Daly :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Some parts of this message have been removed. Learn more about Nabble's security policy.
Pingwah Leronz wrote:
Clustering seems a damn good idea!
A question though - would the process of mixing many sources into one be pretty much as operation intensive as just playing the sources themselves?  Maybe not, given that I'll only have to mix them each time an object moves.  I'll have to experiment with this.

You do have the added complexity of the pre-mixing step, but you also eliminate the need to spatialize as many sources.  Each pre-mixed source is only spatialized once, regardless of how many sounds are pre-mixed into it.  In my example, there are only three sources spatialized, while there are ten sounds involved in all.

Here's a link to a gamasutra article for more info.  It's a little bit dated, but probably still relevant:

http://www.gamasutra.com/view/feature/2850/breaking_the_64_spatialized_.php

--"J"


_______________________________________________
Openal mailing list
Openal@...
http://opensource.creative.com/mailman/listinfo/openal