Use unique_ptr for bs2b
[openal-soft.git] / Alc / panning.cpp
blob29c708f6f993300e49797e182eabf7aab269309a
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 "alconfig.h"
33 #include "ambdec.h"
34 #include "bformatdec.h"
35 #include "filters/splitter.h"
36 #include "uhjfilter.h"
37 #include "bs2b.h"
40 namespace {
42 constexpr ALsizei FuMa2ACN[MAX_AMBI_COEFFS] = {
43 0, /* W */
44 3, /* X */
45 1, /* Y */
46 2, /* Z */
47 6, /* R */
48 7, /* S */
49 5, /* T */
50 8, /* U */
51 4, /* V */
52 12, /* K */
53 13, /* L */
54 11, /* M */
55 14, /* N */
56 10, /* O */
57 15, /* P */
58 9, /* Q */
60 constexpr ALsizei ACN2ACN[MAX_AMBI_COEFFS] = {
61 0, 1, 2, 3, 4, 5, 6, 7,
62 8, 9, 10, 11, 12, 13, 14, 15
65 } // namespace
67 void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALfloat spread,
68 ALfloat coeffs[MAX_AMBI_COEFFS])
70 /* Zeroth-order */
71 coeffs[0] = 1.0f; /* ACN 0 = 1 */
72 /* First-order */
73 coeffs[1] = SQRTF_3 * y; /* ACN 1 = sqrt(3) * Y */
74 coeffs[2] = SQRTF_3 * z; /* ACN 2 = sqrt(3) * Z */
75 coeffs[3] = SQRTF_3 * x; /* ACN 3 = sqrt(3) * X */
76 /* Second-order */
77 coeffs[4] = 3.872983346f * x * y; /* ACN 4 = sqrt(15) * X * Y */
78 coeffs[5] = 3.872983346f * y * z; /* ACN 5 = sqrt(15) * Y * Z */
79 coeffs[6] = 1.118033989f * (3.0f*z*z - 1.0f); /* ACN 6 = sqrt(5)/2 * (3*Z*Z - 1) */
80 coeffs[7] = 3.872983346f * x * z; /* ACN 7 = sqrt(15) * X * Z */
81 coeffs[8] = 1.936491673f * (x*x - y*y); /* ACN 8 = sqrt(15)/2 * (X*X - Y*Y) */
82 /* Third-order */
83 coeffs[9] = 2.091650066f * y * (3.0f*x*x - y*y); /* ACN 9 = sqrt(35/8) * Y * (3*X*X - Y*Y) */
84 coeffs[10] = 10.246950766f * z * x * y; /* ACN 10 = sqrt(105) * Z * X * Y */
85 coeffs[11] = 1.620185175f * y * (5.0f*z*z - 1.0f); /* ACN 11 = sqrt(21/8) * Y * (5*Z*Z - 1) */
86 coeffs[12] = 1.322875656f * z * (5.0f*z*z - 3.0f); /* ACN 12 = sqrt(7)/2 * Z * (5*Z*Z - 3) */
87 coeffs[13] = 1.620185175f * x * (5.0f*z*z - 1.0f); /* ACN 13 = sqrt(21/8) * X * (5*Z*Z - 1) */
88 coeffs[14] = 5.123475383f * z * (x*x - y*y); /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */
89 coeffs[15] = 2.091650066f * x * (x*x - 3.0f*y*y); /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */
91 if(spread > 0.0f)
93 /* Implement the spread by using a spherical source that subtends the
94 * angle spread. See:
95 * http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3
97 * When adjusted for N3D normalization instead of SN3D, these
98 * calculations are:
100 * ZH0 = -sqrt(pi) * (-1+ca);
101 * ZH1 = 0.5*sqrt(pi) * sa*sa;
102 * ZH2 = -0.5*sqrt(pi) * ca*(-1+ca)*(ca+1);
103 * ZH3 = -0.125*sqrt(pi) * (-1+ca)*(ca+1)*(5*ca*ca - 1);
104 * ZH4 = -0.125*sqrt(pi) * ca*(-1+ca)*(ca+1)*(7*ca*ca - 3);
105 * ZH5 = -0.0625*sqrt(pi) * (-1+ca)*(ca+1)*(21*ca*ca*ca*ca - 14*ca*ca + 1);
107 * The gain of the source is compensated for size, so that the
108 * loundness doesn't depend on the spread. Thus:
110 * ZH0 = 1.0f;
111 * ZH1 = 0.5f * (ca+1.0f);
112 * ZH2 = 0.5f * (ca+1.0f)*ca;
113 * ZH3 = 0.125f * (ca+1.0f)*(5.0f*ca*ca - 1.0f);
114 * ZH4 = 0.125f * (ca+1.0f)*(7.0f*ca*ca - 3.0f)*ca;
115 * ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f);
117 ALfloat ca = cosf(spread * 0.5f);
118 /* Increase the source volume by up to +3dB for a full spread. */
119 ALfloat scale = sqrtf(1.0f + spread/F_TAU);
121 ALfloat ZH0_norm = scale;
122 ALfloat ZH1_norm = 0.5f * (ca+1.f) * scale;
123 ALfloat ZH2_norm = 0.5f * (ca+1.f)*ca * scale;
124 ALfloat ZH3_norm = 0.125f * (ca+1.f)*(5.f*ca*ca-1.f) * scale;
126 /* Zeroth-order */
127 coeffs[0] *= ZH0_norm;
128 /* First-order */
129 coeffs[1] *= ZH1_norm;
130 coeffs[2] *= ZH1_norm;
131 coeffs[3] *= ZH1_norm;
132 /* Second-order */
133 coeffs[4] *= ZH2_norm;
134 coeffs[5] *= ZH2_norm;
135 coeffs[6] *= ZH2_norm;
136 coeffs[7] *= ZH2_norm;
137 coeffs[8] *= ZH2_norm;
138 /* Third-order */
139 coeffs[9] *= ZH3_norm;
140 coeffs[10] *= ZH3_norm;
141 coeffs[11] *= ZH3_norm;
142 coeffs[12] *= ZH3_norm;
143 coeffs[13] *= ZH3_norm;
144 coeffs[14] *= ZH3_norm;
145 coeffs[15] *= ZH3_norm;
150 void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat*RESTRICT coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
152 ALsizei i, j;
154 for(i = 0;i < numchans;i++)
156 float gain = 0.0f;
157 for(j = 0;j < numcoeffs;j++)
158 gain += chancoeffs[i][j]*coeffs[j];
159 gains[i] = clampf(gain, 0.0f, 1.0f) * ingain;
161 for(;i < MAX_OUTPUT_CHANNELS;i++)
162 gains[i] = 0.0f;
165 void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat*RESTRICT coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
167 ALsizei i;
169 for(i = 0;i < numchans;i++)
170 gains[i] = chanmap[i].Scale * coeffs[chanmap[i].Index] * ingain;
171 for(;i < MAX_OUTPUT_CHANNELS;i++)
172 gains[i] = 0.0f;
176 static inline const char *GetLabelFromChannel(enum Channel channel)
178 switch(channel)
180 case FrontLeft: return "front-left";
181 case FrontRight: return "front-right";
182 case FrontCenter: return "front-center";
183 case LFE: return "lfe";
184 case BackLeft: return "back-left";
185 case BackRight: return "back-right";
186 case BackCenter: return "back-center";
187 case SideLeft: return "side-left";
188 case SideRight: return "side-right";
190 case UpperFrontLeft: return "upper-front-left";
191 case UpperFrontRight: return "upper-front-right";
192 case UpperBackLeft: return "upper-back-left";
193 case UpperBackRight: return "upper-back-right";
194 case LowerFrontLeft: return "lower-front-left";
195 case LowerFrontRight: return "lower-front-right";
196 case LowerBackLeft: return "lower-back-left";
197 case LowerBackRight: return "lower-back-right";
199 case Aux0: return "aux-0";
200 case Aux1: return "aux-1";
201 case Aux2: return "aux-2";
202 case Aux3: return "aux-3";
203 case Aux4: return "aux-4";
204 case Aux5: return "aux-5";
205 case Aux6: return "aux-6";
206 case Aux7: return "aux-7";
207 case Aux8: return "aux-8";
208 case Aux9: return "aux-9";
209 case Aux10: return "aux-10";
210 case Aux11: return "aux-11";
211 case Aux12: return "aux-12";
212 case Aux13: return "aux-13";
213 case Aux14: return "aux-14";
214 case Aux15: return "aux-15";
216 case InvalidChannel: break;
218 return "(unknown)";
222 typedef struct ChannelMap {
223 enum Channel ChanName;
224 ChannelConfig Config;
225 } ChannelMap;
227 static void SetChannelMap(const enum Channel devchans[MAX_OUTPUT_CHANNELS],
228 ChannelConfig *ambicoeffs, const ChannelMap *chanmap,
229 ALsizei count, ALsizei *outcount)
231 ALsizei maxchans = 0;
232 ALsizei i, j;
234 for(i = 0;i < count;i++)
236 ALint idx = GetChannelIndex(devchans, chanmap[i].ChanName);
237 if(idx < 0)
239 ERR("Failed to find %s channel in device\n",
240 GetLabelFromChannel(chanmap[i].ChanName));
241 continue;
244 maxchans = maxi(maxchans, idx+1);
245 for(j = 0;j < MAX_AMBI_COEFFS;j++)
246 ambicoeffs[idx][j] = chanmap[i].Config[j];
248 *outcount = mini(maxchans, MAX_OUTPUT_CHANNELS);
251 static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALsizei speakermap[MAX_OUTPUT_CHANNELS])
253 ALsizei i;
255 for(i = 0;i < conf->NumSpeakers;i++)
257 enum Channel ch;
258 int chidx = -1;
260 /* NOTE: AmbDec does not define any standard speaker names, however
261 * for this to work we have to by able to find the output channel
262 * the speaker definition corresponds to. Therefore, OpenAL Soft
263 * requires these channel labels to be recognized:
265 * LF = Front left
266 * RF = Front right
267 * LS = Side left
268 * RS = Side right
269 * LB = Back left
270 * RB = Back right
271 * CE = Front center
272 * CB = Back center
274 * Additionally, surround51 will acknowledge back speakers for side
275 * channels, and surround51rear will acknowledge side speakers for
276 * back channels, to avoid issues with an ambdec expecting 5.1 to
277 * use the side channels when the device is configured for back,
278 * and vice-versa.
280 if(conf->Speakers[i].Name == "LF")
281 ch = FrontLeft;
282 else if(conf->Speakers[i].Name == "RF")
283 ch = FrontRight;
284 else if(conf->Speakers[i].Name == "CE")
285 ch = FrontCenter;
286 else if(conf->Speakers[i].Name == "LS")
288 if(device->FmtChans == DevFmtX51Rear)
289 ch = BackLeft;
290 else
291 ch = SideLeft;
293 else if(conf->Speakers[i].Name == "RS")
295 if(device->FmtChans == DevFmtX51Rear)
296 ch = BackRight;
297 else
298 ch = SideRight;
300 else if(conf->Speakers[i].Name == "LB")
302 if(device->FmtChans == DevFmtX51)
303 ch = SideLeft;
304 else
305 ch = BackLeft;
307 else if(conf->Speakers[i].Name == "RB")
309 if(device->FmtChans == DevFmtX51)
310 ch = SideRight;
311 else
312 ch = BackRight;
314 else if(conf->Speakers[i].Name == "CB")
315 ch = BackCenter;
316 else
318 const char *name = conf->Speakers[i].Name.c_str();
319 unsigned int n;
320 char c;
322 if(sscanf(name, "AUX%u%c", &n, &c) == 1 && n < 16)
323 ch = static_cast<enum Channel>(Aux0+n);
324 else
326 ERR("AmbDec speaker label \"%s\" not recognized\n", name);
327 return false;
330 chidx = GetChannelIdxByName(&device->RealOut, ch);
331 if(chidx == -1)
333 ERR("Failed to lookup AmbDec speaker label %s\n",
334 conf->Speakers[i].Name.c_str());
335 return false;
337 speakermap[i] = chidx;
340 return true;
344 static const ChannelMap MonoCfg[1] = {
345 { FrontCenter, { 1.0f } },
346 }, StereoCfg[2] = {
347 { FrontLeft, { 5.00000000e-1f, 2.88675135e-1f, 0.0f, 5.52305643e-2f } },
348 { FrontRight, { 5.00000000e-1f, -2.88675135e-1f, 0.0f, 5.52305643e-2f } },
349 }, QuadCfg[4] = {
350 { BackLeft, { 3.53553391e-1f, 2.04124145e-1f, 0.0f, -2.04124145e-1f } },
351 { FrontLeft, { 3.53553391e-1f, 2.04124145e-1f, 0.0f, 2.04124145e-1f } },
352 { FrontRight, { 3.53553391e-1f, -2.04124145e-1f, 0.0f, 2.04124145e-1f } },
353 { BackRight, { 3.53553391e-1f, -2.04124145e-1f, 0.0f, -2.04124145e-1f } },
354 }, X51SideCfg[4] = {
355 { SideLeft, { 3.33000782e-1f, 1.89084803e-1f, 0.0f, -2.00042375e-1f, -2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } },
356 { FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 0.0f, 1.66295695e-1f, 7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } },
357 { FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 0.0f, 1.66295695e-1f, -7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } },
358 { SideRight, { 3.33000782e-1f, -1.89084803e-1f, 0.0f, -2.00042375e-1f, 2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } },
359 }, X51RearCfg[4] = {
360 { BackLeft, { 3.33000782e-1f, 1.89084803e-1f, 0.0f, -2.00042375e-1f, -2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } },
361 { FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 0.0f, 1.66295695e-1f, 7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } },
362 { FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 0.0f, 1.66295695e-1f, -7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } },
363 { BackRight, { 3.33000782e-1f, -1.89084803e-1f, 0.0f, -2.00042375e-1f, 2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } },
364 }, X61Cfg[6] = {
365 { SideLeft, { 2.04460341e-1f, 2.17177926e-1f, 0.0f, -4.39996780e-2f, -2.60790269e-2f, 0.0f, 0.0f, 0.0f, -6.87239792e-2f } },
366 { FrontLeft, { 1.58923161e-1f, 9.21772680e-2f, 0.0f, 1.59658796e-1f, 6.66278083e-2f, 0.0f, 0.0f, 0.0f, 3.84686854e-2f } },
367 { FrontRight, { 1.58923161e-1f, -9.21772680e-2f, 0.0f, 1.59658796e-1f, -6.66278083e-2f, 0.0f, 0.0f, 0.0f, 3.84686854e-2f } },
368 { SideRight, { 2.04460341e-1f, -2.17177926e-1f, 0.0f, -4.39996780e-2f, 2.60790269e-2f, 0.0f, 0.0f, 0.0f, -6.87239792e-2f } },
369 { BackCenter, { 2.50001688e-1f, 0.00000000e+0f, 0.0f, -2.50000094e-1f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, 6.05133395e-2f } },
370 }, X71Cfg[6] = {
371 { BackLeft, { 2.04124145e-1f, 1.08880247e-1f, 0.0f, -1.88586120e-1f, -1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, 3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } },
372 { SideLeft, { 2.04124145e-1f, 2.17760495e-1f, 0.0f, 0.00000000e+0f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, -1.49071198e-1f, -3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } },
373 { FrontLeft, { 2.04124145e-1f, 1.08880247e-1f, 0.0f, 1.88586120e-1f, 1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, 3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } },
374 { FrontRight, { 2.04124145e-1f, -1.08880247e-1f, 0.0f, 1.88586120e-1f, -1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, -3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } },
375 { SideRight, { 2.04124145e-1f, -2.17760495e-1f, 0.0f, 0.00000000e+0f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, -1.49071198e-1f, 3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } },
376 { BackRight, { 2.04124145e-1f, -1.08880247e-1f, 0.0f, -1.88586120e-1f, 1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, -3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } },
379 static void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei order,
380 const ALsizei *RESTRICT chans_per_order)
382 const char *devname = device->DeviceName.c_str();
383 ALsizei i;
385 if(GetConfigValueBool(devname, "decoder", "nfc", 1) && ctrl_dist > 0.0f)
387 /* NFC is only used when AvgSpeakerDist is greater than 0, and can only
388 * be used when rendering to an ambisonic buffer.
390 device->AvgSpeakerDist = minf(ctrl_dist, 10.0f);
391 TRACE("Using near-field reference distance: %.2f meters\n", device->AvgSpeakerDist);
393 for(i = 0;i < order+1;i++)
394 device->NumChannelsPerOrder[i] = chans_per_order[i];
395 for(;i < MAX_AMBI_ORDER+1;i++)
396 device->NumChannelsPerOrder[i] = 0;
400 static void InitDistanceComp(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS])
402 const char *devname = device->DeviceName.c_str();
403 ALfloat maxdist = 0.0f;
404 size_t total = 0;
405 ALsizei i;
407 for(i = 0;i < conf->NumSpeakers;i++)
408 maxdist = maxf(maxdist, conf->Speakers[i].Distance);
410 if(GetConfigValueBool(devname, "decoder", "distance-comp", 1) && maxdist > 0.0f)
412 ALfloat srate = (ALfloat)device->Frequency;
413 for(i = 0;i < conf->NumSpeakers;i++)
415 ALsizei chan = speakermap[i];
416 ALfloat delay;
418 /* Distance compensation only delays in steps of the sample rate.
419 * This is a bit less accurate since the delay time falls to the
420 * nearest sample time, but it's far simpler as it doesn't have to
421 * deal with phase offsets. This means at 48khz, for instance, the
422 * distance delay will be in steps of about 7 millimeters.
424 delay = floorf((maxdist-conf->Speakers[i].Distance) / SPEEDOFSOUNDMETRESPERSEC *
425 srate + 0.5f);
426 if(delay >= (ALfloat)MAX_DELAY_LENGTH)
427 ERR("Delay for speaker \"%s\" exceeds buffer length (%f >= %u)\n",
428 conf->Speakers[i].Name.c_str(), delay, MAX_DELAY_LENGTH);
430 device->ChannelDelay[chan].Length = (ALsizei)clampf(
431 delay, 0.0f, (ALfloat)(MAX_DELAY_LENGTH-1)
433 device->ChannelDelay[chan].Gain = conf->Speakers[i].Distance / maxdist;
434 TRACE("Channel %u \"%s\" distance compensation: %d samples, %f gain\n", chan,
435 conf->Speakers[i].Name.c_str(), device->ChannelDelay[chan].Length,
436 device->ChannelDelay[chan].Gain
439 /* Round up to the next 4th sample, so each channel buffer starts
440 * 16-byte aligned.
442 total += RoundUp(device->ChannelDelay[chan].Length, 4);
446 if(total > 0)
448 device->ChannelDelay.resize(total);
449 device->ChannelDelay[0].Buffer = device->ChannelDelay.data();
450 for(i = 1;i < MAX_OUTPUT_CHANNELS;i++)
452 size_t len = RoundUp(device->ChannelDelay[i-1].Length, 4);
453 device->ChannelDelay[i].Buffer = device->ChannelDelay[i-1].Buffer + len;
458 static void InitPanning(ALCdevice *device)
460 const ChannelMap *chanmap = NULL;
461 ALsizei coeffcount = 0;
462 ALsizei count = 0;
463 ALsizei i, j;
465 switch(device->FmtChans)
467 case DevFmtMono:
468 count = COUNTOF(MonoCfg);
469 chanmap = MonoCfg;
470 coeffcount = 1;
471 break;
473 case DevFmtStereo:
474 count = COUNTOF(StereoCfg);
475 chanmap = StereoCfg;
476 coeffcount = 4;
477 break;
479 case DevFmtQuad:
480 count = COUNTOF(QuadCfg);
481 chanmap = QuadCfg;
482 coeffcount = 4;
483 break;
485 case DevFmtX51:
486 count = COUNTOF(X51SideCfg);
487 chanmap = X51SideCfg;
488 coeffcount = 9;
489 break;
491 case DevFmtX51Rear:
492 count = COUNTOF(X51RearCfg);
493 chanmap = X51RearCfg;
494 coeffcount = 9;
495 break;
497 case DevFmtX61:
498 count = COUNTOF(X61Cfg);
499 chanmap = X61Cfg;
500 coeffcount = 9;
501 break;
503 case DevFmtX71:
504 count = COUNTOF(X71Cfg);
505 chanmap = X71Cfg;
506 coeffcount = 16;
507 break;
509 case DevFmtAmbi3D:
510 break;
513 if(device->FmtChans == DevFmtAmbi3D)
515 const char *devname = device->DeviceName.c_str();
516 const ALsizei *acnmap = (device->mAmbiLayout == AmbiLayout::FuMa) ? FuMa2ACN : ACN2ACN;
517 const ALfloat *n3dscale = (device->mAmbiScale == AmbiNorm::FuMa) ? FuMa2N3DScale :
518 (device->mAmbiScale == AmbiNorm::SN3D) ? SN3D2N3DScale :
519 /*(device->mAmbiScale == AmbiNorm::N3D) ?*/ N3D2N3DScale;
520 ALfloat nfc_delay = 0.0f;
522 count = (device->mAmbiOrder == 3) ? 16 :
523 (device->mAmbiOrder == 2) ? 9 :
524 (device->mAmbiOrder == 1) ? 4 : 1;
525 for(i = 0;i < count;i++)
527 ALsizei acn = acnmap[i];
528 device->Dry.Ambi.Map[i].Scale = 1.0f/n3dscale[acn];
529 device->Dry.Ambi.Map[i].Index = acn;
531 device->Dry.CoeffCount = 0;
532 device->Dry.NumChannels = count;
534 if(device->mAmbiOrder < 2)
536 device->FOAOut.Ambi = device->Dry.Ambi;
537 device->FOAOut.CoeffCount = device->Dry.CoeffCount;
538 device->FOAOut.NumChannels = 0;
540 else
542 ALfloat w_scale=1.0f, xyz_scale=1.0f;
544 /* FOA output is always ACN+N3D for higher-order ambisonic output.
545 * The upsampler expects this and will convert it for output.
547 memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
548 for(i = 0;i < 4;i++)
550 device->FOAOut.Ambi.Map[i].Scale = 1.0f;
551 device->FOAOut.Ambi.Map[i].Index = i;
553 device->FOAOut.CoeffCount = 0;
554 device->FOAOut.NumChannels = 4;
556 if(device->mAmbiOrder >= 3)
558 w_scale = W_SCALE_3H3P;
559 xyz_scale = XYZ_SCALE_3H3P;
561 else
563 w_scale = W_SCALE_2H2P;
564 xyz_scale = XYZ_SCALE_2H2P;
566 ambiup_reset(device->AmbiUp.get(), device, w_scale, xyz_scale);
569 if(ConfigValueFloat(devname, "decoder", "nfc-ref-delay", &nfc_delay) && nfc_delay > 0.0f)
571 static const ALsizei chans_per_order[MAX_AMBI_ORDER+1] = {
572 1, 3, 5, 7
574 nfc_delay = clampf(nfc_delay, 0.001f, 1000.0f);
575 InitNearFieldCtrl(device, nfc_delay * SPEEDOFSOUNDMETRESPERSEC,
576 device->mAmbiOrder, chans_per_order);
579 else
581 ALfloat w_scale, xyz_scale;
583 SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs,
584 chanmap, count, &device->Dry.NumChannels);
585 device->Dry.CoeffCount = coeffcount;
587 w_scale = (device->Dry.CoeffCount > 9) ? W_SCALE_3H0P :
588 (device->Dry.CoeffCount > 4) ? W_SCALE_2H0P : 1.0f;
589 xyz_scale = (device->Dry.CoeffCount > 9) ? XYZ_SCALE_3H0P :
590 (device->Dry.CoeffCount > 4) ? XYZ_SCALE_2H0P : 1.0f;
592 memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
593 for(i = 0;i < device->Dry.NumChannels;i++)
595 device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0] * w_scale;
596 for(j = 1;j < 4;j++)
597 device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * xyz_scale;
599 device->FOAOut.CoeffCount = 4;
600 device->FOAOut.NumChannels = 0;
602 device->RealOut.NumChannels = 0;
605 static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS])
607 ChannelMap chanmap[MAX_OUTPUT_CHANNELS];
608 const ALfloat *coeff_scale = N3D2N3DScale;
609 ALfloat w_scale = 1.0f;
610 ALfloat xyz_scale = 1.0f;
611 ALsizei i, j;
613 if(conf->FreqBands != 1)
614 ERR("Basic renderer uses the high-frequency matrix as single-band (xover_freq = %.0fhz)\n",
615 conf->XOverFreq);
617 if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
619 if(conf->ChanMask > 0x1ff)
621 w_scale = W_SCALE_3H3P;
622 xyz_scale = XYZ_SCALE_3H3P;
624 else if(conf->ChanMask > 0xf)
626 w_scale = W_SCALE_2H2P;
627 xyz_scale = XYZ_SCALE_2H2P;
630 else
632 if(conf->ChanMask > 0x1ff)
634 w_scale = W_SCALE_3H0P;
635 xyz_scale = XYZ_SCALE_3H0P;
637 else if(conf->ChanMask > 0xf)
639 w_scale = W_SCALE_2H0P;
640 xyz_scale = XYZ_SCALE_2H0P;
644 if(conf->CoeffScale == AmbDecScale::SN3D)
645 coeff_scale = SN3D2N3DScale;
646 else if(conf->CoeffScale == AmbDecScale::FuMa)
647 coeff_scale = FuMa2N3DScale;
649 for(i = 0;i < conf->NumSpeakers;i++)
651 ALsizei chan = speakermap[i];
652 ALfloat gain;
653 ALsizei k = 0;
655 for(j = 0;j < MAX_AMBI_COEFFS;j++)
656 chanmap[i].Config[j] = 0.0f;
658 chanmap[i].ChanName = device->RealOut.ChannelName[chan];
659 for(j = 0;j < MAX_AMBI_COEFFS;j++)
661 if(j == 0) gain = conf->HFOrderGain[0];
662 else if(j == 1) gain = conf->HFOrderGain[1];
663 else if(j == 4) gain = conf->HFOrderGain[2];
664 else if(j == 9) gain = conf->HFOrderGain[3];
665 if((conf->ChanMask&(1<<j)))
666 chanmap[i].Config[j] = conf->HFMatrix[i][k++] / coeff_scale[j] * gain;
670 SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs, chanmap,
671 conf->NumSpeakers, &device->Dry.NumChannels);
672 device->Dry.CoeffCount = (conf->ChanMask > 0x1ff) ? 16 :
673 (conf->ChanMask > 0xf) ? 9 : 4;
675 memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
676 for(i = 0;i < device->Dry.NumChannels;i++)
678 device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0] * w_scale;
679 for(j = 1;j < 4;j++)
680 device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * xyz_scale;
682 device->FOAOut.CoeffCount = 4;
683 device->FOAOut.NumChannels = 0;
685 device->RealOut.NumChannels = 0;
687 InitDistanceComp(device, conf, speakermap);
690 static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS])
692 static const ALsizei chans_per_order2d[MAX_AMBI_ORDER+1] = { 1, 2, 2, 2 };
693 static const ALsizei chans_per_order3d[MAX_AMBI_ORDER+1] = { 1, 3, 5, 7 };
694 ALfloat avg_dist;
695 ALsizei count;
696 ALsizei i;
698 if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
700 count = (conf->ChanMask > 0x1ff) ? 16 :
701 (conf->ChanMask > 0xf) ? 9 : 4;
702 for(i = 0;i < count;i++)
704 device->Dry.Ambi.Map[i].Scale = 1.0f;
705 device->Dry.Ambi.Map[i].Index = i;
708 else
710 static const int map[MAX_AMBI2D_COEFFS] = { 0, 1, 3, 4, 8, 9, 15 };
712 count = (conf->ChanMask > 0x1ff) ? 7 :
713 (conf->ChanMask > 0xf) ? 5 : 3;
714 for(i = 0;i < count;i++)
716 device->Dry.Ambi.Map[i].Scale = 1.0f;
717 device->Dry.Ambi.Map[i].Index = map[i];
720 device->Dry.CoeffCount = 0;
721 device->Dry.NumChannels = count;
723 TRACE("Enabling %s-band %s-order%s ambisonic decoder\n",
724 (conf->FreqBands == 1) ? "single" : "dual",
725 (conf->ChanMask > 0xf) ? (conf->ChanMask > 0x1ff) ? "third" : "second" : "first",
726 (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? " periphonic" : ""
728 bformatdec_reset(device->AmbiDecoder.get(), conf, count, device->Frequency, speakermap);
730 if(conf->ChanMask <= 0xf)
732 device->FOAOut.Ambi = device->Dry.Ambi;
733 device->FOAOut.CoeffCount = device->Dry.CoeffCount;
734 device->FOAOut.NumChannels = 0;
736 else
738 memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
739 if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
741 count = 4;
742 for(i = 0;i < count;i++)
744 device->FOAOut.Ambi.Map[i].Scale = 1.0f;
745 device->FOAOut.Ambi.Map[i].Index = i;
748 else
750 static const int map[3] = { 0, 1, 3 };
751 count = 3;
752 for(i = 0;i < count;i++)
754 device->FOAOut.Ambi.Map[i].Scale = 1.0f;
755 device->FOAOut.Ambi.Map[i].Index = map[i];
758 device->FOAOut.CoeffCount = 0;
759 device->FOAOut.NumChannels = count;
762 device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->mAmbiOrder);
764 avg_dist = 0.0f;
765 for(i = 0;i < conf->NumSpeakers;i++)
766 avg_dist += conf->Speakers[i].Distance;
767 avg_dist /= (ALfloat)conf->NumSpeakers;
768 InitNearFieldCtrl(device, avg_dist,
769 (conf->ChanMask > 0x1ff) ? 3 : (conf->ChanMask > 0xf) ? 2 : 1,
770 (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? chans_per_order3d : chans_per_order2d
773 InitDistanceComp(device, conf, speakermap);
776 static void InitHrtfPanning(ALCdevice *device)
778 /* NOTE: azimuth goes clockwise. */
779 static const struct AngularPoint AmbiPoints[] = {
780 { DEG2RAD( 90.0f), DEG2RAD( 0.0f) },
781 { DEG2RAD( 35.2643897f), DEG2RAD( 45.0f) },
782 { DEG2RAD( 35.2643897f), DEG2RAD( 135.0f) },
783 { DEG2RAD( 35.2643897f), DEG2RAD(-135.0f) },
784 { DEG2RAD( 35.2643897f), DEG2RAD( -45.0f) },
785 { DEG2RAD( 0.0f), DEG2RAD( 0.0f) },
786 { DEG2RAD( 0.0f), DEG2RAD( 45.0f) },
787 { DEG2RAD( 0.0f), DEG2RAD( 90.0f) },
788 { DEG2RAD( 0.0f), DEG2RAD( 135.0f) },
789 { DEG2RAD( 0.0f), DEG2RAD( 180.0f) },
790 { DEG2RAD( 0.0f), DEG2RAD(-135.0f) },
791 { DEG2RAD( 0.0f), DEG2RAD( -90.0f) },
792 { DEG2RAD( 0.0f), DEG2RAD( -45.0f) },
793 { DEG2RAD(-35.2643897f), DEG2RAD( 45.0f) },
794 { DEG2RAD(-35.2643897f), DEG2RAD( 135.0f) },
795 { DEG2RAD(-35.2643897f), DEG2RAD(-135.0f) },
796 { DEG2RAD(-35.2643897f), DEG2RAD( -45.0f) },
797 { DEG2RAD(-90.0f), DEG2RAD( 0.0f) },
799 static const ALfloat AmbiMatrixFOA[][MAX_AMBI_COEFFS] = {
800 { 5.55555556e-02f, 0.00000000e+00f, 1.23717915e-01f, 0.00000000e+00f },
801 { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f },
802 { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f },
803 { 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f },
804 { 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f },
805 { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, 8.66025404e-02f },
806 { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f },
807 { 5.55555556e-02f, -8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f },
808 { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f },
809 { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, -8.66025404e-02f },
810 { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f },
811 { 5.55555556e-02f, 8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f },
812 { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f },
813 { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f },
814 { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f },
815 { 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f },
816 { 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f },
817 { 5.55555556e-02f, 0.00000000e+00f, -1.23717915e-01f, 0.00000000e+00f },
818 }, AmbiMatrixHOA[][MAX_AMBI_COEFFS] = {
819 { 5.55555556e-02f, 0.00000000e+00f, 1.23717915e-01f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f },
820 { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f },
821 { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f },
822 { 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f },
823 { 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f },
824 { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, 8.66025404e-02f, 0.00000000e+00f, 1.29099445e-01f },
825 { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f, -6.83467648e-02f, 0.00000000e+00f },
826 { 5.55555556e-02f, -8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -1.29099445e-01f },
827 { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f, 6.83467648e-02f, 0.00000000e+00f },
828 { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, -8.66025404e-02f, 0.00000000e+00f, 1.29099445e-01f },
829 { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f, -6.83467648e-02f, 0.00000000e+00f },
830 { 5.55555556e-02f, 8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -1.29099445e-01f },
831 { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f, 6.83467648e-02f, 0.00000000e+00f },
832 { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f },
833 { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f },
834 { 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f },
835 { 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f },
836 { 5.55555556e-02f, 0.00000000e+00f, -1.23717915e-01f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f },
838 static const ALfloat AmbiOrderHFGainFOA[MAX_AMBI_ORDER+1] = {
839 3.00000000e+00f, 1.73205081e+00f
840 }, AmbiOrderHFGainHOA[MAX_AMBI_ORDER+1] = {
841 2.40192231e+00f, 1.86052102e+00f, 9.60768923e-01f
843 static const ALsizei IndexMap[6] = { 0, 1, 2, 3, 4, 8 };
844 static const ALsizei ChansPerOrder[MAX_AMBI_ORDER+1] = { 1, 3, 2, 0 };
845 const ALfloat (*RESTRICT AmbiMatrix)[MAX_AMBI_COEFFS] = AmbiMatrixFOA;
846 const ALfloat *RESTRICT AmbiOrderHFGain = AmbiOrderHFGainFOA;
847 ALsizei count = 4;
848 ALsizei i;
850 static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrixFOA), "FOA Ambisonic HRTF mismatch");
851 static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrixHOA), "HOA Ambisonic HRTF mismatch");
853 if(device->AmbiUp)
855 AmbiMatrix = AmbiMatrixHOA;
856 AmbiOrderHFGain = AmbiOrderHFGainHOA;
857 count = COUNTOF(IndexMap);
860 device->Hrtf = reinterpret_cast<DirectHrtfState*>(
861 al_calloc(16, FAM_SIZE(DirectHrtfState, Chan, count)));
863 for(i = 0;i < count;i++)
865 device->Dry.Ambi.Map[i].Scale = 1.0f;
866 device->Dry.Ambi.Map[i].Index = IndexMap[i];
868 device->Dry.CoeffCount = 0;
869 device->Dry.NumChannels = count;
871 if(device->AmbiUp)
873 memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
874 for(i = 0;i < 4;i++)
876 device->FOAOut.Ambi.Map[i].Scale = 1.0f;
877 device->FOAOut.Ambi.Map[i].Index = i;
879 device->FOAOut.CoeffCount = 0;
880 device->FOAOut.NumChannels = 4;
882 ambiup_reset(device->AmbiUp.get(), device, AmbiOrderHFGainFOA[0] / AmbiOrderHFGain[0],
883 AmbiOrderHFGainFOA[1] / AmbiOrderHFGain[1]);
885 else
887 device->FOAOut.Ambi = device->Dry.Ambi;
888 device->FOAOut.CoeffCount = device->Dry.CoeffCount;
889 device->FOAOut.NumChannels = 0;
892 device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->mAmbiOrder);
894 BuildBFormatHrtf(device->HrtfHandle,
895 device->Hrtf, device->Dry.NumChannels, AmbiPoints, AmbiMatrix, COUNTOF(AmbiPoints),
896 AmbiOrderHFGain
899 InitNearFieldCtrl(device, device->HrtfHandle->distance, device->AmbiUp ? 2 : 1,
900 ChansPerOrder);
903 static void InitUhjPanning(ALCdevice *device)
905 ALsizei count = 3;
906 ALsizei i;
908 for(i = 0;i < count;i++)
910 ALsizei acn = FuMa2ACN[i];
911 device->Dry.Ambi.Map[i].Scale = 1.0f/FuMa2N3DScale[acn];
912 device->Dry.Ambi.Map[i].Index = acn;
914 device->Dry.CoeffCount = 0;
915 device->Dry.NumChannels = count;
917 device->FOAOut.Ambi = device->Dry.Ambi;
918 device->FOAOut.CoeffCount = device->Dry.CoeffCount;
919 device->FOAOut.NumChannels = 0;
921 device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->mAmbiOrder);
924 void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf_appreq, enum HrtfRequestMode hrtf_userreq)
926 /* Hold the HRTF the device last used, in case it's used again. */
927 struct Hrtf *old_hrtf = device->HrtfHandle;
928 const char *mode;
929 bool headphones;
930 int bs2blevel;
931 size_t i;
933 al_free(device->Hrtf);
934 device->Hrtf = nullptr;
935 device->HrtfHandle = nullptr;
936 device->HrtfName.clear();
937 device->Render_Mode = NormalRender;
939 memset(&device->Dry.Ambi, 0, sizeof(device->Dry.Ambi));
940 device->Dry.CoeffCount = 0;
941 device->Dry.NumChannels = 0;
942 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
943 device->NumChannelsPerOrder[i] = 0;
945 device->AvgSpeakerDist = 0.0f;
946 device->ChannelDelay.clear();
948 device->Stablizer = nullptr;
950 if(device->FmtChans != DevFmtStereo)
952 ALsizei speakermap[MAX_OUTPUT_CHANNELS];
953 const char *devname, *layout = NULL;
954 AmbDecConf conf, *pconf = NULL;
956 if(old_hrtf)
957 Hrtf_DecRef(old_hrtf);
958 old_hrtf = NULL;
959 if(hrtf_appreq == Hrtf_Enable)
960 device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
962 devname = device->DeviceName.c_str();
963 switch(device->FmtChans)
965 case DevFmtQuad: layout = "quad"; break;
966 case DevFmtX51: /* fall-through */
967 case DevFmtX51Rear: layout = "surround51"; break;
968 case DevFmtX61: layout = "surround61"; break;
969 case DevFmtX71: layout = "surround71"; break;
970 /* Mono, Stereo, and Ambisonics output don't use custom decoders. */
971 case DevFmtMono:
972 case DevFmtStereo:
973 case DevFmtAmbi3D:
974 break;
976 if(layout)
978 const char *fname;
979 if(ConfigValueStr(devname, "decoder", layout, &fname))
981 if(!conf.load(fname))
982 ERR("Failed to load layout file %s\n", fname);
983 else
985 if(conf.ChanMask > 0xffff)
986 ERR("Unsupported channel mask 0x%04x (max 0xffff)\n", conf.ChanMask);
987 else
989 if(MakeSpeakerMap(device, &conf, speakermap))
990 pconf = &conf;
996 if(pconf && GetConfigValueBool(devname, "decoder", "hq-mode", 0))
998 device->AmbiUp = nullptr;
999 if(!device->AmbiDecoder)
1000 device->AmbiDecoder.reset(new BFormatDec{});
1002 else
1004 device->AmbiDecoder = nullptr;
1005 if(device->FmtChans != DevFmtAmbi3D || device->mAmbiOrder < 2)
1006 device->AmbiUp = nullptr;
1007 else if(!device->AmbiUp)
1008 device->AmbiUp.reset(new AmbiUpsampler{});
1011 if(!pconf)
1012 InitPanning(device);
1013 else if(device->AmbiDecoder)
1014 InitHQPanning(device, pconf, speakermap);
1015 else
1016 InitCustomPanning(device, pconf, speakermap);
1018 /* Enable the stablizer only for formats that have front-left, front-
1019 * right, and front-center outputs.
1021 switch(device->FmtChans)
1023 case DevFmtX51:
1024 case DevFmtX51Rear:
1025 case DevFmtX61:
1026 case DevFmtX71:
1027 if(GetConfigValueBool(devname, NULL, "front-stablizer", 0))
1029 /* Initialize band-splitting filters for the front-left and
1030 * front-right channels, with a crossover at 5khz (could be
1031 * higher).
1033 ALfloat scale = (ALfloat)(5000.0 / device->Frequency);
1034 std::unique_ptr<FrontStablizer> stablizer{new FrontStablizer{}};
1036 bandsplit_init(&stablizer->LFilter, scale);
1037 stablizer->RFilter = stablizer->LFilter;
1039 /* Initialize all-pass filters for all other channels. */
1040 splitterap_init(&stablizer->APFilter[0], scale);
1041 for(i = 1;i < (size_t)device->RealOut.NumChannels;i++)
1042 stablizer->APFilter[i] = stablizer->APFilter[0];
1044 device->Stablizer = std::move(stablizer);
1046 break;
1047 case DevFmtMono:
1048 case DevFmtStereo:
1049 case DevFmtQuad:
1050 case DevFmtAmbi3D:
1051 break;
1053 TRACE("Front stablizer %s\n", device->Stablizer ? "enabled" : "disabled");
1055 return;
1058 device->AmbiDecoder = nullptr;
1060 headphones = device->IsHeadphones;
1061 if(device->Type != Loopback)
1063 const char *mode;
1064 if(ConfigValueStr(device->DeviceName.c_str(), NULL, "stereo-mode", &mode))
1066 if(strcasecmp(mode, "headphones") == 0)
1067 headphones = true;
1068 else if(strcasecmp(mode, "speakers") == 0)
1069 headphones = false;
1070 else if(strcasecmp(mode, "auto") != 0)
1071 ERR("Unexpected stereo-mode: %s\n", mode);
1075 if(hrtf_userreq == Hrtf_Default)
1077 bool usehrtf = (headphones && hrtf_appreq != Hrtf_Disable) ||
1078 (hrtf_appreq == Hrtf_Enable);
1079 if(!usehrtf) goto no_hrtf;
1081 device->HrtfStatus = ALC_HRTF_ENABLED_SOFT;
1082 if(headphones && hrtf_appreq != Hrtf_Disable)
1083 device->HrtfStatus = ALC_HRTF_HEADPHONES_DETECTED_SOFT;
1085 else
1087 if(hrtf_userreq != Hrtf_Enable)
1089 if(hrtf_appreq == Hrtf_Enable)
1090 device->HrtfStatus = ALC_HRTF_DENIED_SOFT;
1091 goto no_hrtf;
1093 device->HrtfStatus = ALC_HRTF_REQUIRED_SOFT;
1096 if(device->HrtfList.empty())
1097 device->HrtfList = EnumerateHrtf(device->DeviceName.c_str());
1099 if(hrtf_id >= 0 && (size_t)hrtf_id < device->HrtfList.size())
1101 const EnumeratedHrtf &entry = device->HrtfList[hrtf_id];
1102 struct Hrtf *hrtf = GetLoadedHrtf(entry.hrtf);
1103 if(hrtf && hrtf->sampleRate == device->Frequency)
1105 device->HrtfHandle = hrtf;
1106 device->HrtfName = entry.name;
1108 else if(hrtf)
1109 Hrtf_DecRef(hrtf);
1112 for(i = 0;!device->HrtfHandle && i < device->HrtfList.size();i++)
1114 const EnumeratedHrtf &entry = device->HrtfList[i];
1115 struct Hrtf *hrtf = GetLoadedHrtf(entry.hrtf);
1116 if(hrtf && hrtf->sampleRate == device->Frequency)
1118 device->HrtfHandle = hrtf;
1119 device->HrtfName = entry.name;
1121 else if(hrtf)
1122 Hrtf_DecRef(hrtf);
1125 if(device->HrtfHandle)
1127 if(old_hrtf)
1128 Hrtf_DecRef(old_hrtf);
1129 old_hrtf = NULL;
1131 device->Render_Mode = HrtfRender;
1132 if(ConfigValueStr(device->DeviceName.c_str(), NULL, "hrtf-mode", &mode))
1134 if(strcasecmp(mode, "full") == 0)
1135 device->Render_Mode = HrtfRender;
1136 else if(strcasecmp(mode, "basic") == 0)
1137 device->Render_Mode = NormalRender;
1138 else
1139 ERR("Unexpected hrtf-mode: %s\n", mode);
1142 if(device->Render_Mode == HrtfRender)
1144 /* Don't bother with HOA when using full HRTF rendering. Nothing
1145 * needs it, and it eases the CPU/memory load.
1147 device->AmbiUp = nullptr;
1149 else
1151 if(!device->AmbiUp)
1152 device->AmbiUp.reset(new AmbiUpsampler{});
1155 TRACE("%s HRTF rendering enabled, using \"%s\"\n",
1156 ((device->Render_Mode == HrtfRender) ? "Full" : "Basic"), device->HrtfName.c_str()
1158 InitHrtfPanning(device);
1159 return;
1161 device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
1163 no_hrtf:
1164 if(old_hrtf)
1165 Hrtf_DecRef(old_hrtf);
1166 old_hrtf = nullptr;
1167 TRACE("HRTF disabled\n");
1169 device->Render_Mode = StereoPair;
1171 device->AmbiUp = nullptr;
1173 bs2blevel = ((headphones && hrtf_appreq != Hrtf_Disable) ||
1174 (hrtf_appreq == Hrtf_Enable)) ? 5 : 0;
1175 if(device->Type != Loopback)
1176 ConfigValueInt(device->DeviceName.c_str(), NULL, "cf_level", &bs2blevel);
1177 if(bs2blevel > 0 && bs2blevel <= 6)
1179 device->Bs2b.reset(new bs2b{});
1180 bs2b_set_params(device->Bs2b.get(), bs2blevel, device->Frequency);
1181 TRACE("BS2B enabled\n");
1182 InitPanning(device);
1183 return;
1186 TRACE("BS2B disabled\n");
1188 if(ConfigValueStr(device->DeviceName.c_str(), NULL, "stereo-encoding", &mode))
1190 if(strcasecmp(mode, "uhj") == 0)
1191 device->Render_Mode = NormalRender;
1192 else if(strcasecmp(mode, "panpot") != 0)
1193 ERR("Unexpected stereo-encoding: %s\n", mode);
1195 if(device->Render_Mode == NormalRender)
1197 device->Uhj_Encoder.reset(new Uhj2Encoder{});
1198 TRACE("UHJ enabled\n");
1199 InitUhjPanning(device);
1200 return;
1203 TRACE("UHJ disabled\n");
1204 InitPanning(device);
1208 void aluInitEffectPanning(ALeffectslot *slot)
1210 ALsizei i;
1212 memset(slot->ChanMap, 0, sizeof(slot->ChanMap));
1213 slot->NumChannels = 0;
1215 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
1217 slot->ChanMap[i].Scale = 1.0f;
1218 slot->ChanMap[i].Index = i;
1220 slot->NumChannels = i;