Improve radius behavior with scaling of ambisonic coefficients
[openal-soft.git] / Alc / panning.c
blob9ff974778006ec0a3c0b5714da356fde8b67a599
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2010 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <math.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <assert.h>
29 #include "alMain.h"
30 #include "alAuxEffectSlot.h"
31 #include "alu.h"
32 #include "bool.h"
33 #include "ambdec.h"
34 #include "bformatdec.h"
35 #include "uhjfilter.h"
36 #include "bs2b.h"
39 extern inline void CalcXYZCoeffs(ALfloat x, ALfloat y, ALfloat z, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
42 #define ZERO_ORDER_SCALE 0.0f
43 #define FIRST_ORDER_SCALE 1.0f
44 #define SECOND_ORDER_SCALE (1.0f / 1.22474f)
45 #define THIRD_ORDER_SCALE (1.0f / 1.30657f)
48 static const ALuint FuMa2ACN[MAX_AMBI_COEFFS] = {
49 0, /* W */
50 3, /* X */
51 1, /* Y */
52 2, /* Z */
53 6, /* R */
54 7, /* S */
55 5, /* T */
56 8, /* U */
57 4, /* V */
58 12, /* K */
59 13, /* L */
60 11, /* M */
61 14, /* N */
62 10, /* O */
63 15, /* P */
64 9, /* Q */
67 /* NOTE: These are scale factors as applied to Ambisonics content. Decoder
68 * coefficients should be divided by these values to get proper N3D scalings.
70 static const ALfloat UnitScale[MAX_AMBI_COEFFS] = {
71 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
72 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
74 static const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = {
75 1.000000000f, /* ACN 0 (W), sqrt(1) */
76 1.732050808f, /* ACN 1 (Y), sqrt(3) */
77 1.732050808f, /* ACN 2 (Z), sqrt(3) */
78 1.732050808f, /* ACN 3 (X), sqrt(3) */
79 2.236067978f, /* ACN 4 (V), sqrt(5) */
80 2.236067978f, /* ACN 5 (T), sqrt(5) */
81 2.236067978f, /* ACN 6 (R), sqrt(5) */
82 2.236067978f, /* ACN 7 (S), sqrt(5) */
83 2.236067978f, /* ACN 8 (U), sqrt(5) */
84 2.645751311f, /* ACN 9 (Q), sqrt(7) */
85 2.645751311f, /* ACN 10 (O), sqrt(7) */
86 2.645751311f, /* ACN 11 (M), sqrt(7) */
87 2.645751311f, /* ACN 12 (K), sqrt(7) */
88 2.645751311f, /* ACN 13 (L), sqrt(7) */
89 2.645751311f, /* ACN 14 (N), sqrt(7) */
90 2.645751311f, /* ACN 15 (P), sqrt(7) */
92 static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = {
93 1.414213562f, /* ACN 0 (W), sqrt(2) */
94 1.732050808f, /* ACN 1 (Y), sqrt(3) */
95 1.732050808f, /* ACN 2 (Z), sqrt(3) */
96 1.732050808f, /* ACN 3 (X), sqrt(3) */
97 1.936491673f, /* ACN 4 (V), sqrt(15)/2 */
98 1.936491673f, /* ACN 5 (T), sqrt(15)/2 */
99 2.236067978f, /* ACN 6 (R), sqrt(5) */
100 1.936491673f, /* ACN 7 (S), sqrt(15)/2 */
101 1.936491673f, /* ACN 8 (U), sqrt(15)/2 */
102 2.091650066f, /* ACN 9 (Q), sqrt(35/8) */
103 1.972026594f, /* ACN 10 (O), sqrt(35)/3 */
104 2.231093404f, /* ACN 11 (M), sqrt(224/45) */
105 2.645751311f, /* ACN 12 (K), sqrt(7) */
106 2.231093404f, /* ACN 13 (L), sqrt(224/45) */
107 1.972026594f, /* ACN 14 (N), sqrt(35)/3 */
108 2.091650066f, /* ACN 15 (P), sqrt(35/8) */
112 void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
114 /* Convert from OpenAL coords to Ambisonics. */
115 ALfloat x = -dir[2];
116 ALfloat y = -dir[0];
117 ALfloat z = dir[1];
119 /* Zeroth-order */
120 coeffs[0] = 1.0f; /* ACN 0 = 1 */
121 /* First-order */
122 coeffs[1] = 1.732050808f * y; /* ACN 1 = sqrt(3) * Y */
123 coeffs[2] = 1.732050808f * z; /* ACN 2 = sqrt(3) * Z */
124 coeffs[3] = 1.732050808f * x; /* ACN 3 = sqrt(3) * X */
125 /* Second-order */
126 coeffs[4] = 3.872983346f * x * y; /* ACN 4 = sqrt(15) * X * Y */
127 coeffs[5] = 3.872983346f * y * z; /* ACN 5 = sqrt(15) * Y * Z */
128 coeffs[6] = 1.118033989f * (3.0f*z*z - 1.0f); /* ACN 6 = sqrt(5)/2 * (3*Z*Z - 1) */
129 coeffs[7] = 3.872983346f * x * z; /* ACN 7 = sqrt(15) * X * Z */
130 coeffs[8] = 1.936491673f * (x*x - y*y); /* ACN 8 = sqrt(15)/2 * (X*X - Y*Y) */
131 /* Third-order */
132 coeffs[9] = 2.091650066f * y * (3.0f*x*x - y*y); /* ACN 9 = sqrt(35/8) * Y * (3*X*X - Y*Y) */
133 coeffs[10] = 10.246950766f * z * x * y; /* ACN 10 = sqrt(105) * Z * X * Y */
134 coeffs[11] = 1.620185175f * y * (5.0f*z*z - 1.0f); /* ACN 11 = sqrt(21/8) * Y * (5*Z*Z - 1) */
135 coeffs[12] = 1.322875656f * z * (5.0f*z*z - 3.0f); /* ACN 12 = sqrt(7)/2 * Z * (5*Z*Z - 3) */
136 coeffs[13] = 1.620185175f * x * (5.0f*z*z - 1.0f); /* ACN 13 = sqrt(21/8) * X * (5*Z*Z - 1) */
137 coeffs[14] = 5.123475383f * z * (x*x - y*y); /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */
138 coeffs[15] = 2.091650066f * x * (x*x - 3.0f*y*y); /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */
140 if(spread > 0.0f)
142 /* Implement the spread by using a spherical source that subtends the
143 * angle spread. See:
144 * http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3
146 * The gain of the source is compensated for size, so that the
147 * loundness doesn't depend on the spread.
149 * ZH0 = (-sqrt_pi * (-1.f + ca));
150 * ZH1 = ( 0.5f*sqrtf(3.f)*sqrt_pi * sa*sa);
151 * ZH2 = (-0.5f*sqrtf(5.f)*sqrt_pi * ca*(-1.f+ca)*(ca+1.f));
152 * ZH3 = (-0.125f*sqrtf(7.f)*sqrt_pi * (-1.f+ca)*(ca+1.f)*(5.f*ca*ca-1.f));
153 * solidangle = 2.f*F_PI*(1.f-ca)
154 * size_normalisation_coef = 1.f/ZH0;
156 * This is then adjusted for N3D normalization over SN3D.
158 ALfloat ca = cosf(spread * 0.5f);
160 ALfloat ZH0_norm = 1.0f;
161 ALfloat ZH1_norm = 0.5f * (ca+1.f);
162 ALfloat ZH2_norm = 0.5f * (ca+1.f)*ca;
163 ALfloat ZH3_norm = 0.125f * (ca+1.f)*(5.f*ca*ca-1.f);
165 /* Zeroth-order */
166 coeffs[0] *= ZH0_norm;
167 /* First-order */
168 coeffs[1] *= ZH1_norm;
169 coeffs[2] *= ZH1_norm;
170 coeffs[3] *= ZH1_norm;
171 /* Second-order */
172 coeffs[4] *= ZH2_norm;
173 coeffs[5] *= ZH2_norm;
174 coeffs[6] *= ZH2_norm;
175 coeffs[7] *= ZH2_norm;
176 coeffs[8] *= ZH2_norm;
177 /* Third-order */
178 coeffs[9] *= ZH3_norm;
179 coeffs[10] *= ZH3_norm;
180 coeffs[11] *= ZH3_norm;
181 coeffs[12] *= ZH3_norm;
182 coeffs[13] *= ZH3_norm;
183 coeffs[14] *= ZH3_norm;
184 coeffs[15] *= ZH3_norm;
188 void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
190 ALfloat dir[3] = {
191 sinf(azimuth) * cosf(elevation),
192 sinf(elevation),
193 -cosf(azimuth) * cosf(elevation)
195 CalcDirectionCoeffs(dir, spread, coeffs);
199 void ComputeAmbientGainsMC(const ChannelConfig *chancoeffs, ALuint numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
201 ALuint i;
203 for(i = 0;i < numchans;i++)
205 // The W coefficients are based on a mathematical average of the
206 // output. The square root of the base average provides for a more
207 // perceptual average volume, better suited to non-directional gains.
208 gains[i] = sqrtf(chancoeffs[i][0]) * ingain;
210 for(;i < MAX_OUTPUT_CHANNELS;i++)
211 gains[i] = 0.0f;
214 void ComputeAmbientGainsBF(const BFChannelConfig *chanmap, ALuint numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
216 ALfloat gain = 0.0f;
217 ALuint i;
219 for(i = 0;i < numchans;i++)
221 if(chanmap[i].Index == 0)
222 gain += chanmap[i].Scale;
224 gains[0] = gain * 1.414213562f * ingain;
225 for(i = 1;i < MAX_OUTPUT_CHANNELS;i++)
226 gains[i] = 0.0f;
229 void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALuint numchans, ALuint numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
231 ALuint i, j;
233 for(i = 0;i < numchans;i++)
235 float gain = 0.0f;
236 for(j = 0;j < numcoeffs;j++)
237 gain += chancoeffs[i][j]*coeffs[j];
238 gains[i] = gain * ingain;
240 for(;i < MAX_OUTPUT_CHANNELS;i++)
241 gains[i] = 0.0f;
244 void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALuint numchans, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
246 ALuint i;
248 for(i = 0;i < numchans;i++)
249 gains[i] = chanmap[i].Scale * coeffs[chanmap[i].Index] * ingain;
250 for(;i < MAX_OUTPUT_CHANNELS;i++)
251 gains[i] = 0.0f;
254 void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, ALuint numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
256 ALuint i, j;
258 for(i = 0;i < numchans;i++)
260 float gain = 0.0f;
261 for(j = 0;j < 4;j++)
262 gain += chancoeffs[i][j] * mtx[j];
263 gains[i] = gain * ingain;
265 for(;i < MAX_OUTPUT_CHANNELS;i++)
266 gains[i] = 0.0f;
269 void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALuint numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
271 ALuint i;
273 for(i = 0;i < numchans;i++)
274 gains[i] = chanmap[i].Scale * mtx[chanmap[i].Index] * ingain;
275 for(;i < MAX_OUTPUT_CHANNELS;i++)
276 gains[i] = 0.0f;
280 DECL_CONST static inline const char *GetLabelFromChannel(enum Channel channel)
282 switch(channel)
284 case FrontLeft: return "front-left";
285 case FrontRight: return "front-right";
286 case FrontCenter: return "front-center";
287 case LFE: return "lfe";
288 case BackLeft: return "back-left";
289 case BackRight: return "back-right";
290 case BackCenter: return "back-center";
291 case SideLeft: return "side-left";
292 case SideRight: return "side-right";
294 case UpperFrontLeft: return "upper-front-left";
295 case UpperFrontRight: return "upper-front-right";
296 case UpperBackLeft: return "upper-back-left";
297 case UpperBackRight: return "upper-back-right";
298 case LowerFrontLeft: return "lower-front-left";
299 case LowerFrontRight: return "lower-front-right";
300 case LowerBackLeft: return "lower-back-left";
301 case LowerBackRight: return "lower-back-right";
303 case Aux0: return "aux-0";
304 case Aux1: return "aux-1";
305 case Aux2: return "aux-2";
306 case Aux3: return "aux-3";
307 case Aux4: return "aux-4";
308 case Aux5: return "aux-5";
309 case Aux6: return "aux-6";
310 case Aux7: return "aux-7";
311 case Aux8: return "aux-8";
312 case Aux9: return "aux-9";
313 case Aux10: return "aux-10";
314 case Aux11: return "aux-11";
315 case Aux12: return "aux-12";
316 case Aux13: return "aux-13";
317 case Aux14: return "aux-14";
318 case Aux15: return "aux-15";
320 case InvalidChannel: break;
322 return "(unknown)";
326 typedef struct ChannelMap {
327 enum Channel ChanName;
328 ChannelConfig Config;
329 } ChannelMap;
331 static void SetChannelMap(const enum Channel *devchans, ChannelConfig *ambicoeffs,
332 const ChannelMap *chanmap, size_t count, ALuint *outcount,
333 ALboolean isfuma)
335 size_t j, k;
336 ALuint i;
338 for(i = 0;i < MAX_OUTPUT_CHANNELS && devchans[i] != InvalidChannel;i++)
340 if(devchans[i] == LFE)
342 for(j = 0;j < MAX_AMBI_COEFFS;j++)
343 ambicoeffs[i][j] = 0.0f;
344 continue;
347 for(j = 0;j < count;j++)
349 if(devchans[i] != chanmap[j].ChanName)
350 continue;
352 if(isfuma)
354 /* Reformat FuMa -> ACN/N3D */
355 for(k = 0;k < MAX_AMBI_COEFFS;++k)
357 ALuint acn = FuMa2ACN[k];
358 ambicoeffs[i][acn] = chanmap[j].Config[k] / FuMa2N3DScale[acn];
361 else
363 for(k = 0;k < MAX_AMBI_COEFFS;++k)
364 ambicoeffs[i][k] = chanmap[j].Config[k];
366 break;
368 if(j == count)
369 ERR("Failed to match %s channel (%u) in channel map\n", GetLabelFromChannel(devchans[i]), i);
371 *outcount = i;
374 static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALuint speakermap[MAX_OUTPUT_CHANNELS])
376 ALuint i;
378 for(i = 0;i < conf->NumSpeakers;i++)
380 int c = -1;
382 /* NOTE: AmbDec does not define any standard speaker names, however
383 * for this to work we have to by able to find the output channel
384 * the speaker definition corresponds to. Therefore, OpenAL Soft
385 * requires these channel labels to be recognized:
387 * LF = Front left
388 * RF = Front right
389 * LS = Side left
390 * RS = Side right
391 * LB = Back left
392 * RB = Back right
393 * CE = Front center
394 * CB = Back center
396 * Additionally, surround51 will acknowledge back speakers for side
397 * channels, and surround51rear will acknowledge side speakers for
398 * back channels, to avoid issues with an ambdec expecting 5.1 to
399 * use the side channels when the device is configured for back,
400 * and vice-versa.
402 if(al_string_cmp_cstr(conf->Speakers[i].Name, "LF") == 0)
403 c = GetChannelIdxByName(device->RealOut, FrontLeft);
404 else if(al_string_cmp_cstr(conf->Speakers[i].Name, "RF") == 0)
405 c = GetChannelIdxByName(device->RealOut, FrontRight);
406 else if(al_string_cmp_cstr(conf->Speakers[i].Name, "CE") == 0)
407 c = GetChannelIdxByName(device->RealOut, FrontCenter);
408 else if(al_string_cmp_cstr(conf->Speakers[i].Name, "LS") == 0)
410 if(device->FmtChans == DevFmtX51Rear)
411 c = GetChannelIdxByName(device->RealOut, BackLeft);
412 else
413 c = GetChannelIdxByName(device->RealOut, SideLeft);
415 else if(al_string_cmp_cstr(conf->Speakers[i].Name, "RS") == 0)
417 if(device->FmtChans == DevFmtX51Rear)
418 c = GetChannelIdxByName(device->RealOut, BackRight);
419 else
420 c = GetChannelIdxByName(device->RealOut, SideRight);
422 else if(al_string_cmp_cstr(conf->Speakers[i].Name, "LB") == 0)
424 if(device->FmtChans == DevFmtX51)
425 c = GetChannelIdxByName(device->RealOut, SideLeft);
426 else
427 c = GetChannelIdxByName(device->RealOut, BackLeft);
429 else if(al_string_cmp_cstr(conf->Speakers[i].Name, "RB") == 0)
431 if(device->FmtChans == DevFmtX51)
432 c = GetChannelIdxByName(device->RealOut, SideRight);
433 else
434 c = GetChannelIdxByName(device->RealOut, BackRight);
436 else if(al_string_cmp_cstr(conf->Speakers[i].Name, "CB") == 0)
437 c = GetChannelIdxByName(device->RealOut, BackCenter);
438 else
440 ERR("AmbDec speaker label \"%s\" not recognized\n",
441 al_string_get_cstr(conf->Speakers[i].Name));
442 return false;
444 if(c == -1)
446 ERR("Failed to lookup AmbDec speaker label %s\n",
447 al_string_get_cstr(conf->Speakers[i].Name));
448 return false;
450 speakermap[i] = c;
453 return true;
457 /* NOTE: These decoder coefficients are using FuMa channel ordering and
458 * normalization, since that's what was produced by the Ambisonic Decoder
459 * Toolbox. SetChannelMap will convert them to N3D.
461 static const ChannelMap MonoCfg[1] = {
462 { FrontCenter, { 1.414213562f } },
463 }, StereoCfg[2] = {
464 { FrontLeft, { 0.707106781f, 0.0f, 0.5f, 0.0f } },
465 { FrontRight, { 0.707106781f, 0.0f, -0.5f, 0.0f } },
466 }, QuadCfg[4] = {
467 { FrontLeft, { 0.353553f, 0.306184f, 0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.117186f } },
468 { FrontRight, { 0.353553f, 0.306184f, -0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.117186f } },
469 { BackLeft, { 0.353553f, -0.306184f, 0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.117186f } },
470 { BackRight, { 0.353553f, -0.306184f, -0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.117186f } },
471 }, X51SideCfg[5] = {
472 { FrontLeft, { 0.208954f, 0.199518f, 0.223424f, 0.0f, 0.0f, 0.0f, 0.0f, -0.012543f, 0.144260f } },
473 { FrontRight, { 0.208950f, 0.199514f, -0.223425f, 0.0f, 0.0f, 0.0f, 0.0f, -0.012544f, -0.144258f } },
474 { FrontCenter, { 0.109403f, 0.168250f, -0.000002f, 0.0f, 0.0f, 0.0f, 0.0f, 0.100431f, -0.000001f } },
475 { SideLeft, { 0.470934f, -0.346484f, 0.327504f, 0.0f, 0.0f, 0.0f, 0.0f, -0.022188f, -0.041113f } },
476 { SideRight, { 0.470936f, -0.346480f, -0.327507f, 0.0f, 0.0f, 0.0f, 0.0f, -0.022186f, 0.041114f } },
477 }, X51RearCfg[5] = {
478 { FrontLeft, { 0.208954f, 0.199518f, 0.223424f, 0.0f, 0.0f, 0.0f, 0.0f, -0.012543f, 0.144260f } },
479 { FrontRight, { 0.208950f, 0.199514f, -0.223425f, 0.0f, 0.0f, 0.0f, 0.0f, -0.012544f, -0.144258f } },
480 { FrontCenter, { 0.109403f, 0.168250f, -0.000002f, 0.0f, 0.0f, 0.0f, 0.0f, 0.100431f, -0.000001f } },
481 { BackLeft, { 0.470934f, -0.346484f, 0.327504f, 0.0f, 0.0f, 0.0f, 0.0f, -0.022188f, -0.041113f } },
482 { BackRight, { 0.470936f, -0.346480f, -0.327507f, 0.0f, 0.0f, 0.0f, 0.0f, -0.022186f, 0.041114f } },
483 }, X61Cfg[6] = {
484 { FrontLeft, { 0.167065f, 0.200583f, 0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, 0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, 0.068910f } },
485 { FrontRight, { 0.167065f, 0.200583f, -0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, -0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, -0.068910f } },
486 { FrontCenter, { 0.109403f, 0.179490f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.142031f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.072024f, 0.000000f } },
487 { BackCenter, { 0.353556f, -0.461940f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.165723f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.000000f } },
488 { SideLeft, { 0.289151f, -0.081301f, 0.401292f, 0.0f, 0.0f, 0.0f, 0.0f, -0.188208f, -0.071420f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.010099f, -0.032897f } },
489 { SideRight, { 0.289151f, -0.081301f, -0.401292f, 0.0f, 0.0f, 0.0f, 0.0f, -0.188208f, 0.071420f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.010099f, 0.032897f } },
490 }, X71Cfg[7] = {
491 { FrontLeft, { 0.167065f, 0.200583f, 0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, 0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, 0.068910f } },
492 { FrontRight, { 0.167065f, 0.200583f, -0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, -0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, -0.068910f } },
493 { FrontCenter, { 0.109403f, 0.179490f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.142031f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.072024f, 0.000000f } },
494 { BackLeft, { 0.224752f, -0.295009f, 0.170325f, 0.0f, 0.0f, 0.0f, 0.0f, 0.105349f, -0.182473f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.065799f } },
495 { BackRight, { 0.224752f, -0.295009f, -0.170325f, 0.0f, 0.0f, 0.0f, 0.0f, 0.105349f, 0.182473f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.065799f } },
496 { SideLeft, { 0.224739f, 0.000000f, 0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.065795f } },
497 { SideRight, { 0.224739f, 0.000000f, -0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.065795f } },
500 static void InitPanning(ALCdevice *device)
502 const ChannelMap *chanmap = NULL;
503 ALuint coeffcount = 0;
504 ALfloat ambiscale;
505 size_t count = 0;
506 ALuint i, j;
508 ambiscale = 1.0f;
509 switch(device->FmtChans)
511 case DevFmtMono:
512 count = COUNTOF(MonoCfg);
513 chanmap = MonoCfg;
514 ambiscale = ZERO_ORDER_SCALE;
515 coeffcount = 1;
516 break;
518 case DevFmtStereo:
519 count = COUNTOF(StereoCfg);
520 chanmap = StereoCfg;
521 ambiscale = FIRST_ORDER_SCALE;
522 coeffcount = 4;
523 break;
525 case DevFmtQuad:
526 count = COUNTOF(QuadCfg);
527 chanmap = QuadCfg;
528 ambiscale = SECOND_ORDER_SCALE;
529 coeffcount = 9;
530 break;
532 case DevFmtX51:
533 count = COUNTOF(X51SideCfg);
534 chanmap = X51SideCfg;
535 ambiscale = SECOND_ORDER_SCALE;
536 coeffcount = 9;
537 break;
539 case DevFmtX51Rear:
540 count = COUNTOF(X51RearCfg);
541 chanmap = X51RearCfg;
542 ambiscale = SECOND_ORDER_SCALE;
543 coeffcount = 9;
544 break;
546 case DevFmtX61:
547 count = COUNTOF(X61Cfg);
548 chanmap = X61Cfg;
549 ambiscale = THIRD_ORDER_SCALE;
550 coeffcount = 16;
551 break;
553 case DevFmtX71:
554 count = COUNTOF(X71Cfg);
555 chanmap = X71Cfg;
556 ambiscale = THIRD_ORDER_SCALE;
557 coeffcount = 16;
558 break;
560 case DevFmtBFormat3D:
561 break;
564 if(device->FmtChans == DevFmtBFormat3D)
566 count = 4;
567 for(i = 0;i < count;i++)
569 ALuint acn = FuMa2ACN[i];
570 device->Dry.Ambi.Map[i].Scale = 1.0f/FuMa2N3DScale[acn];
571 device->Dry.Ambi.Map[i].Index = acn;
573 device->Dry.CoeffCount = 0;
574 device->Dry.NumChannels = count;
576 memcpy(&device->FOAOut.Ambi, &device->Dry.Ambi, sizeof(device->FOAOut.Ambi));
577 device->FOAOut.CoeffCount = device->Dry.CoeffCount;
579 else
581 SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs,
582 chanmap, count, &device->Dry.NumChannels, AL_TRUE);
583 device->Dry.CoeffCount = coeffcount;
585 memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
586 for(i = 0;i < device->Dry.NumChannels;i++)
588 device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0];
589 for(j = 1;j < 4;j++)
590 device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * ambiscale;
592 device->FOAOut.CoeffCount = 4;
596 static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const ALuint speakermap[MAX_OUTPUT_CHANNELS])
598 ChannelMap chanmap[MAX_OUTPUT_CHANNELS];
599 const ALfloat *coeff_scale = UnitScale;
600 ALfloat ambiscale = 1.0f;
601 ALuint i, j;
603 if(conf->FreqBands != 1)
604 ERR("Basic renderer uses the high-frequency matrix as single-band (xover_freq = %.0fhz)\n",
605 conf->XOverFreq);
607 if(conf->ChanMask > 0x1ff)
608 ambiscale = THIRD_ORDER_SCALE;
609 else if(conf->ChanMask > 0xf)
610 ambiscale = SECOND_ORDER_SCALE;
611 else if(conf->ChanMask > 0x1)
612 ambiscale = FIRST_ORDER_SCALE;
613 else
614 ambiscale = 0.0f;
616 if(conf->CoeffScale == ADS_SN3D)
617 coeff_scale = SN3D2N3DScale;
618 else if(conf->CoeffScale == ADS_FuMa)
619 coeff_scale = FuMa2N3DScale;
621 for(i = 0;i < conf->NumSpeakers;i++)
623 ALuint chan = speakermap[i];
624 ALfloat gain;
625 ALuint k = 0;
627 for(j = 0;j < MAX_AMBI_COEFFS;j++)
628 chanmap[i].Config[j] = 0.0f;
630 chanmap[i].ChanName = device->RealOut.ChannelName[chan];
631 for(j = 0;j < MAX_AMBI_COEFFS;j++)
633 if(j == 0) gain = conf->HFOrderGain[0];
634 else if(j == 1) gain = conf->HFOrderGain[1];
635 else if(j == 4) gain = conf->HFOrderGain[2];
636 else if(j == 9) gain = conf->HFOrderGain[3];
637 if((conf->ChanMask&(1<<j)))
638 chanmap[i].Config[j] = conf->HFMatrix[i][k++] / coeff_scale[j] * gain;
642 SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs, chanmap,
643 conf->NumSpeakers, &device->Dry.NumChannels, AL_FALSE);
644 device->Dry.CoeffCount = (conf->ChanMask > 0x1ff) ? 16 :
645 (conf->ChanMask > 0xf) ? 9 : 4;
647 memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
648 for(i = 0;i < device->Dry.NumChannels;i++)
650 device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0];
651 for(j = 1;j < 4;j++)
652 device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * ambiscale;
654 device->FOAOut.CoeffCount = 4;
657 static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALuint speakermap[MAX_OUTPUT_CHANNELS])
659 const char *devname;
660 int decflags = 0;
661 size_t count;
662 ALuint i;
664 devname = al_string_get_cstr(device->DeviceName);
665 if(GetConfigValueBool(devname, "decoder", "distance-comp", 1))
666 decflags |= BFDF_DistanceComp;
668 if((conf->ChanMask & ~0x831b))
670 count = (conf->ChanMask > 0x1ff) ? 16 :
671 (conf->ChanMask > 0xf) ? 9 : 4;
672 for(i = 0;i < count;i++)
674 device->Dry.Ambi.Map[i].Scale = 1.0f;
675 device->Dry.Ambi.Map[i].Index = i;
678 else
680 static int map[MAX_AMBI_COEFFS] = { 0, 1, 3, 4, 8, 9, 15 };
682 count = (conf->ChanMask > 0x1ff) ? 7 :
683 (conf->ChanMask > 0xf) ? 5 : 3;
684 for(i = 0;i < count;i++)
686 device->Dry.Ambi.Map[i].Scale = 1.0f;
687 device->Dry.Ambi.Map[i].Index = map[i];
690 device->Dry.CoeffCount = 0;
691 device->Dry.NumChannels = count;
693 TRACE("Enabling %s-band %s-order%s ambisonic decoder\n",
694 (conf->FreqBands == 1) ? "single" : "dual",
695 (conf->ChanMask > 0xf) ? (conf->ChanMask > 0x1ff) ? "third" : "second" : "first",
696 (conf->ChanMask & ~0x831b) ? " periphonic" : ""
698 bformatdec_reset(device->AmbiDecoder, conf, count, device->Frequency,
699 speakermap, decflags);
701 if(bformatdec_getOrder(device->AmbiDecoder) < 2)
703 memcpy(&device->FOAOut.Ambi, &device->Dry.Ambi, sizeof(device->FOAOut.Ambi));
704 device->FOAOut.CoeffCount = device->Dry.CoeffCount;
706 else
708 memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
709 for(i = 0;i < 4;i++)
711 device->FOAOut.Ambi.Map[i].Scale = 1.0f;
712 device->FOAOut.Ambi.Map[i].Index = i;
714 device->FOAOut.CoeffCount = 0;
718 static void InitHrtfPanning(ALCdevice *device)
720 static const enum Channel CubeChannels[MAX_OUTPUT_CHANNELS] = {
721 UpperFrontLeft, UpperFrontRight, UpperBackLeft, UpperBackRight,
722 LowerFrontLeft, LowerFrontRight, LowerBackLeft, LowerBackRight,
723 InvalidChannel, InvalidChannel, InvalidChannel, InvalidChannel,
724 InvalidChannel, InvalidChannel, InvalidChannel, InvalidChannel
726 static const ChannelMap Cube8Cfg[8] = {
727 { UpperFrontLeft, { 0.176776695f, 0.072168784f, 0.072168784f, 0.072168784f } },
728 { UpperFrontRight, { 0.176776695f, 0.072168784f, -0.072168784f, 0.072168784f } },
729 { UpperBackLeft, { 0.176776695f, -0.072168784f, 0.072168784f, 0.072168784f } },
730 { UpperBackRight, { 0.176776695f, -0.072168784f, -0.072168784f, 0.072168784f } },
731 { LowerFrontLeft, { 0.176776695f, 0.072168784f, 0.072168784f, -0.072168784f } },
732 { LowerFrontRight, { 0.176776695f, 0.072168784f, -0.072168784f, -0.072168784f } },
733 { LowerBackLeft, { 0.176776695f, -0.072168784f, 0.072168784f, -0.072168784f } },
734 { LowerBackRight, { 0.176776695f, -0.072168784f, -0.072168784f, -0.072168784f } },
736 static const struct {
737 enum Channel Channel;
738 ALfloat Angle;
739 ALfloat Elevation;
740 } CubeInfo[8] = {
741 { UpperFrontLeft, DEG2RAD( -45.0f), DEG2RAD( 45.0f) },
742 { UpperFrontRight, DEG2RAD( 45.0f), DEG2RAD( 45.0f) },
743 { UpperBackLeft, DEG2RAD(-135.0f), DEG2RAD( 45.0f) },
744 { UpperBackRight, DEG2RAD( 135.0f), DEG2RAD( 45.0f) },
745 { LowerFrontLeft, DEG2RAD( -45.0f), DEG2RAD(-45.0f) },
746 { LowerFrontRight, DEG2RAD( 45.0f), DEG2RAD(-45.0f) },
747 { LowerBackLeft, DEG2RAD(-135.0f), DEG2RAD(-45.0f) },
748 { LowerBackRight, DEG2RAD( 135.0f), DEG2RAD(-45.0f) },
750 const ChannelMap *chanmap = Cube8Cfg;
751 size_t count = COUNTOF(Cube8Cfg);
752 ALuint i;
754 SetChannelMap(CubeChannels, device->Dry.Ambi.Coeffs, chanmap, count,
755 &device->Dry.NumChannels, AL_TRUE);
756 device->Dry.CoeffCount = 4;
758 memcpy(&device->FOAOut.Ambi, &device->Dry.Ambi, sizeof(device->FOAOut.Ambi));
759 device->FOAOut.CoeffCount = device->Dry.CoeffCount;
761 for(i = 0;i < device->Dry.NumChannels;i++)
763 int chan = GetChannelIndex(CubeChannels, CubeInfo[i].Channel);
764 GetLerpedHrtfCoeffs(device->Hrtf, CubeInfo[i].Elevation, CubeInfo[i].Angle, 1.0f, 0.0f,
765 device->Hrtf_Params[chan].Coeffs, device->Hrtf_Params[chan].Delay);
769 static void InitUhjPanning(ALCdevice *device)
771 size_t count = 3;
772 ALuint i;
774 for(i = 0;i < count;i++)
776 ALuint acn = FuMa2ACN[i];
777 device->Dry.Ambi.Map[i].Scale = 1.0f/FuMa2N3DScale[acn];
778 device->Dry.Ambi.Map[i].Index = acn;
780 device->Dry.CoeffCount = 0;
781 device->Dry.NumChannels = count;
783 memcpy(&device->FOAOut.Ambi, &device->Dry.Ambi, sizeof(device->FOAOut.Ambi));
784 device->FOAOut.CoeffCount = device->Dry.CoeffCount;
787 void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf_appreq, enum HrtfRequestMode hrtf_userreq)
789 const char *mode;
790 bool headphones;
791 int bs2blevel;
792 size_t i;
794 device->Hrtf = NULL;
795 al_string_clear(&device->Hrtf_Name);
796 device->Render_Mode = NormalRender;
798 memset(&device->Dry.Ambi, 0, sizeof(device->Dry.Ambi));
799 device->Dry.CoeffCount = 0;
800 device->Dry.NumChannels = 0;
802 if(device->FmtChans != DevFmtStereo)
804 ALuint speakermap[MAX_OUTPUT_CHANNELS];
805 const char *devname, *layout = NULL;
806 AmbDecConf conf, *pconf = NULL;
808 if(hrtf_appreq == Hrtf_Enable)
809 device->Hrtf_Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
811 ambdec_init(&conf);
813 devname = al_string_get_cstr(device->DeviceName);
814 switch(device->FmtChans)
816 case DevFmtQuad: layout = "quad"; break;
817 case DevFmtX51: layout = "surround51"; break;
818 case DevFmtX51Rear: layout = "surround51rear"; break;
819 case DevFmtX61: layout = "surround61"; break;
820 case DevFmtX71: layout = "surround71"; break;
821 /* Mono, Stereo, and B-Fornat output don't use custom decoders. */
822 case DevFmtMono:
823 case DevFmtStereo:
824 case DevFmtBFormat3D:
825 break;
827 if(layout)
829 const char *fname;
830 if(ConfigValueStr(devname, "decoder", layout, &fname))
832 if(!ambdec_load(&conf, fname))
833 ERR("Failed to load layout file %s\n", fname);
834 else
836 if(conf.ChanMask > 0xffff)
837 ERR("Unsupported channel mask 0x%04x (max 0xffff)\n", conf.ChanMask);
838 else
840 if(MakeSpeakerMap(device, &conf, speakermap))
841 pconf = &conf;
847 if(pconf && GetConfigValueBool(devname, "decoder", "hq-mode", 0))
849 if(!device->AmbiDecoder)
850 device->AmbiDecoder = bformatdec_alloc();
852 else
854 bformatdec_free(device->AmbiDecoder);
855 device->AmbiDecoder = NULL;
858 if(!pconf)
859 InitPanning(device);
860 else if(device->AmbiDecoder)
861 InitHQPanning(device, pconf, speakermap);
862 else
863 InitCustomPanning(device, pconf, speakermap);
865 ambdec_deinit(&conf);
866 return;
869 bformatdec_free(device->AmbiDecoder);
870 device->AmbiDecoder = NULL;
872 headphones = device->IsHeadphones;
873 if(device->Type != Loopback)
875 const char *mode;
876 if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "stereo-mode", &mode))
878 if(strcasecmp(mode, "headphones") == 0)
879 headphones = true;
880 else if(strcasecmp(mode, "speakers") == 0)
881 headphones = false;
882 else if(strcasecmp(mode, "auto") != 0)
883 ERR("Unexpected stereo-mode: %s\n", mode);
887 if(hrtf_userreq == Hrtf_Default)
889 bool usehrtf = (headphones && hrtf_appreq != Hrtf_Disable) ||
890 (hrtf_appreq == Hrtf_Enable);
891 if(!usehrtf) goto no_hrtf;
893 device->Hrtf_Status = ALC_HRTF_ENABLED_SOFT;
894 if(headphones && hrtf_appreq != Hrtf_Disable)
895 device->Hrtf_Status = ALC_HRTF_HEADPHONES_DETECTED_SOFT;
897 else
899 if(hrtf_userreq != Hrtf_Enable)
901 if(hrtf_appreq == Hrtf_Enable)
902 device->Hrtf_Status = ALC_HRTF_DENIED_SOFT;
903 goto no_hrtf;
905 device->Hrtf_Status = ALC_HRTF_REQUIRED_SOFT;
908 if(VECTOR_SIZE(device->Hrtf_List) == 0)
910 VECTOR_DEINIT(device->Hrtf_List);
911 device->Hrtf_List = EnumerateHrtf(device->DeviceName);
914 if(hrtf_id >= 0 && (size_t)hrtf_id < VECTOR_SIZE(device->Hrtf_List))
916 const HrtfEntry *entry = &VECTOR_ELEM(device->Hrtf_List, hrtf_id);
917 if(GetHrtfSampleRate(entry->hrtf) == device->Frequency)
919 device->Hrtf = entry->hrtf;
920 al_string_copy(&device->Hrtf_Name, entry->name);
924 for(i = 0;!device->Hrtf && i < VECTOR_SIZE(device->Hrtf_List);i++)
926 const HrtfEntry *entry = &VECTOR_ELEM(device->Hrtf_List, i);
927 if(GetHrtfSampleRate(entry->hrtf) == device->Frequency)
929 device->Hrtf = entry->hrtf;
930 al_string_copy(&device->Hrtf_Name, entry->name);
934 if(device->Hrtf)
936 device->Render_Mode = HrtfRender;
937 if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "hrtf-mode", &mode))
939 if(strcasecmp(mode, "full") == 0)
940 device->Render_Mode = HrtfRender;
941 else if(strcasecmp(mode, "basic") == 0)
942 device->Render_Mode = NormalRender;
943 else
944 ERR("Unexpected hrtf-mode: %s\n", mode);
947 TRACE("HRTF enabled, \"%s\"\n", al_string_get_cstr(device->Hrtf_Name));
948 InitHrtfPanning(device);
949 return;
951 device->Hrtf_Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
953 no_hrtf:
954 TRACE("HRTF disabled\n");
956 bs2blevel = ((headphones && hrtf_appreq != Hrtf_Disable) ||
957 (hrtf_appreq == Hrtf_Enable)) ? 5 : 0;
958 if(device->Type != Loopback)
959 ConfigValueInt(al_string_get_cstr(device->DeviceName), NULL, "cf_level", &bs2blevel);
960 if(bs2blevel > 0 && bs2blevel <= 6)
962 device->Bs2b = al_calloc(16, sizeof(*device->Bs2b));
963 bs2b_set_params(device->Bs2b, bs2blevel, device->Frequency);
964 device->Render_Mode = StereoPair;
965 TRACE("BS2B enabled\n");
966 InitPanning(device);
967 return;
970 TRACE("BS2B disabled\n");
972 device->Render_Mode = NormalRender;
973 if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "stereo-panning", &mode))
975 if(strcasecmp(mode, "paired") == 0)
976 device->Render_Mode = StereoPair;
977 else if(strcasecmp(mode, "uhj") != 0)
978 ERR("Unexpected stereo-panning: %s\n", mode);
980 if(device->Render_Mode == NormalRender)
982 device->Uhj_Encoder = al_calloc(16, sizeof(Uhj2Encoder));
983 TRACE("UHJ enabled\n");
984 InitUhjPanning(device);
985 return;
988 TRACE("UHJ disabled\n");
989 InitPanning(device);
993 void aluInitEffectPanning(ALeffectslot *slot)
995 ALuint i;
997 memset(slot->ChanMap, 0, sizeof(slot->ChanMap));
998 slot->NumChannels = 0;
1000 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
1002 slot->ChanMap[i].Scale = 1.0f;
1003 slot->ChanMap[i].Index = i;
1005 slot->NumChannels = i;