One more thing to make things even more cleared ;D Everybody know this from the openal sites
3.5.1. No Culling By Distance
With the DirectSound3D compatible Inverse Clamped Distance Model, OpenAL provides a per-source AL_MAX_DISTANCE attribute that can be used to define a distance beyond which the source will not be further attenuated by distance. The DS3D distance attenuation model and its clamping of volume is also extended by a mechanism to cull (mute) sources from processing, based on distance. However, the OpenAL does not support culling a source from processing based on a distance threshold.
At this time OpenAL does not support culling at all. Culling based on distance, or bounding volumes, or other criteria, is left to the application. For example, the application might employ sophisticated techniques to determine whether sources are audible. In particular, rule based culling inevitably introduces acoustic artifacts. For example, if the listener-source distance is nearly equal to the culling threshold distance, but varies above and below, there will be popping artifacts in the absence of hysteresis.
Actually the red sentence above, what I do not know how could be done
Do anybody implemented such before ?
Hey Atta,
I just started learning openAL to add sound to a game. This post is kind of old, so I don't know how relevant this is, but here's an overview of the approach I'm working on.
Classes:
Sound -- holds info about the sound (most notably, source id)
SoundComparator -- compares two sounds (used when resources are tight for culling/freeing sources)
SoundLoader -- handles all sound resources
Most of the work is done in the last one, which I'm trying to make resemble a TextureLoader. So I've got a HashMap of sound paths to buffer ids (to see if a sound is already loaded), a HashMap of sound paths to reference integers (to record how many sources are playing a buffered sound), and finally a sorted list of Sounds (the class).
The Sounds are sorted by volume (and an extra importance parameter, so the final value is volume * importance, but that's very application-specific). The volume is determined using a linear distance model -- I messed around with the default Inverse Distance Clamped Model, but if you want to do culling, linear seems to make more sense so you can really make the sound go to zero. As far as I can tell, you just manually calculate the gain every time and update it. I could be wrong, about most of this... but things seem to work ok with my very limited testing.
Anyway, to get back to what I've done on your specific questions. For the Inverse Distance Clamped Model, I found these equations:
dist = max(dist,REFERENCE_DISTANCE);
dist = min(dist,MAX_DISTANCE);
G_dB = GAIN - 20*log10(1 + ROLLOFF_FACTOR*(dist-REFERENCE_DISTANCE)/REFERENCE_DISTANCE )
G_dB = min(G_dB,MAX_GAIN);
G_dB = max(G_dB,MIN_GAIN);
Now, if I understand correctly, every -6dB halves the gain (volume). So, I divided the gain by 2^(-1/6*G_dB) to get the current volume. dist is current distance to the listener from the sound location. I use some more dynamic game objects, so my distance code is messier.
float G_dB = gain - 20*(float)Math.log10(1 + rolloffFactor*(dist-referenceDistance)/referenceDistance );
if(G_dB > maxGain) G_dB = maxGain;
if(G_dB < minGain) G_dB = minGain;
float divisor = (float)Math.pow(2,-1/6 * G_dB);
float curGain = gain / divisor;
The trickiest part I found was actually figuring out how to drop off the sound after a MAX_DISTANCE cutoff. After some variable solving, I chugged out this, which again has had almost no testing:
public void calculateRolloff(){
//NOTE: every -6db results in half volume
//want: gain to be VOLUME_CUTOFF at maxDistance
float G_dB_target = -6*(float)(Math.log(gain/VOLUME_CUTOFF)/Math.log(2));
//solve for rolloff from Inverse Distance Clamped Model
float newRolloff =
(float)(Math.pow(10,(G_dB_target - gain)/(-20)) - 1)
/ ((maxDistance - referenceDistance) / referenceDistance);
setRolloffFactor(newRolloff);
}
(Obviously, you can't have volume cutoff be zero).
Frankly, all of this was giving me a bit of a headache, so I switched to manual linear model. I inserted a little catch into the volume code:
float gainRange = maxGain - minGain;
float volume = ((maxDistance - dist-referenceDistance)
/ (maxDistance-referenceDistance))
* gainRange
+ minGain;
And now in fact set the volume the exact same way as I get it, so it's guaranteed to be correct.
To overview, when a Sound is made:
- it loads the buffered sound (keyed via path to wav) from the SoundLoader (which in turn loads it if need be, and adds a reference)
- requests a sound source (if none available, checks if its volume/priority is higher than one being used, in which case the used one is destroyed, if not, removes reference to buffer)
Each Sound object gets a turn every game cycle, and checks if its sound is done playing, or if it should be culled because it's too quiet. If it is to be removed, the SoundLoader reference to its buffer is decreased, and its source id is returned to SoundLoader.
There's probably stuff like this out there, but my googling turned up more how-stuff-works than code snippets. Right now my sound code is pretty entangled with my game code, with sounds following objects and whatnot each cycle. I'm also not entirely sure where I got all parts from... I know there's some from the OpenAL lessons. And I've done precious little testing on the full thing, let alone efficiency analysis (like, the hashmap thing might not make sense when you consider you only have a handful of sources tops... but it came to mind first).
Anyway, if this is something unique, I could try to clean it up for more general use. My guess is once you figure out volume calculation, the sorting and culling will be pretty easy and based on how you want to do your app. In particular it looks like you're going to be juggling lots of sound sources, maybe even all of them in the game world at once, constantly shuffling in those that you can play? The way I'm doing it is more of a try-now-or-fail-forever approach. My persistent sound sources will just have to periodically keep trying to play. Hmm, more for me to think about...