Use member functions for BFormatDec and AmbiUpsampler
[openal-soft.git] / Alc / panning.cpp
blobf970799bebbd78aaa8b1d3874eab950423e920e3
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 struct ChannelMap {
223 Channel ChanName;
224 ChannelConfig Config;
227 static void SetChannelMap(const Channel (&devchans)[MAX_OUTPUT_CHANNELS],
228 ChannelConfig *ambicoeffs, const ChannelMap *chanmap,
229 ALsizei count, ALsizei *outcount)
231 ALsizei maxchans{0};
232 std::for_each(chanmap, chanmap+count,
233 [&maxchans,&devchans,ambicoeffs](const ChannelMap &channel) -> void
235 ALint idx = GetChannelIndex(devchans, channel.ChanName);
236 if(idx < 0)
238 ERR("Failed to find %s channel in device\n",
239 GetLabelFromChannel(channel.ChanName));
240 return;
243 maxchans = maxi(maxchans, idx+1);
244 std::copy_n(channel.Config, MAX_AMBI_COEFFS, ambicoeffs[idx]);
247 *outcount = mini(maxchans, MAX_OUTPUT_CHANNELS);
250 static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALsizei speakermap[MAX_OUTPUT_CHANNELS])
252 ALsizei i;
254 for(i = 0;i < conf->NumSpeakers;i++)
256 enum Channel ch;
257 int chidx = -1;
259 /* NOTE: AmbDec does not define any standard speaker names, however
260 * for this to work we have to by able to find the output channel
261 * the speaker definition corresponds to. Therefore, OpenAL Soft
262 * requires these channel labels to be recognized:
264 * LF = Front left
265 * RF = Front right
266 * LS = Side left
267 * RS = Side right
268 * LB = Back left
269 * RB = Back right
270 * CE = Front center
271 * CB = Back center
273 * Additionally, surround51 will acknowledge back speakers for side
274 * channels, and surround51rear will acknowledge side speakers for
275 * back channels, to avoid issues with an ambdec expecting 5.1 to
276 * use the side channels when the device is configured for back,
277 * and vice-versa.
279 if(conf->Speakers[i].Name == "LF")
280 ch = FrontLeft;
281 else if(conf->Speakers[i].Name == "RF")
282 ch = FrontRight;
283 else if(conf->Speakers[i].Name == "CE")
284 ch = FrontCenter;
285 else if(conf->Speakers[i].Name == "LS")
287 if(device->FmtChans == DevFmtX51Rear)
288 ch = BackLeft;
289 else
290 ch = SideLeft;
292 else if(conf->Speakers[i].Name == "RS")
294 if(device->FmtChans == DevFmtX51Rear)
295 ch = BackRight;
296 else
297 ch = SideRight;
299 else if(conf->Speakers[i].Name == "LB")
301 if(device->FmtChans == DevFmtX51)
302 ch = SideLeft;
303 else
304 ch = BackLeft;
306 else if(conf->Speakers[i].Name == "RB")
308 if(device->FmtChans == DevFmtX51)
309 ch = SideRight;
310 else
311 ch = BackRight;
313 else if(conf->Speakers[i].Name == "CB")
314 ch = BackCenter;
315 else
317 const char *name = conf->Speakers[i].Name.c_str();
318 unsigned int n;
319 char c;
321 if(sscanf(name, "AUX%u%c", &n, &c) == 1 && n < 16)
322 ch = static_cast<enum Channel>(Aux0+n);
323 else
325 ERR("AmbDec speaker label \"%s\" not recognized\n", name);
326 return false;
329 chidx = GetChannelIdxByName(&device->RealOut, ch);
330 if(chidx == -1)
332 ERR("Failed to lookup AmbDec speaker label %s\n",
333 conf->Speakers[i].Name.c_str());
334 return false;
336 speakermap[i] = chidx;
339 return true;
343 static const ChannelMap MonoCfg[1] = {
344 { FrontCenter, { 1.0f } },
345 }, StereoCfg[2] = {
346 { FrontLeft, { 5.00000000e-1f, 2.88675135e-1f, 0.0f, 5.52305643e-2f } },
347 { FrontRight, { 5.00000000e-1f, -2.88675135e-1f, 0.0f, 5.52305643e-2f } },
348 }, QuadCfg[4] = {
349 { BackLeft, { 3.53553391e-1f, 2.04124145e-1f, 0.0f, -2.04124145e-1f } },
350 { FrontLeft, { 3.53553391e-1f, 2.04124145e-1f, 0.0f, 2.04124145e-1f } },
351 { FrontRight, { 3.53553391e-1f, -2.04124145e-1f, 0.0f, 2.04124145e-1f } },
352 { BackRight, { 3.53553391e-1f, -2.04124145e-1f, 0.0f, -2.04124145e-1f } },
353 }, X51SideCfg[4] = {
354 { SideLeft, { 3.33000782e-1f, 1.89084803e-1f, 0.0f, -2.00042375e-1f, -2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } },
355 { FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 0.0f, 1.66295695e-1f, 7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } },
356 { FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 0.0f, 1.66295695e-1f, -7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } },
357 { SideRight, { 3.33000782e-1f, -1.89084803e-1f, 0.0f, -2.00042375e-1f, 2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } },
358 }, X51RearCfg[4] = {
359 { BackLeft, { 3.33000782e-1f, 1.89084803e-1f, 0.0f, -2.00042375e-1f, -2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } },
360 { FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 0.0f, 1.66295695e-1f, 7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } },
361 { FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 0.0f, 1.66295695e-1f, -7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } },
362 { BackRight, { 3.33000782e-1f, -1.89084803e-1f, 0.0f, -2.00042375e-1f, 2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } },
363 }, X61Cfg[6] = {
364 { SideLeft, { 2.04460341e-1f, 2.17177926e-1f, 0.0f, -4.39996780e-2f, -2.60790269e-2f, 0.0f, 0.0f, 0.0f, -6.87239792e-2f } },
365 { FrontLeft, { 1.58923161e-1f, 9.21772680e-2f, 0.0f, 1.59658796e-1f, 6.66278083e-2f, 0.0f, 0.0f, 0.0f, 3.84686854e-2f } },
366 { FrontRight, { 1.58923161e-1f, -9.21772680e-2f, 0.0f, 1.59658796e-1f, -6.66278083e-2f, 0.0f, 0.0f, 0.0f, 3.84686854e-2f } },
367 { SideRight, { 2.04460341e-1f, -2.17177926e-1f, 0.0f, -4.39996780e-2f, 2.60790269e-2f, 0.0f, 0.0f, 0.0f, -6.87239792e-2f } },
368 { BackCenter, { 2.50001688e-1f, 0.00000000e+0f, 0.0f, -2.50000094e-1f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, 6.05133395e-2f } },
369 }, X71Cfg[6] = {
370 { 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 } },
371 { 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 } },
372 { 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 } },
373 { 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 } },
374 { 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 } },
375 { 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 } },
378 static void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei order,
379 const ALsizei *RESTRICT chans_per_order)
381 const char *devname = device->DeviceName.c_str();
382 ALsizei i;
384 if(GetConfigValueBool(devname, "decoder", "nfc", 1) && ctrl_dist > 0.0f)
386 /* NFC is only used when AvgSpeakerDist is greater than 0, and can only
387 * be used when rendering to an ambisonic buffer.
389 device->AvgSpeakerDist = minf(ctrl_dist, 10.0f);
390 TRACE("Using near-field reference distance: %.2f meters\n", device->AvgSpeakerDist);
392 for(i = 0;i < order+1;i++)
393 device->NumChannelsPerOrder[i] = chans_per_order[i];
394 for(;i < MAX_AMBI_ORDER+1;i++)
395 device->NumChannelsPerOrder[i] = 0;
399 static void InitDistanceComp(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS])
401 const char *devname = device->DeviceName.c_str();
402 ALfloat maxdist = 0.0f;
403 size_t total = 0;
404 ALsizei i;
406 for(i = 0;i < conf->NumSpeakers;i++)
407 maxdist = maxf(maxdist, conf->Speakers[i].Distance);
409 if(GetConfigValueBool(devname, "decoder", "distance-comp", 1) && maxdist > 0.0f)
411 ALfloat srate = (ALfloat)device->Frequency;
412 for(i = 0;i < conf->NumSpeakers;i++)
414 ALsizei chan = speakermap[i];
415 ALfloat delay;
417 /* Distance compensation only delays in steps of the sample rate.
418 * This is a bit less accurate since the delay time falls to the
419 * nearest sample time, but it's far simpler as it doesn't have to
420 * deal with phase offsets. This means at 48khz, for instance, the
421 * distance delay will be in steps of about 7 millimeters.
423 delay = floorf((maxdist-conf->Speakers[i].Distance) / SPEEDOFSOUNDMETRESPERSEC *
424 srate + 0.5f);
425 if(delay >= (ALfloat)MAX_DELAY_LENGTH)
426 ERR("Delay for speaker \"%s\" exceeds buffer length (%f >= %u)\n",
427 conf->Speakers[i].Name.c_str(), delay, MAX_DELAY_LENGTH);
429 device->ChannelDelay[chan].Length = (ALsizei)clampf(
430 delay, 0.0f, (ALfloat)(MAX_DELAY_LENGTH-1)
432 device->ChannelDelay[chan].Gain = conf->Speakers[i].Distance / maxdist;
433 TRACE("Channel %u \"%s\" distance compensation: %d samples, %f gain\n", chan,
434 conf->Speakers[i].Name.c_str(), device->ChannelDelay[chan].Length,
435 device->ChannelDelay[chan].Gain
438 /* Round up to the next 4th sample, so each channel buffer starts
439 * 16-byte aligned.
441 total += RoundUp(device->ChannelDelay[chan].Length, 4);
445 if(total > 0)
447 device->ChannelDelay.resize(total);
448 device->ChannelDelay[0].Buffer = device->ChannelDelay.data();
449 for(i = 1;i < MAX_OUTPUT_CHANNELS;i++)
451 size_t len = RoundUp(device->ChannelDelay[i-1].Length, 4);
452 device->ChannelDelay[i].Buffer = device->ChannelDelay[i-1].Buffer + len;
457 static void InitPanning(ALCdevice *device)
459 const ChannelMap *chanmap = NULL;
460 ALsizei coeffcount = 0;
461 ALsizei count = 0;
462 ALsizei i, j;
464 switch(device->FmtChans)
466 case DevFmtMono:
467 count = COUNTOF(MonoCfg);
468 chanmap = MonoCfg;
469 coeffcount = 1;
470 break;
472 case DevFmtStereo:
473 count = COUNTOF(StereoCfg);
474 chanmap = StereoCfg;
475 coeffcount = 4;
476 break;
478 case DevFmtQuad:
479 count = COUNTOF(QuadCfg);
480 chanmap = QuadCfg;
481 coeffcount = 4;
482 break;
484 case DevFmtX51:
485 count = COUNTOF(X51SideCfg);
486 chanmap = X51SideCfg;
487 coeffcount = 9;
488 break;
490 case DevFmtX51Rear:
491 count = COUNTOF(X51RearCfg);
492 chanmap = X51RearCfg;
493 coeffcount = 9;
494 break;
496 case DevFmtX61:
497 count = COUNTOF(X61Cfg);
498 chanmap = X61Cfg;
499 coeffcount = 9;
500 break;
502 case DevFmtX71:
503 count = COUNTOF(X71Cfg);
504 chanmap = X71Cfg;
505 coeffcount = 16;
506 break;
508 case DevFmtAmbi3D:
509 break;
512 if(device->FmtChans == DevFmtAmbi3D)
514 const char *devname = device->DeviceName.c_str();
515 const ALsizei *acnmap = (device->mAmbiLayout == AmbiLayout::FuMa) ? FuMa2ACN : ACN2ACN;
516 const ALfloat *n3dscale = (device->mAmbiScale == AmbiNorm::FuMa) ? FuMa2N3DScale :
517 (device->mAmbiScale == AmbiNorm::SN3D) ? SN3D2N3DScale :
518 /*(device->mAmbiScale == AmbiNorm::N3D) ?*/ N3D2N3DScale;
519 ALfloat nfc_delay = 0.0f;
521 count = (device->mAmbiOrder == 3) ? 16 :
522 (device->mAmbiOrder == 2) ? 9 :
523 (device->mAmbiOrder == 1) ? 4 : 1;
524 for(i = 0;i < count;i++)
526 ALsizei acn = acnmap[i];
527 device->Dry.Ambi.Map[i].Scale = 1.0f/n3dscale[acn];
528 device->Dry.Ambi.Map[i].Index = acn;
530 device->Dry.CoeffCount = 0;
531 device->Dry.NumChannels = count;
533 if(device->mAmbiOrder < 2)
535 device->FOAOut.Ambi = device->Dry.Ambi;
536 device->FOAOut.CoeffCount = device->Dry.CoeffCount;
537 device->FOAOut.NumChannels = 0;
539 else
541 ALfloat w_scale=1.0f, xyz_scale=1.0f;
543 /* FOA output is always ACN+N3D for higher-order ambisonic output.
544 * The upsampler expects this and will convert it for output.
546 memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
547 for(i = 0;i < 4;i++)
549 device->FOAOut.Ambi.Map[i].Scale = 1.0f;
550 device->FOAOut.Ambi.Map[i].Index = i;
552 device->FOAOut.CoeffCount = 0;
553 device->FOAOut.NumChannels = 4;
555 if(device->mAmbiOrder >= 3)
557 w_scale = W_SCALE_3H3P;
558 xyz_scale = XYZ_SCALE_3H3P;
560 else
562 w_scale = W_SCALE_2H2P;
563 xyz_scale = XYZ_SCALE_2H2P;
565 device->AmbiUp->reset(device, w_scale, xyz_scale);
568 if(ConfigValueFloat(devname, "decoder", "nfc-ref-delay", &nfc_delay) && nfc_delay > 0.0f)
570 static const ALsizei chans_per_order[MAX_AMBI_ORDER+1] = {
571 1, 3, 5, 7
573 nfc_delay = clampf(nfc_delay, 0.001f, 1000.0f);
574 InitNearFieldCtrl(device, nfc_delay * SPEEDOFSOUNDMETRESPERSEC,
575 device->mAmbiOrder, chans_per_order);
578 else
580 ALfloat w_scale, xyz_scale;
582 SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs,
583 chanmap, count, &device->Dry.NumChannels);
584 device->Dry.CoeffCount = coeffcount;
586 w_scale = (device->Dry.CoeffCount > 9) ? W_SCALE_3H0P :
587 (device->Dry.CoeffCount > 4) ? W_SCALE_2H0P : 1.0f;
588 xyz_scale = (device->Dry.CoeffCount > 9) ? XYZ_SCALE_3H0P :
589 (device->Dry.CoeffCount > 4) ? XYZ_SCALE_2H0P : 1.0f;
591 memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
592 for(i = 0;i < device->Dry.NumChannels;i++)
594 device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0] * w_scale;
595 for(j = 1;j < 4;j++)
596 device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * xyz_scale;
598 device->FOAOut.CoeffCount = 4;
599 device->FOAOut.NumChannels = 0;
601 device->RealOut.NumChannels = 0;
604 static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei (&speakermap)[MAX_OUTPUT_CHANNELS])
606 ChannelMap chanmap[MAX_OUTPUT_CHANNELS];
607 const ALfloat *coeff_scale = N3D2N3DScale;
608 ALfloat w_scale = 1.0f;
609 ALfloat xyz_scale = 1.0f;
610 ALsizei i, j;
612 if(conf->FreqBands != 1)
613 ERR("Basic renderer uses the high-frequency matrix as single-band (xover_freq = %.0fhz)\n",
614 conf->XOverFreq);
616 if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
618 if(conf->ChanMask > 0x1ff)
620 w_scale = W_SCALE_3H3P;
621 xyz_scale = XYZ_SCALE_3H3P;
623 else if(conf->ChanMask > 0xf)
625 w_scale = W_SCALE_2H2P;
626 xyz_scale = XYZ_SCALE_2H2P;
629 else
631 if(conf->ChanMask > 0x1ff)
633 w_scale = W_SCALE_3H0P;
634 xyz_scale = XYZ_SCALE_3H0P;
636 else if(conf->ChanMask > 0xf)
638 w_scale = W_SCALE_2H0P;
639 xyz_scale = XYZ_SCALE_2H0P;
643 if(conf->CoeffScale == AmbDecScale::SN3D)
644 coeff_scale = SN3D2N3DScale;
645 else if(conf->CoeffScale == AmbDecScale::FuMa)
646 coeff_scale = FuMa2N3DScale;
648 for(i = 0;i < conf->NumSpeakers;i++)
650 ALsizei chan = speakermap[i];
651 ALfloat gain;
652 ALsizei k = 0;
654 for(j = 0;j < MAX_AMBI_COEFFS;j++)
655 chanmap[i].Config[j] = 0.0f;
657 chanmap[i].ChanName = device->RealOut.ChannelName[chan];
658 for(j = 0;j < MAX_AMBI_COEFFS;j++)
660 if(j == 0) gain = conf->HFOrderGain[0];
661 else if(j == 1) gain = conf->HFOrderGain[1];
662 else if(j == 4) gain = conf->HFOrderGain[2];
663 else if(j == 9) gain = conf->HFOrderGain[3];
664 if((conf->ChanMask&(1<<j)))
665 chanmap[i].Config[j] = conf->HFMatrix[i][k++] / coeff_scale[j] * gain;
669 SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs, chanmap,
670 conf->NumSpeakers, &device->Dry.NumChannels);
671 device->Dry.CoeffCount = (conf->ChanMask > 0x1ff) ? 16 :
672 (conf->ChanMask > 0xf) ? 9 : 4;
674 memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
675 for(i = 0;i < device->Dry.NumChannels;i++)
677 device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0] * w_scale;
678 for(j = 1;j < 4;j++)
679 device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * xyz_scale;
681 device->FOAOut.CoeffCount = 4;
682 device->FOAOut.NumChannels = 0;
684 device->RealOut.NumChannels = 0;
686 InitDistanceComp(device, conf, speakermap);
689 static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei (&speakermap)[MAX_OUTPUT_CHANNELS])
691 static constexpr ALsizei chans_per_order2d[MAX_AMBI_ORDER+1] = { 1, 2, 2, 2 };
692 static constexpr ALsizei chans_per_order3d[MAX_AMBI_ORDER+1] = { 1, 3, 5, 7 };
693 ALfloat avg_dist;
694 ALsizei count;
695 ALsizei i;
697 if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
699 count = (conf->ChanMask > 0x1ff) ? 16 :
700 (conf->ChanMask > 0xf) ? 9 : 4;
701 for(i = 0;i < count;i++)
703 device->Dry.Ambi.Map[i].Scale = 1.0f;
704 device->Dry.Ambi.Map[i].Index = i;
707 else
709 static const int map[MAX_AMBI2D_COEFFS] = { 0, 1, 3, 4, 8, 9, 15 };
711 count = (conf->ChanMask > 0x1ff) ? 7 :
712 (conf->ChanMask > 0xf) ? 5 : 3;
713 for(i = 0;i < count;i++)
715 device->Dry.Ambi.Map[i].Scale = 1.0f;
716 device->Dry.Ambi.Map[i].Index = map[i];
719 device->Dry.CoeffCount = 0;
720 device->Dry.NumChannels = count;
722 TRACE("Enabling %s-band %s-order%s ambisonic decoder\n",
723 (conf->FreqBands == 1) ? "single" : "dual",
724 (conf->ChanMask > 0xf) ? (conf->ChanMask > 0x1ff) ? "third" : "second" : "first",
725 (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? " periphonic" : ""
727 device->AmbiDecoder->reset(conf, count, device->Frequency, speakermap);
729 if(conf->ChanMask <= 0xf)
731 device->FOAOut.Ambi = device->Dry.Ambi;
732 device->FOAOut.CoeffCount = device->Dry.CoeffCount;
733 device->FOAOut.NumChannels = 0;
735 else
737 memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
738 if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
740 count = 4;
741 for(i = 0;i < count;i++)
743 device->FOAOut.Ambi.Map[i].Scale = 1.0f;
744 device->FOAOut.Ambi.Map[i].Index = i;
747 else
749 static const int map[3] = { 0, 1, 3 };
750 count = 3;
751 for(i = 0;i < count;i++)
753 device->FOAOut.Ambi.Map[i].Scale = 1.0f;
754 device->FOAOut.Ambi.Map[i].Index = map[i];
757 device->FOAOut.CoeffCount = 0;
758 device->FOAOut.NumChannels = count;
761 device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->mAmbiOrder);
763 avg_dist = 0.0f;
764 for(i = 0;i < conf->NumSpeakers;i++)
765 avg_dist += conf->Speakers[i].Distance;
766 avg_dist /= (ALfloat)conf->NumSpeakers;
767 InitNearFieldCtrl(device, avg_dist,
768 (conf->ChanMask > 0x1ff) ? 3 : (conf->ChanMask > 0xf) ? 2 : 1,
769 (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? chans_per_order3d : chans_per_order2d
772 InitDistanceComp(device, conf, speakermap);
775 static void InitHrtfPanning(ALCdevice *device)
777 /* NOTE: azimuth goes clockwise. */
778 static const struct AngularPoint AmbiPoints[] = {
779 { DEG2RAD( 90.0f), DEG2RAD( 0.0f) },
780 { DEG2RAD( 35.2643897f), DEG2RAD( 45.0f) },
781 { DEG2RAD( 35.2643897f), DEG2RAD( 135.0f) },
782 { DEG2RAD( 35.2643897f), DEG2RAD(-135.0f) },
783 { DEG2RAD( 35.2643897f), DEG2RAD( -45.0f) },
784 { DEG2RAD( 0.0f), DEG2RAD( 0.0f) },
785 { DEG2RAD( 0.0f), DEG2RAD( 45.0f) },
786 { DEG2RAD( 0.0f), DEG2RAD( 90.0f) },
787 { DEG2RAD( 0.0f), DEG2RAD( 135.0f) },
788 { DEG2RAD( 0.0f), DEG2RAD( 180.0f) },
789 { DEG2RAD( 0.0f), DEG2RAD(-135.0f) },
790 { DEG2RAD( 0.0f), DEG2RAD( -90.0f) },
791 { DEG2RAD( 0.0f), DEG2RAD( -45.0f) },
792 { DEG2RAD(-35.2643897f), DEG2RAD( 45.0f) },
793 { DEG2RAD(-35.2643897f), DEG2RAD( 135.0f) },
794 { DEG2RAD(-35.2643897f), DEG2RAD(-135.0f) },
795 { DEG2RAD(-35.2643897f), DEG2RAD( -45.0f) },
796 { DEG2RAD(-90.0f), DEG2RAD( 0.0f) },
798 static const ALfloat AmbiMatrixFOA[][MAX_AMBI_COEFFS] = {
799 { 5.55555556e-02f, 0.00000000e+00f, 1.23717915e-01f, 0.00000000e+00f },
800 { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f },
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, 0.00000000e+00f, 0.00000000e+00f, 8.66025404e-02f },
805 { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f },
806 { 5.55555556e-02f, -8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f },
807 { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f },
808 { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, -8.66025404e-02f },
809 { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f },
810 { 5.55555556e-02f, 8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f },
811 { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f },
812 { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, 5.00000000e-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, 0.00000000e+00f, -1.23717915e-01f, 0.00000000e+00f },
817 }, AmbiMatrixHOA[][MAX_AMBI_COEFFS] = {
818 { 5.55555556e-02f, 0.00000000e+00f, 1.23717915e-01f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f },
819 { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f, -4.55645099e-02f, 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, 0.00000000e+00f, 0.00000000e+00f, 8.66025404e-02f, 0.00000000e+00f, 1.29099445e-01f },
824 { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f, -6.83467648e-02f, 0.00000000e+00f },
825 { 5.55555556e-02f, -8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -1.29099445e-01f },
826 { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f, 6.83467648e-02f, 0.00000000e+00f },
827 { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, -8.66025404e-02f, 0.00000000e+00f, 1.29099445e-01f },
828 { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f, -6.83467648e-02f, 0.00000000e+00f },
829 { 5.55555556e-02f, 8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -1.29099445e-01f },
830 { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f, 6.83467648e-02f, 0.00000000e+00f },
831 { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f, -4.55645099e-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, 0.00000000e+00f, -1.23717915e-01f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f },
837 static const ALfloat AmbiOrderHFGainFOA[MAX_AMBI_ORDER+1] = {
838 3.00000000e+00f, 1.73205081e+00f
839 }, AmbiOrderHFGainHOA[MAX_AMBI_ORDER+1] = {
840 2.40192231e+00f, 1.86052102e+00f, 9.60768923e-01f
842 static const ALsizei IndexMap[6] = { 0, 1, 2, 3, 4, 8 };
843 static const ALsizei ChansPerOrder[MAX_AMBI_ORDER+1] = { 1, 3, 2, 0 };
844 const ALfloat (*RESTRICT AmbiMatrix)[MAX_AMBI_COEFFS] = AmbiMatrixFOA;
845 const ALfloat *RESTRICT AmbiOrderHFGain = AmbiOrderHFGainFOA;
846 ALsizei count = 4;
847 ALsizei i;
849 static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrixFOA), "FOA Ambisonic HRTF mismatch");
850 static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrixHOA), "HOA Ambisonic HRTF mismatch");
852 if(device->AmbiUp)
854 AmbiMatrix = AmbiMatrixHOA;
855 AmbiOrderHFGain = AmbiOrderHFGainHOA;
856 count = COUNTOF(IndexMap);
859 device->mHrtfState.reset(
860 new (al_calloc(16, FAM_SIZE(DirectHrtfState, Chan, count))) DirectHrtfState{});
862 for(i = 0;i < count;i++)
864 device->Dry.Ambi.Map[i].Scale = 1.0f;
865 device->Dry.Ambi.Map[i].Index = IndexMap[i];
867 device->Dry.CoeffCount = 0;
868 device->Dry.NumChannels = count;
870 if(device->AmbiUp)
872 memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
873 for(i = 0;i < 4;i++)
875 device->FOAOut.Ambi.Map[i].Scale = 1.0f;
876 device->FOAOut.Ambi.Map[i].Index = i;
878 device->FOAOut.CoeffCount = 0;
879 device->FOAOut.NumChannels = 4;
881 device->AmbiUp->reset(device, AmbiOrderHFGainFOA[0] / AmbiOrderHFGain[0],
882 AmbiOrderHFGainFOA[1] / AmbiOrderHFGain[1]);
884 else
886 device->FOAOut.Ambi = device->Dry.Ambi;
887 device->FOAOut.CoeffCount = device->Dry.CoeffCount;
888 device->FOAOut.NumChannels = 0;
891 device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->mAmbiOrder);
893 BuildBFormatHrtf(device->HrtfHandle,
894 device->mHrtfState.get(), device->Dry.NumChannels, AmbiPoints, AmbiMatrix,
895 COUNTOF(AmbiPoints), AmbiOrderHFGain
898 InitNearFieldCtrl(device, device->HrtfHandle->distance, device->AmbiUp ? 2 : 1,
899 ChansPerOrder);
902 static void InitUhjPanning(ALCdevice *device)
904 ALsizei count = 3;
905 ALsizei i;
907 for(i = 0;i < count;i++)
909 ALsizei acn = FuMa2ACN[i];
910 device->Dry.Ambi.Map[i].Scale = 1.0f/FuMa2N3DScale[acn];
911 device->Dry.Ambi.Map[i].Index = acn;
913 device->Dry.CoeffCount = 0;
914 device->Dry.NumChannels = count;
916 device->FOAOut.Ambi = device->Dry.Ambi;
917 device->FOAOut.CoeffCount = device->Dry.CoeffCount;
918 device->FOAOut.NumChannels = 0;
920 device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->mAmbiOrder);
923 void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf_appreq, enum HrtfRequestMode hrtf_userreq)
925 /* Hold the HRTF the device last used, in case it's used again. */
926 struct Hrtf *old_hrtf = device->HrtfHandle;
928 device->mHrtfState = nullptr;
929 device->HrtfHandle = nullptr;
930 device->HrtfName.clear();
931 device->Render_Mode = NormalRender;
933 device->Dry.Ambi = AmbiConfig{};
934 device->Dry.CoeffCount = 0;
935 device->Dry.NumChannels = 0;
936 std::fill(std::begin(device->NumChannelsPerOrder), std::end(device->NumChannelsPerOrder), 0);
938 device->AvgSpeakerDist = 0.0f;
939 device->ChannelDelay.clear();
941 device->Stablizer = nullptr;
943 if(device->FmtChans != DevFmtStereo)
945 ALsizei speakermap[MAX_OUTPUT_CHANNELS];
946 const char *devname, *layout = NULL;
947 AmbDecConf conf, *pconf = NULL;
949 if(old_hrtf)
950 Hrtf_DecRef(old_hrtf);
951 old_hrtf = NULL;
952 if(hrtf_appreq == Hrtf_Enable)
953 device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
955 devname = device->DeviceName.c_str();
956 switch(device->FmtChans)
958 case DevFmtQuad: layout = "quad"; break;
959 case DevFmtX51: /* fall-through */
960 case DevFmtX51Rear: layout = "surround51"; break;
961 case DevFmtX61: layout = "surround61"; break;
962 case DevFmtX71: layout = "surround71"; break;
963 /* Mono, Stereo, and Ambisonics output don't use custom decoders. */
964 case DevFmtMono:
965 case DevFmtStereo:
966 case DevFmtAmbi3D:
967 break;
969 if(layout)
971 const char *fname;
972 if(ConfigValueStr(devname, "decoder", layout, &fname))
974 if(!conf.load(fname))
975 ERR("Failed to load layout file %s\n", fname);
976 else
978 if(conf.ChanMask > 0xffff)
979 ERR("Unsupported channel mask 0x%04x (max 0xffff)\n", conf.ChanMask);
980 else
982 if(MakeSpeakerMap(device, &conf, speakermap))
983 pconf = &conf;
989 if(pconf && GetConfigValueBool(devname, "decoder", "hq-mode", 0))
991 device->AmbiUp = nullptr;
992 if(!device->AmbiDecoder)
993 device->AmbiDecoder.reset(new BFormatDec{});
995 else
997 device->AmbiDecoder = nullptr;
998 if(device->FmtChans != DevFmtAmbi3D || device->mAmbiOrder < 2)
999 device->AmbiUp = nullptr;
1000 else if(!device->AmbiUp)
1001 device->AmbiUp.reset(new AmbiUpsampler{});
1004 if(!pconf)
1005 InitPanning(device);
1006 else if(device->AmbiDecoder)
1007 InitHQPanning(device, pconf, speakermap);
1008 else
1009 InitCustomPanning(device, pconf, speakermap);
1011 /* Enable the stablizer only for formats that have front-left, front-
1012 * right, and front-center outputs.
1014 switch(device->FmtChans)
1016 case DevFmtX51:
1017 case DevFmtX51Rear:
1018 case DevFmtX61:
1019 case DevFmtX71:
1020 if(GetConfigValueBool(devname, NULL, "front-stablizer", 0))
1022 /* Initialize band-splitting filters for the front-left and
1023 * front-right channels, with a crossover at 5khz (could be
1024 * higher).
1026 ALfloat scale = (ALfloat)(5000.0 / device->Frequency);
1027 std::unique_ptr<FrontStablizer> stablizer{new FrontStablizer{}};
1029 stablizer->LFilter.init(scale);
1030 stablizer->RFilter = stablizer->LFilter;
1032 /* Initialize all-pass filters for all other channels. */
1033 stablizer->APFilter[0].init(scale);
1034 std::fill(std::begin(stablizer->APFilter)+1, std::end(stablizer->APFilter),
1035 stablizer->APFilter[0]);
1037 device->Stablizer = std::move(stablizer);
1039 break;
1040 case DevFmtMono:
1041 case DevFmtStereo:
1042 case DevFmtQuad:
1043 case DevFmtAmbi3D:
1044 break;
1046 TRACE("Front stablizer %s\n", device->Stablizer ? "enabled" : "disabled");
1048 return;
1051 device->AmbiDecoder = nullptr;
1053 bool headphones{device->IsHeadphones != AL_FALSE};
1054 if(device->Type != Loopback)
1056 const char *mode;
1057 if(ConfigValueStr(device->DeviceName.c_str(), NULL, "stereo-mode", &mode))
1059 if(strcasecmp(mode, "headphones") == 0)
1060 headphones = true;
1061 else if(strcasecmp(mode, "speakers") == 0)
1062 headphones = false;
1063 else if(strcasecmp(mode, "auto") != 0)
1064 ERR("Unexpected stereo-mode: %s\n", mode);
1068 if(hrtf_userreq == Hrtf_Default)
1070 bool usehrtf = (headphones && hrtf_appreq != Hrtf_Disable) ||
1071 (hrtf_appreq == Hrtf_Enable);
1072 if(!usehrtf) goto no_hrtf;
1074 device->HrtfStatus = ALC_HRTF_ENABLED_SOFT;
1075 if(headphones && hrtf_appreq != Hrtf_Disable)
1076 device->HrtfStatus = ALC_HRTF_HEADPHONES_DETECTED_SOFT;
1078 else
1080 if(hrtf_userreq != Hrtf_Enable)
1082 if(hrtf_appreq == Hrtf_Enable)
1083 device->HrtfStatus = ALC_HRTF_DENIED_SOFT;
1084 goto no_hrtf;
1086 device->HrtfStatus = ALC_HRTF_REQUIRED_SOFT;
1089 if(device->HrtfList.empty())
1090 device->HrtfList = EnumerateHrtf(device->DeviceName.c_str());
1092 if(hrtf_id >= 0 && (size_t)hrtf_id < device->HrtfList.size())
1094 const EnumeratedHrtf &entry = device->HrtfList[hrtf_id];
1095 struct Hrtf *hrtf = GetLoadedHrtf(entry.hrtf);
1096 if(hrtf && hrtf->sampleRate == device->Frequency)
1098 device->HrtfHandle = hrtf;
1099 device->HrtfName = entry.name;
1101 else if(hrtf)
1102 Hrtf_DecRef(hrtf);
1105 if(!device->HrtfHandle)
1107 auto find_hrtf = [device](const EnumeratedHrtf &entry) -> bool
1109 struct Hrtf *hrtf = GetLoadedHrtf(entry.hrtf);
1110 if(!hrtf) return false;
1111 if(hrtf->sampleRate != device->Frequency)
1113 Hrtf_DecRef(hrtf);
1114 return false;
1116 device->HrtfHandle = hrtf;
1117 device->HrtfName = entry.name;
1118 return true;
1120 std::find_if(device->HrtfList.cbegin(), device->HrtfList.cend(), find_hrtf);
1123 if(device->HrtfHandle)
1125 if(old_hrtf)
1126 Hrtf_DecRef(old_hrtf);
1127 old_hrtf = NULL;
1129 device->Render_Mode = HrtfRender;
1130 const char *mode;
1131 if(ConfigValueStr(device->DeviceName.c_str(), NULL, "hrtf-mode", &mode))
1133 if(strcasecmp(mode, "full") == 0)
1134 device->Render_Mode = HrtfRender;
1135 else if(strcasecmp(mode, "basic") == 0)
1136 device->Render_Mode = NormalRender;
1137 else
1138 ERR("Unexpected hrtf-mode: %s\n", mode);
1141 if(device->Render_Mode == HrtfRender)
1143 /* Don't bother with HOA when using full HRTF rendering. Nothing
1144 * needs it, and it eases the CPU/memory load.
1146 device->AmbiUp = nullptr;
1148 else
1150 if(!device->AmbiUp)
1151 device->AmbiUp.reset(new AmbiUpsampler{});
1154 TRACE("%s HRTF rendering enabled, using \"%s\"\n",
1155 ((device->Render_Mode == HrtfRender) ? "Full" : "Basic"), device->HrtfName.c_str()
1157 InitHrtfPanning(device);
1158 return;
1160 device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
1162 no_hrtf:
1163 if(old_hrtf)
1164 Hrtf_DecRef(old_hrtf);
1165 old_hrtf = nullptr;
1166 TRACE("HRTF disabled\n");
1168 device->Render_Mode = StereoPair;
1170 device->AmbiUp = nullptr;
1172 int bs2blevel{((headphones && hrtf_appreq != Hrtf_Disable) ||
1173 (hrtf_appreq == Hrtf_Enable)) ? 5 : 0};
1174 if(device->Type != Loopback)
1175 ConfigValueInt(device->DeviceName.c_str(), NULL, "cf_level", &bs2blevel);
1176 if(bs2blevel > 0 && bs2blevel <= 6)
1178 device->Bs2b.reset(new bs2b{});
1179 bs2b_set_params(device->Bs2b.get(), bs2blevel, device->Frequency);
1180 TRACE("BS2B enabled\n");
1181 InitPanning(device);
1182 return;
1185 TRACE("BS2B disabled\n");
1187 const char *mode;
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;