Add a method to calculate gains given a sound point and its half-width, and use it for reverb

The half-width ranges from 0 to pi, and essentially specifies the coverage area
around the listener. At 0, it's an infinitely small point sound and behaves
like a usual panning sound. At pi/2 it covers half the area, and at pi it
covers the whole area.
This commit is contained in:
Chris Robinson
2012-04-28 07:28:36 -07:00
parent 267d38cf20
commit 611bd0b2d3
3 changed files with 173 additions and 32 deletions
+10 -32
View File
@@ -1016,20 +1016,17 @@ static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *Reflection
ReflectionsPan[2] };
ALfloat latePan[3] = { LateReverbPan[0], LateReverbPan[1],
LateReverbPan[2] };
const ALfloat *ChannelGain;
ALfloat ambientGain;
ALfloat dirGain;
ALfloat length;
ALuint index;
ALint pos;
Gain *= ReverbBoost;
// Attenuate non-directional reverb according to the number of channels
ambientGain = aluSqrt(2.0f/Device->NumChan);
/* Attenuate reverb according to its coverage (dirGain=0 will give
* Gain*ambientGain, and dirGain=1 will give Gain). */
ambientGain = minf(aluSqrt(2.0f/Device->NumChan), 1.0f);
// Calculate the 3D-panning gains for the early reflections and late
// reverb.
length = earlyPan[0]*earlyPan[0] + earlyPan[1]*earlyPan[1] + earlyPan[2]*earlyPan[2];
if(length > 1.0f)
{
@@ -1047,36 +1044,17 @@ static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *Reflection
latePan[2] *= length;
}
/* This code applies directional reverb just like the mixer applies
* directional sources. It diffuses the sound toward all speakers as the
* magnitude of the panning vector drops, which is only a rough
* approximation of the expansion of sound across the speakers from the
* panning direction.
*/
pos = aluCart2LUTpos(earlyPan[0], earlyPan[2]);
ChannelGain = Device->PanningLUT[pos];
dirGain = aluSqrt((earlyPan[0] * earlyPan[0]) + (earlyPan[2] * earlyPan[2]));
dirGain = aluSqrt(earlyPan[0]*earlyPan[0] + earlyPan[2]*earlyPan[2]);
for(index = 0;index < MAXCHANNELS;index++)
State->Early.PanGain[index] = 0.0f;
for(index = 0;index < Device->NumChan;index++)
{
enum Channel chan = Device->Speaker2Chan[index];
State->Early.PanGain[chan] = lerp(ambientGain, ChannelGain[chan], dirGain) * Gain;
}
pos = aluCart2LUTpos(latePan[0], latePan[2]);
ChannelGain = Device->PanningLUT[pos];
dirGain = aluSqrt((latePan[0] * latePan[0]) + (latePan[2] * latePan[2]));
State->Early.PanGain[index] = 0.0f;
ComputeAngleGains(Device, aluAtan2(earlyPan[0], earlyPan[2]), (1.0f-dirGain)*F_PI,
lerp(ambientGain, 1.0f, dirGain) * Gain, State->Early.PanGain);
dirGain = aluSqrt(latePan[0]*latePan[0] + latePan[2]*latePan[2]);
for(index = 0;index < MAXCHANNELS;index++)
State->Late.PanGain[index] = 0.0f;
for(index = 0;index < Device->NumChan;index++)
{
enum Channel chan = Device->Speaker2Chan[index];
State->Late.PanGain[chan] = lerp(ambientGain, ChannelGain[chan], dirGain) * Gain;
}
ComputeAngleGains(Device, aluAtan2(latePan[0], latePan[2]), (1.0f-dirGain)*F_PI,
lerp(ambientGain, 1.0f, dirGain) * Gain, State->Late.PanGain);
}
// This updates the EAX reverb state. This is called any time the EAX reverb
+161
View File
@@ -164,6 +164,167 @@ ALint aluCart2LUTpos(ALfloat im, ALfloat re)
return pos%LUT_NUM;
}
/**
* ComputeAngleGains
*
* Sets channel gains based on a given source's angle and its half-width. The
* angle and hwidth parameters are in radians.
*/
ALvoid ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth, ALfloat ingain, ALfloat *gains)
{
const enum Channel *Speaker2Chan = device->Speaker2Chan;
const ALfloat *SpeakerAngle = device->SpeakerAngle;
ALfloat tmpgains[MAXCHANNELS] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
ALboolean inverted = AL_FALSE;
ALfloat langle, rangle;
ALfloat a;
ALuint i;
/* Some easy special-cases first... */
if(device->NumChan == 1 || hwidth >= F_PI)
{
/* Full coverage for all speakers. */
for(i = 0;i < device->NumChan;i++)
{
enum Channel chan = Speaker2Chan[i];
gains[chan] = ingain;
}
return;
}
if(hwidth <= 0.0f)
{
/* Infinitismally small sound point. */
for(i = 0;i < device->NumChan-1;i++)
{
if(angle >= SpeakerAngle[i] && angle < SpeakerAngle[i+1])
{
/* Sound is between speaker i and i+1 */
a = (angle-SpeakerAngle[i]) /
(SpeakerAngle[i+1]-SpeakerAngle[i]);
gains[Speaker2Chan[i]] = aluSqrt(1.0f-a) * ingain;
gains[Speaker2Chan[i+1]] = aluSqrt( a) * ingain;
return;
}
}
/* Sound is between last and first speakers */
if(angle < SpeakerAngle[0])
angle += F_PI*2.0f;
a = (angle-SpeakerAngle[i]) /
(F_PI*2.0f + SpeakerAngle[0]-SpeakerAngle[i]);
gains[Speaker2Chan[i]] = aluSqrt(1.0f-a) * ingain;
gains[Speaker2Chan[0]] = aluSqrt( a) * ingain;
return;
}
langle = angle - hwidth;
rangle = angle + hwidth;
if(langle < -F_PI)
langle += F_PI*2.0f;
if(rangle > F_PI)
rangle -= F_PI*2.0f;
if(langle > rangle)
{
/* langle and rangle are swapped to keep the langle<rangle assumption
* true, which keeps the following calculations sane. This inverts the
* results, so speakers within the original field end up as 0 and
* outside end up as 1. A fixup is done afterward to make sure the
* results are as expected. */
ALfloat tmp = rangle;
rangle = langle;
langle = tmp;
inverted = AL_TRUE;
}
/* First speaker */
i = 0;
{
ALuint last = device->NumChan-1;
if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle)
tmpgains[Speaker2Chan[i]] = 1.0f;
else if(SpeakerAngle[i] < langle && SpeakerAngle[i+1] > langle)
{
a = (langle-SpeakerAngle[i]) /
(SpeakerAngle[i+1]-SpeakerAngle[i]);
tmpgains[Speaker2Chan[i]] = 1.0f - a;
}
else if(SpeakerAngle[i] > rangle)
{
a = (F_PI*2.0f + rangle-SpeakerAngle[last]) /
(F_PI*2.0f + SpeakerAngle[i]-SpeakerAngle[last]);
tmpgains[Speaker2Chan[i]] = a;
}
else if(rangle > SpeakerAngle[last])
{
a = (rangle-SpeakerAngle[last]) /
(F_PI*2.0f + SpeakerAngle[i]-SpeakerAngle[last]);
tmpgains[Speaker2Chan[i]] = a;
}
}
for(i = 1;i < device->NumChan-1;i++)
{
if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle)
tmpgains[Speaker2Chan[i]] = 1.0f;
else if(SpeakerAngle[i] < langle && SpeakerAngle[i+1] > langle)
{
a = (langle-SpeakerAngle[i]) /
(SpeakerAngle[i+1]-SpeakerAngle[i]);
tmpgains[Speaker2Chan[i]] = 1.0f - a;
}
else if(SpeakerAngle[i] > rangle && SpeakerAngle[i-1] < rangle)
{
a = (rangle-SpeakerAngle[i-1]) /
(SpeakerAngle[i]-SpeakerAngle[i-1]);
tmpgains[Speaker2Chan[i]] = a;
}
}
/* Last speaker */
i = device->NumChan-1;
{
if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle)
tmpgains[Speaker2Chan[i]] = 1.0f;
else if(SpeakerAngle[i] > rangle && SpeakerAngle[i-1] < rangle)
{
a = (rangle-SpeakerAngle[i-1]) /
(SpeakerAngle[i]-SpeakerAngle[i-1]);
tmpgains[Speaker2Chan[i]] = a;
}
else if(SpeakerAngle[i] < langle)
{
ALfloat nextangle = SpeakerAngle[0] + F_PI*2.0f;
a = (langle-SpeakerAngle[i]) /
(nextangle-SpeakerAngle[i]);
tmpgains[Speaker2Chan[i]] = 1.0f - a;
}
else if(SpeakerAngle[0] > langle)
{
a = (langle-SpeakerAngle[i] - F_PI*2.0f) /
(SpeakerAngle[0]-SpeakerAngle[i] - F_PI*2.0f);
tmpgains[Speaker2Chan[i]] = 1.0f - a;
}
}
if(inverted)
{
for(i = 0;i < device->NumChan;i++)
{
enum Channel chan = device->Speaker2Chan[i];
gains[chan] = aluSqrt(1.0f - tmpgains[chan]) * ingain;
}
}
else
{
for(i = 0;i < device->NumChan;i++)
{
enum Channel chan = device->Speaker2Chan[i];
gains[chan] = aluSqrt(tmpgains[chan]) * ingain;
}
}
}
ALvoid aluInitPanning(ALCdevice *Device)
{
const char *layoutname = NULL;
+2
View File
@@ -298,6 +298,8 @@ static __inline void aluNormalize(ALfloat *inVector)
ALvoid aluInitPanning(ALCdevice *Device);
ALint aluCart2LUTpos(ALfloat im, ALfloat re);
ALvoid ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth, ALfloat ingain, ALfloat *gains);
ALvoid CalcSourceParams(struct ALsource *ALSource, const ALCcontext *ALContext);
ALvoid CalcNonAttnSourceParams(struct ALsource *ALSource, const ALCcontext *ALContext);