aboutsummaryrefslogtreecommitdiffstats
path: root/Alc
diff options
context:
space:
mode:
Diffstat (limited to 'Alc')
-rw-r--r--Alc/alcReverb.c42
-rw-r--r--Alc/panning.c161
2 files changed, 171 insertions, 32 deletions
diff --git a/Alc/alcReverb.c b/Alc/alcReverb.c
index b5c85999..a2230c7c 100644
--- a/Alc/alcReverb.c
+++ b/Alc/alcReverb.c
@@ -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
diff --git a/Alc/panning.c b/Alc/panning.c
index 0e09bc5c..0433fd3d 100644
--- a/Alc/panning.c
+++ b/Alc/panning.c
@@ -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;