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
30 #include "alAuxEffectSlot.h"
34 #include "bformatdec.h"
35 #include "filters/splitter.h"
36 #include "uhjfilter.h"
42 constexpr ALsizei FuMa2ACN
[MAX_AMBI_COEFFS
] = {
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
67 void CalcAmbiCoeffs(const ALfloat y
, const ALfloat z
, const ALfloat x
, const ALfloat spread
,
68 ALfloat coeffs
[MAX_AMBI_COEFFS
])
71 coeffs
[0] = 1.0f
; /* ACN 0 = 1 */
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 */
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) */
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) */
93 /* Implement the spread by using a spherical source that subtends the
95 * http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3
97 * When adjusted for N3D normalization instead of SN3D, these
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:
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
;
127 coeffs
[0] *= ZH0_norm
;
129 coeffs
[1] *= ZH1_norm
;
130 coeffs
[2] *= ZH1_norm
;
131 coeffs
[3] *= ZH1_norm
;
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
;
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
])
154 for(i
= 0;i
< numchans
;i
++)
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
++)
165 void ComputePanningGainsBF(const BFChannelConfig
*chanmap
, ALsizei numchans
, const ALfloat
*RESTRICT coeffs
, ALfloat ingain
, ALfloat gains
[MAX_OUTPUT_CHANNELS
])
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
++)
176 static inline const char *GetLabelFromChannel(enum Channel 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;
224 ChannelConfig Config
;
227 static void SetChannelMap(const Channel (&devchans
)[MAX_OUTPUT_CHANNELS
],
228 ChannelConfig
*ambicoeffs
, const ChannelMap
*chanmap
,
229 ALsizei count
, ALsizei
*outcount
)
232 std::for_each(chanmap
, chanmap
+count
,
233 [&maxchans
,&devchans
,ambicoeffs
](const ChannelMap
&channel
) -> void
235 ALint idx
= GetChannelIndex(devchans
, channel
.ChanName
);
238 ERR("Failed to find %s channel in device\n",
239 GetLabelFromChannel(channel
.ChanName
));
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
])
254 for(i
= 0;i
< conf
->NumSpeakers
;i
++)
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:
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,
279 if(conf
->Speakers
[i
].Name
== "LF")
281 else if(conf
->Speakers
[i
].Name
== "RF")
283 else if(conf
->Speakers
[i
].Name
== "CE")
285 else if(conf
->Speakers
[i
].Name
== "LS")
287 if(device
->FmtChans
== DevFmtX51Rear
)
292 else if(conf
->Speakers
[i
].Name
== "RS")
294 if(device
->FmtChans
== DevFmtX51Rear
)
299 else if(conf
->Speakers
[i
].Name
== "LB")
301 if(device
->FmtChans
== DevFmtX51
)
306 else if(conf
->Speakers
[i
].Name
== "RB")
308 if(device
->FmtChans
== DevFmtX51
)
313 else if(conf
->Speakers
[i
].Name
== "CB")
317 const char *name
= conf
->Speakers
[i
].Name
.c_str();
321 if(sscanf(name
, "AUX%u%c", &n
, &c
) == 1 && n
< 16)
322 ch
= static_cast<enum Channel
>(Aux0
+n
);
325 ERR("AmbDec speaker label \"%s\" not recognized\n", name
);
329 chidx
= GetChannelIdxByName(&device
->RealOut
, ch
);
332 ERR("Failed to lookup AmbDec speaker label %s\n",
333 conf
->Speakers
[i
].Name
.c_str());
336 speakermap
[i
] = chidx
;
343 static const ChannelMap MonoCfg
[1] = {
344 { FrontCenter
, { 1.0f
} },
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
} },
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
} },
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
} },
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
} },
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
} },
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();
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
;
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
];
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
*
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
441 total
+= RoundUp(device
->ChannelDelay
[chan
].Length
, 4);
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;
464 switch(device
->FmtChans
)
467 count
= COUNTOF(MonoCfg
);
473 count
= COUNTOF(StereoCfg
);
479 count
= COUNTOF(QuadCfg
);
485 count
= COUNTOF(X51SideCfg
);
486 chanmap
= X51SideCfg
;
491 count
= COUNTOF(X51RearCfg
);
492 chanmap
= X51RearCfg
;
497 count
= COUNTOF(X61Cfg
);
503 count
= COUNTOF(X71Cfg
);
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;
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
));
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
;
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] = {
573 nfc_delay
= clampf(nfc_delay
, 0.001f
, 1000.0f
);
574 InitNearFieldCtrl(device
, nfc_delay
* SPEEDOFSOUNDMETRESPERSEC
,
575 device
->mAmbiOrder
, chans_per_order
);
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
;
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
;
612 if(conf
->FreqBands
!= 1)
613 ERR("Basic renderer uses the high-frequency matrix as single-band (xover_freq = %.0fhz)\n",
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
;
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
];
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
;
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 };
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
;
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;
737 memset(&device
->FOAOut
.Ambi
, 0, sizeof(device
->FOAOut
.Ambi
));
738 if((conf
->ChanMask
&AMBI_PERIPHONIC_MASK
))
741 for(i
= 0;i
< count
;i
++)
743 device
->FOAOut
.Ambi
.Map
[i
].Scale
= 1.0f
;
744 device
->FOAOut
.Ambi
.Map
[i
].Index
= i
;
749 static const int map
[3] = { 0, 1, 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
);
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
;
849 static_assert(COUNTOF(AmbiPoints
) == COUNTOF(AmbiMatrixFOA
), "FOA Ambisonic HRTF mismatch");
850 static_assert(COUNTOF(AmbiPoints
) == COUNTOF(AmbiMatrixHOA
), "HOA Ambisonic HRTF mismatch");
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
;
872 memset(&device
->FOAOut
.Ambi
, 0, sizeof(device
->FOAOut
.Ambi
));
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]);
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,
902 static void InitUhjPanning(ALCdevice
*device
)
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
;
950 Hrtf_DecRef(old_hrtf
);
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. */
972 if(ConfigValueStr(devname
, "decoder", layout
, &fname
))
974 if(!conf
.load(fname
))
975 ERR("Failed to load layout file %s\n", fname
);
978 if(conf
.ChanMask
> 0xffff)
979 ERR("Unsupported channel mask 0x%04x (max 0xffff)\n", conf
.ChanMask
);
982 if(MakeSpeakerMap(device
, &conf
, speakermap
))
989 if(pconf
&& GetConfigValueBool(devname
, "decoder", "hq-mode", 0))
991 device
->AmbiUp
= nullptr;
992 if(!device
->AmbiDecoder
)
993 device
->AmbiDecoder
.reset(new BFormatDec
{});
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
{});
1005 InitPanning(device
);
1006 else if(device
->AmbiDecoder
)
1007 InitHQPanning(device
, pconf
, speakermap
);
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
)
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
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
);
1046 TRACE("Front stablizer %s\n", device
->Stablizer
? "enabled" : "disabled");
1051 device
->AmbiDecoder
= nullptr;
1053 bool headphones
{device
->IsHeadphones
!= AL_FALSE
};
1054 if(device
->Type
!= Loopback
)
1057 if(ConfigValueStr(device
->DeviceName
.c_str(), NULL
, "stereo-mode", &mode
))
1059 if(strcasecmp(mode
, "headphones") == 0)
1061 else if(strcasecmp(mode
, "speakers") == 0)
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
;
1080 if(hrtf_userreq
!= Hrtf_Enable
)
1082 if(hrtf_appreq
== Hrtf_Enable
)
1083 device
->HrtfStatus
= ALC_HRTF_DENIED_SOFT
;
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
;
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
)
1116 device
->HrtfHandle
= hrtf
;
1117 device
->HrtfName
= entry
.name
;
1120 std::find_if(device
->HrtfList
.cbegin(), device
->HrtfList
.cend(), find_hrtf
);
1123 if(device
->HrtfHandle
)
1126 Hrtf_DecRef(old_hrtf
);
1129 device
->Render_Mode
= HrtfRender
;
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
;
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;
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
);
1160 device
->HrtfStatus
= ALC_HRTF_UNSUPPORTED_FORMAT_SOFT
;
1164 Hrtf_DecRef(old_hrtf
);
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
);
1185 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
);
1203 TRACE("UHJ disabled\n");
1204 InitPanning(device
);
1208 void aluInitEffectPanning(ALeffectslot
*slot
)
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
;