aboutsummaryrefslogtreecommitdiffstats
path: root/Alc/panning.c
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2012-04-28 07:28:36 -0700
committerChris Robinson <[email protected]>2012-04-28 08:21:53 -0700
commit611bd0b2d3b40f306f120de5bb5d7edeccb0d32e (patch)
tree554666ad8d85349a94784c73dd4723918a939da1 /Alc/panning.c
parent267d38cf2057bb78f5d03c7f923573d1da2c84be (diff)
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.
Diffstat (limited to 'Alc/panning.c')
-rw-r--r--Alc/panning.c161
1 files changed, 161 insertions, 0 deletions
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;