Use c++ headers
[openal-soft.git] / Alc / panning.cpp
blob9c92a166724a3d4ff87f96a9951f46f0f3b2598a
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 <cmath>
24 #include <cstdlib>
25 #include <cstring>
26 #include <cctype>
27 #include <cassert>
29 #include <cmath>
30 #include <numeric>
31 #include <algorithm>
32 #include <functional>
34 #include "alMain.h"
35 #include "alAuxEffectSlot.h"
36 #include "alu.h"
37 #include "alconfig.h"
38 #include "ambdec.h"
39 #include "bformatdec.h"
40 #include "filters/splitter.h"
41 #include "uhjfilter.h"
42 #include "bs2b.h"
45 constexpr std::array<float,MAX_AMBI_COEFFS> AmbiScale::FromN3D;
46 constexpr std::array<float,MAX_AMBI_COEFFS> AmbiScale::FromSN3D;
47 constexpr std::array<float,MAX_AMBI_COEFFS> AmbiScale::FromFuMa;
48 constexpr std::array<int,MAX_AMBI_COEFFS> AmbiIndex::FromFuMa;
49 constexpr std::array<int,MAX_AMBI_COEFFS> AmbiIndex::FromACN;
50 constexpr std::array<int,MAX_AMBI2D_COEFFS> AmbiIndex::From2D;
51 constexpr std::array<int,MAX_AMBI_COEFFS> AmbiIndex::From3D;
54 namespace {
56 inline const char *GetLabelFromChannel(Channel channel)
58 switch(channel)
60 case FrontLeft: return "front-left";
61 case FrontRight: return "front-right";
62 case FrontCenter: return "front-center";
63 case LFE: return "lfe";
64 case BackLeft: return "back-left";
65 case BackRight: return "back-right";
66 case BackCenter: return "back-center";
67 case SideLeft: return "side-left";
68 case SideRight: return "side-right";
70 case UpperFrontLeft: return "upper-front-left";
71 case UpperFrontRight: return "upper-front-right";
72 case UpperBackLeft: return "upper-back-left";
73 case UpperBackRight: return "upper-back-right";
74 case LowerFrontLeft: return "lower-front-left";
75 case LowerFrontRight: return "lower-front-right";
76 case LowerBackLeft: return "lower-back-left";
77 case LowerBackRight: return "lower-back-right";
79 case Aux0: return "aux-0";
80 case Aux1: return "aux-1";
81 case Aux2: return "aux-2";
82 case Aux3: return "aux-3";
83 case Aux4: return "aux-4";
84 case Aux5: return "aux-5";
85 case Aux6: return "aux-6";
86 case Aux7: return "aux-7";
87 case Aux8: return "aux-8";
88 case Aux9: return "aux-9";
89 case Aux10: return "aux-10";
90 case Aux11: return "aux-11";
91 case Aux12: return "aux-12";
92 case Aux13: return "aux-13";
93 case Aux14: return "aux-14";
94 case Aux15: return "aux-15";
96 case InvalidChannel: break;
98 return "(unknown)";
102 struct ChannelMap {
103 Channel ChanName;
104 ALfloat Config[MAX_AMBI2D_COEFFS];
107 bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALsizei (&speakermap)[MAX_OUTPUT_CHANNELS])
109 auto map_spkr = [device](const AmbDecConf::SpeakerConf &speaker) -> ALsizei
111 /* NOTE: AmbDec does not define any standard speaker names, however
112 * for this to work we have to by able to find the output channel
113 * the speaker definition corresponds to. Therefore, OpenAL Soft
114 * requires these channel labels to be recognized:
116 * LF = Front left
117 * RF = Front right
118 * LS = Side left
119 * RS = Side right
120 * LB = Back left
121 * RB = Back right
122 * CE = Front center
123 * CB = Back center
125 * Additionally, surround51 will acknowledge back speakers for side
126 * channels, and surround51rear will acknowledge side speakers for
127 * back channels, to avoid issues with an ambdec expecting 5.1 to
128 * use the side channels when the device is configured for back,
129 * and vice-versa.
131 Channel ch{};
132 if(speaker.Name == "LF")
133 ch = FrontLeft;
134 else if(speaker.Name == "RF")
135 ch = FrontRight;
136 else if(speaker.Name == "CE")
137 ch = FrontCenter;
138 else if(speaker.Name == "LS")
140 if(device->FmtChans == DevFmtX51Rear)
141 ch = BackLeft;
142 else
143 ch = SideLeft;
145 else if(speaker.Name == "RS")
147 if(device->FmtChans == DevFmtX51Rear)
148 ch = BackRight;
149 else
150 ch = SideRight;
152 else if(speaker.Name == "LB")
154 if(device->FmtChans == DevFmtX51)
155 ch = SideLeft;
156 else
157 ch = BackLeft;
159 else if(speaker.Name == "RB")
161 if(device->FmtChans == DevFmtX51)
162 ch = SideRight;
163 else
164 ch = BackRight;
166 else if(speaker.Name == "CB")
167 ch = BackCenter;
168 else
170 const char *name{speaker.Name.c_str()};
171 unsigned int n;
172 char c;
174 if(sscanf(name, "AUX%u%c", &n, &c) == 1 && n < 16)
175 ch = static_cast<Channel>(Aux0+n);
176 else
178 ERR("AmbDec speaker label \"%s\" not recognized\n", name);
179 return -1;
182 const int chidx{GetChannelIdxByName(device->RealOut, ch)};
183 if(chidx == -1)
184 ERR("Failed to lookup AmbDec speaker label %s\n", speaker.Name.c_str());
185 return chidx;
187 std::transform(conf->Speakers.begin(), conf->Speakers.end(), std::begin(speakermap), map_spkr);
188 /* Return success if no invalid entries are found. */
189 auto speakermap_end = std::begin(speakermap) + conf->Speakers.size();
190 return std::find(std::begin(speakermap), speakermap_end, -1) == speakermap_end;
194 constexpr ChannelMap MonoCfg[1] = {
195 { FrontCenter, { 1.0f } },
196 }, StereoCfg[2] = {
197 { FrontLeft, { 5.00000000e-1f, 2.88675135e-1f, 5.52305643e-2f } },
198 { FrontRight, { 5.00000000e-1f, -2.88675135e-1f, 5.52305643e-2f } },
199 }, QuadCfg[4] = {
200 { BackLeft, { 3.53553391e-1f, 2.04124145e-1f, -2.04124145e-1f } },
201 { FrontLeft, { 3.53553391e-1f, 2.04124145e-1f, 2.04124145e-1f } },
202 { FrontRight, { 3.53553391e-1f, -2.04124145e-1f, 2.04124145e-1f } },
203 { BackRight, { 3.53553391e-1f, -2.04124145e-1f, -2.04124145e-1f } },
204 }, X51SideCfg[4] = {
205 { SideLeft, { 3.33000782e-1f, 1.89084803e-1f, -2.00042375e-1f, -2.12307769e-2f, -1.14579885e-2f } },
206 { FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 1.66295695e-1f, 7.30571517e-2f, 2.10901184e-2f } },
207 { FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 1.66295695e-1f, -7.30571517e-2f, 2.10901184e-2f } },
208 { SideRight, { 3.33000782e-1f, -1.89084803e-1f, -2.00042375e-1f, 2.12307769e-2f, -1.14579885e-2f } },
209 }, X51RearCfg[4] = {
210 { BackLeft, { 3.33000782e-1f, 1.89084803e-1f, -2.00042375e-1f, -2.12307769e-2f, -1.14579885e-2f } },
211 { FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 1.66295695e-1f, 7.30571517e-2f, 2.10901184e-2f } },
212 { FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 1.66295695e-1f, -7.30571517e-2f, 2.10901184e-2f } },
213 { BackRight, { 3.33000782e-1f, -1.89084803e-1f, -2.00042375e-1f, 2.12307769e-2f, -1.14579885e-2f } },
214 }, X61Cfg[6] = {
215 { SideLeft, { 2.04460341e-1f, 2.17177926e-1f, -4.39996780e-2f, -2.60790269e-2f, -6.87239792e-2f } },
216 { FrontLeft, { 1.58923161e-1f, 9.21772680e-2f, 1.59658796e-1f, 6.66278083e-2f, 3.84686854e-2f } },
217 { FrontRight, { 1.58923161e-1f, -9.21772680e-2f, 1.59658796e-1f, -6.66278083e-2f, 3.84686854e-2f } },
218 { SideRight, { 2.04460341e-1f, -2.17177926e-1f, -4.39996780e-2f, 2.60790269e-2f, -6.87239792e-2f } },
219 { BackCenter, { 2.50001688e-1f, 0.00000000e+0f, -2.50000094e-1f, 0.00000000e+0f, 6.05133395e-2f } },
220 }, X71Cfg[6] = {
221 { BackLeft, { 2.04124145e-1f, 1.08880247e-1f, -1.88586120e-1f, -1.29099444e-1f, 7.45355993e-2f, 3.73460789e-2f, 0.00000000e+0f } },
222 { SideLeft, { 2.04124145e-1f, 2.17760495e-1f, 0.00000000e+0f, 0.00000000e+0f, -1.49071198e-1f, -3.73460789e-2f, 0.00000000e+0f } },
223 { FrontLeft, { 2.04124145e-1f, 1.08880247e-1f, 1.88586120e-1f, 1.29099444e-1f, 7.45355993e-2f, 3.73460789e-2f, 0.00000000e+0f } },
224 { FrontRight, { 2.04124145e-1f, -1.08880247e-1f, 1.88586120e-1f, -1.29099444e-1f, 7.45355993e-2f, -3.73460789e-2f, 0.00000000e+0f } },
225 { SideRight, { 2.04124145e-1f, -2.17760495e-1f, 0.00000000e+0f, 0.00000000e+0f, -1.49071198e-1f, 3.73460789e-2f, 0.00000000e+0f } },
226 { BackRight, { 2.04124145e-1f, -1.08880247e-1f, -1.88586120e-1f, 1.29099444e-1f, 7.45355993e-2f, -3.73460789e-2f, 0.00000000e+0f } },
229 void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei order, const ALsizei *RESTRICT chans_per_order)
231 /* NFC is only used when AvgSpeakerDist is greater than 0, and can only be
232 * used when rendering to an ambisonic buffer.
234 const char *devname{device->DeviceName.c_str()};
235 if(!GetConfigValueBool(devname, "decoder", "nfc", 1) || !(ctrl_dist > 0.0f))
236 return;
238 device->AvgSpeakerDist = minf(ctrl_dist, 10.0f);
239 TRACE("Using near-field reference distance: %.2f meters\n", device->AvgSpeakerDist);
241 auto iter = std::copy(chans_per_order, chans_per_order+order+1,
242 std::begin(device->NumChannelsPerOrder));
243 std::fill(iter, std::end(device->NumChannelsPerOrder), 0);
246 void InitDistanceComp(ALCdevice *device, const AmbDecConf *conf, const ALsizei (&speakermap)[MAX_OUTPUT_CHANNELS])
248 using namespace std::placeholders;
250 const ALfloat maxdist{
251 std::accumulate(conf->Speakers.begin(), conf->Speakers.end(), float{0.0f},
252 std::bind(maxf, _1, std::bind(std::mem_fn(&AmbDecConf::SpeakerConf::Distance), _2))
256 const char *devname{device->DeviceName.c_str()};
257 if(!GetConfigValueBool(devname, "decoder", "distance-comp", 1) || !(maxdist > 0.0f))
258 return;
260 auto srate = static_cast<ALfloat>(device->Frequency);
261 size_t total{0u};
262 for(size_t i{0u};i < conf->Speakers.size();i++)
264 const AmbDecConf::SpeakerConf &speaker = conf->Speakers[i];
265 const ALsizei chan{speakermap[i]};
267 /* Distance compensation only delays in steps of the sample rate. This
268 * is a bit less accurate since the delay time falls to the nearest
269 * sample time, but it's far simpler as it doesn't have to deal with
270 * phase offsets. This means at 48khz, for instance, the distance delay
271 * will be in steps of about 7 millimeters.
273 const ALfloat delay{
274 std::floor((maxdist - speaker.Distance)/SPEEDOFSOUNDMETRESPERSEC*srate + 0.5f)
276 if(delay >= static_cast<ALfloat>(MAX_DELAY_LENGTH))
277 ERR("Delay for speaker \"%s\" exceeds buffer length (%f >= %d)\n",
278 speaker.Name.c_str(), delay, MAX_DELAY_LENGTH);
280 device->ChannelDelay[chan].Length = static_cast<ALsizei>(clampf(
281 delay, 0.0f, static_cast<ALfloat>(MAX_DELAY_LENGTH-1)
283 device->ChannelDelay[chan].Gain = speaker.Distance / maxdist;
284 TRACE("Channel %u \"%s\" distance compensation: %d samples, %f gain\n", chan,
285 speaker.Name.c_str(), device->ChannelDelay[chan].Length,
286 device->ChannelDelay[chan].Gain
289 /* Round up to the next 4th sample, so each channel buffer starts
290 * 16-byte aligned.
292 total += RoundUp(device->ChannelDelay[chan].Length, 4);
295 if(total > 0)
297 device->ChannelDelay.resize(total);
298 device->ChannelDelay[0].Buffer = device->ChannelDelay.data();
299 auto set_bufptr = [](const DistanceComp::DistData &last, const DistanceComp::DistData &cur) -> DistanceComp::DistData
301 DistanceComp::DistData ret{cur};
302 ret.Buffer = last.Buffer + RoundUp(last.Length, 4);
303 return ret;
305 std::partial_sum(device->ChannelDelay.begin(), device->ChannelDelay.end(),
306 device->ChannelDelay.begin(), set_bufptr);
311 auto GetAmbiScales(AmbiNorm scaletype) noexcept -> const std::array<float,MAX_AMBI_COEFFS>&
313 if(scaletype == AmbiNorm::FuMa) return AmbiScale::FromFuMa;
314 if(scaletype == AmbiNorm::SN3D) return AmbiScale::FromSN3D;
315 return AmbiScale::FromN3D;
318 auto GetAmbiLayout(AmbiLayout layouttype) noexcept -> const std::array<int,MAX_AMBI_COEFFS>&
320 if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa;
321 return AmbiIndex::FromACN;
325 void InitPanning(ALCdevice *device)
327 const ChannelMap *chanmap{nullptr};
328 ALsizei coeffcount{0};
329 ALsizei count{0};
331 switch(device->FmtChans)
333 case DevFmtMono:
334 count = static_cast<ALsizei>(COUNTOF(MonoCfg));
335 chanmap = MonoCfg;
336 coeffcount = 1;
337 break;
339 case DevFmtStereo:
340 count = static_cast<ALsizei>(COUNTOF(StereoCfg));
341 chanmap = StereoCfg;
342 coeffcount = 3;
343 break;
345 case DevFmtQuad:
346 count = static_cast<ALsizei>(COUNTOF(QuadCfg));
347 chanmap = QuadCfg;
348 coeffcount = 3;
349 break;
351 case DevFmtX51:
352 count = static_cast<ALsizei>(COUNTOF(X51SideCfg));
353 chanmap = X51SideCfg;
354 coeffcount = 5;
355 break;
357 case DevFmtX51Rear:
358 count = static_cast<ALsizei>(COUNTOF(X51RearCfg));
359 chanmap = X51RearCfg;
360 coeffcount = 5;
361 break;
363 case DevFmtX61:
364 count = static_cast<ALsizei>(COUNTOF(X61Cfg));
365 chanmap = X61Cfg;
366 coeffcount = 5;
367 break;
369 case DevFmtX71:
370 count = static_cast<ALsizei>(COUNTOF(X71Cfg));
371 chanmap = X71Cfg;
372 coeffcount = 7;
373 break;
375 case DevFmtAmbi3D:
376 break;
379 if(device->FmtChans == DevFmtAmbi3D)
381 const char *devname{device->DeviceName.c_str()};
382 const std::array<int,MAX_AMBI_COEFFS> &acnmap = GetAmbiLayout(device->mAmbiLayout);
383 const std::array<float,MAX_AMBI_COEFFS> &n3dscale = GetAmbiScales(device->mAmbiScale);
385 count = (device->mAmbiOrder == 3) ? 16 :
386 (device->mAmbiOrder == 2) ? 9 :
387 (device->mAmbiOrder == 1) ? 4 : 1;
388 std::transform(acnmap.begin(), acnmap.begin()+count, std::begin(device->Dry.AmbiMap),
389 [&n3dscale](const ALsizei &acn) noexcept -> BFChannelConfig
390 { return BFChannelConfig{1.0f/n3dscale[acn], acn}; }
392 device->Dry.NumChannels = count;
394 if(device->mAmbiOrder < 2)
396 device->FOAOut.AmbiMap = device->Dry.AmbiMap;
397 device->FOAOut.NumChannels = 0;
399 else
401 device->FOAOut.AmbiMap.fill(BFChannelConfig{});
402 std::transform(AmbiIndex::From3D.begin(), AmbiIndex::From3D.begin()+4,
403 std::begin(device->FOAOut.AmbiMap),
404 [](const ALsizei &acn) noexcept { return BFChannelConfig{1.0f, acn}; }
406 device->FOAOut.NumChannels = 4;
408 device->AmbiUp = al::make_unique<AmbiUpsampler>();
409 device->AmbiUp->reset(device->mAmbiOrder,
410 400.0f / static_cast<ALfloat>(device->Frequency));
413 ALfloat nfc_delay{0.0f};
414 if(ConfigValueFloat(devname, "decoder", "nfc-ref-delay", &nfc_delay) && nfc_delay > 0.0f)
416 static constexpr ALsizei chans_per_order[MAX_AMBI_ORDER+1]{ 1, 3, 5, 7 };
417 nfc_delay = clampf(nfc_delay, 0.001f, 1000.0f);
418 InitNearFieldCtrl(device, nfc_delay * SPEEDOFSOUNDMETRESPERSEC,
419 device->mAmbiOrder, chans_per_order);
422 device->RealOut.NumChannels = 0;
424 else
426 ChannelDec chancoeffs[MAX_OUTPUT_CHANNELS]{};
427 ALsizei idxmap[MAX_OUTPUT_CHANNELS]{};
428 for(ALsizei i{0};i < count;++i)
430 const ALint idx{GetChannelIdxByName(device->RealOut, chanmap[i].ChanName)};
431 if(idx < 0)
433 ERR("Failed to find %s channel in device\n",
434 GetLabelFromChannel(chanmap[i].ChanName));
435 continue;
437 idxmap[i] = idx;
438 std::copy_n(chanmap[i].Config, coeffcount, chancoeffs[i]);
441 std::transform(AmbiIndex::From2D.begin(), AmbiIndex::From2D.begin()+coeffcount,
442 std::begin(device->Dry.AmbiMap),
443 [](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
445 device->Dry.NumChannels = coeffcount;
447 TRACE("Enabling %s-order%s ambisonic decoder\n",
448 (coeffcount > 5) ? "third" :
449 (coeffcount > 3) ? "second" : "first",
452 device->AmbiDecoder = al::make_unique<BFormatDec>();
453 device->AmbiDecoder->reset(coeffcount, 400.0f / static_cast<ALfloat>(device->Frequency),
454 count, chancoeffs, idxmap);
456 if(coeffcount <= 3)
457 device->FOAOut.AmbiMap = device->Dry.AmbiMap;
458 else
460 const std::array<ALfloat,MAX_AMBI_ORDER+1> scales{AmbiUpsampler::GetHFOrderScales(1,
461 (coeffcount > 7) ? 4 :
462 (coeffcount > 5) ? 3 :
463 (coeffcount > 3) ? 2 : 1)};
465 device->FOAOut.AmbiMap[0] = BFChannelConfig{scales[0], AmbiIndex::From2D[0]};
466 auto ambimap_iter = std::transform(AmbiIndex::From2D.begin()+1,
467 AmbiIndex::From2D.begin()+3, std::begin(device->FOAOut.AmbiMap)+1,
468 [&scales](const ALsizei &acn) noexcept { return BFChannelConfig{scales[1], acn}; }
470 std::fill(ambimap_iter, std::end(device->FOAOut.AmbiMap), BFChannelConfig{});
472 device->FOAOut.NumChannels = 0;
474 device->RealOut.NumChannels = device->channelsFromFmt();
478 void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei (&speakermap)[MAX_OUTPUT_CHANNELS])
480 if(conf->FreqBands != 1)
481 ERR("Basic renderer uses the high-frequency matrix as single-band (xover_freq = %.0fhz)\n",
482 conf->XOverFreq);
484 ALsizei count;
485 if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
487 count = (conf->ChanMask > AMBI_2ORDER_MASK) ? 16 :
488 (conf->ChanMask > AMBI_1ORDER_MASK) ? 9 : 4;
489 std::transform(AmbiIndex::From3D.begin(), AmbiIndex::From3D.begin()+count,
490 std::begin(device->Dry.AmbiMap),
491 [](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
494 else
496 count = (conf->ChanMask > AMBI_2ORDER_MASK) ? 7 :
497 (conf->ChanMask > AMBI_1ORDER_MASK) ? 5 : 3;
498 std::transform(AmbiIndex::From2D.begin(), AmbiIndex::From2D.begin()+count,
499 std::begin(device->Dry.AmbiMap),
500 [](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
503 device->Dry.NumChannels = count;
505 TRACE("Enabling %s-order%s ambisonic decoder\n",
506 (conf->ChanMask > AMBI_2ORDER_MASK) ? "third" :
507 (conf->ChanMask > AMBI_1ORDER_MASK) ? "second" : "first",
508 (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? " periphonic" : ""
510 device->AmbiDecoder = al::make_unique<BFormatDec>();
511 device->AmbiDecoder->reset(conf, false, count, device->Frequency, speakermap);
513 if(conf->ChanMask <= AMBI_1ORDER_MASK)
514 device->FOAOut.AmbiMap = device->Dry.AmbiMap;
515 else
517 const std::array<ALfloat,MAX_AMBI_ORDER+1> scales{AmbiUpsampler::GetHFOrderScales(1,
518 (conf->ChanMask > AMBI_3ORDER_MASK) ? 4 :
519 (conf->ChanMask > AMBI_2ORDER_MASK) ? 3 :
520 (conf->ChanMask > AMBI_1ORDER_MASK) ? 2 : 1)};
522 auto ambimap_iter = std::begin(device->FOAOut.AmbiMap);
523 if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
525 device->FOAOut.AmbiMap[0] = BFChannelConfig{scales[0], AmbiIndex::From3D[0]};
526 ambimap_iter = std::transform(AmbiIndex::From3D.begin()+1,
527 AmbiIndex::From3D.begin()+4, ambimap_iter+1,
528 [&scales](const ALsizei &acn) noexcept { return BFChannelConfig{scales[1], acn}; }
531 else
533 device->FOAOut.AmbiMap[0] = BFChannelConfig{scales[0], AmbiIndex::From2D[0]};
534 ambimap_iter = std::transform(AmbiIndex::From2D.begin()+1,
535 AmbiIndex::From2D.begin()+3, ambimap_iter,
536 [&scales](const ALsizei &acn) noexcept { return BFChannelConfig{scales[1], acn}; }
539 std::fill(ambimap_iter, std::end(device->FOAOut.AmbiMap), BFChannelConfig{});
541 device->FOAOut.NumChannels = 0;
543 device->RealOut.NumChannels = device->channelsFromFmt();
545 InitDistanceComp(device, conf, speakermap);
548 void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei (&speakermap)[MAX_OUTPUT_CHANNELS])
550 static constexpr ALsizei chans_per_order2d[MAX_AMBI_ORDER+1] = { 1, 2, 2, 2 };
551 static constexpr ALsizei chans_per_order3d[MAX_AMBI_ORDER+1] = { 1, 3, 5, 7 };
553 ALsizei count;
554 if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
556 count = (conf->ChanMask > AMBI_2ORDER_MASK) ? 16 :
557 (conf->ChanMask > AMBI_1ORDER_MASK) ? 9 : 4;
558 std::transform(AmbiIndex::From3D.begin(), AmbiIndex::From3D.begin()+count,
559 std::begin(device->Dry.AmbiMap),
560 [](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
563 else
565 count = (conf->ChanMask > AMBI_2ORDER_MASK) ? 7 :
566 (conf->ChanMask > AMBI_1ORDER_MASK) ? 5 : 3;
567 std::transform(AmbiIndex::From2D.begin(), AmbiIndex::From2D.begin()+count,
568 std::begin(device->Dry.AmbiMap),
569 [](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
572 device->Dry.NumChannels = count;
574 TRACE("Enabling %s-band %s-order%s ambisonic decoder\n",
575 (conf->FreqBands == 1) ? "single" : "dual",
576 (conf->ChanMask > AMBI_2ORDER_MASK) ? "third" :
577 (conf->ChanMask > AMBI_1ORDER_MASK) ? "second" : "first",
578 (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? " periphonic" : ""
580 device->AmbiDecoder = al::make_unique<BFormatDec>();
581 device->AmbiDecoder->reset(conf, true, count, device->Frequency, speakermap);
583 if(conf->ChanMask <= AMBI_1ORDER_MASK)
585 device->FOAOut.AmbiMap = device->Dry.AmbiMap;
586 device->FOAOut.NumChannels = 0;
588 else
590 device->FOAOut.AmbiMap.fill(BFChannelConfig{});
591 if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
593 count = 4;
594 std::transform(AmbiIndex::From3D.begin(), AmbiIndex::From3D.begin()+count,
595 std::begin(device->FOAOut.AmbiMap),
596 [](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
599 else
601 count = 3;
602 std::transform(AmbiIndex::From2D.begin(), AmbiIndex::From2D.begin()+count,
603 std::begin(device->FOAOut.AmbiMap),
604 [](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
607 device->FOAOut.NumChannels = count;
610 device->RealOut.NumChannels = device->channelsFromFmt();
612 using namespace std::placeholders;
613 auto accum_spkr_dist = std::bind(
614 std::plus<float>{}, _1, std::bind(std::mem_fn(&AmbDecConf::SpeakerConf::Distance), _2)
616 const ALfloat avg_dist{
617 std::accumulate(conf->Speakers.begin(), conf->Speakers.end(), float{0.0f},
618 accum_spkr_dist) / static_cast<ALfloat>(conf->Speakers.size())
620 InitNearFieldCtrl(device, avg_dist,
621 (conf->ChanMask > AMBI_2ORDER_MASK) ? 3 :
622 (conf->ChanMask > AMBI_1ORDER_MASK) ? 2 : 1,
623 (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? chans_per_order3d : chans_per_order2d
626 InitDistanceComp(device, conf, speakermap);
629 void InitHrtfPanning(ALCdevice *device)
631 /* NOTE: In degrees, and azimuth goes clockwise. */
632 static constexpr AngularPoint AmbiPoints[]{
633 { 35.264390f, -45.000000f },
634 { 35.264390f, 45.000000f },
635 { 35.264390f, 135.000000f },
636 { 35.264390f, -135.000000f },
637 { -35.264390f, -45.000000f },
638 { -35.264390f, 45.000000f },
639 { -35.264390f, 135.000000f },
640 { -35.264390f, -135.000000f },
641 { 0.000000f, -20.905157f },
642 { 0.000000f, 20.905157f },
643 { 0.000000f, 159.094843f },
644 { 0.000000f, -159.094843f },
645 { 20.905157f, -90.000000f },
646 { -20.905157f, -90.000000f },
647 { -20.905157f, 90.000000f },
648 { 20.905157f, 90.000000f },
649 { 69.094843f, 0.000000f },
650 { -69.094843f, 0.000000f },
651 { -69.094843f, 180.000000f },
652 { 69.094843f, 180.000000f },
654 static constexpr ALfloat AmbiMatrix[][MAX_AMBI_COEFFS]{
655 { 5.00000000e-02f, 5.00000000e-02f, 5.00000000e-02f, 5.00000000e-02f, 6.45497224e-02f, 6.45497224e-02f, 0.00000000e+00f, 6.45497224e-02f, 0.00000000e+00f, 1.48264644e-02f, 6.33865691e-02f, 1.01126676e-01f, -7.36485380e-02f, -1.09260065e-02f, 7.08683387e-02f, -1.01622099e-01f },
656 { 5.00000000e-02f, -5.00000000e-02f, 5.00000000e-02f, 5.00000000e-02f, -6.45497224e-02f, -6.45497224e-02f, 0.00000000e+00f, 6.45497224e-02f, 0.00000000e+00f, -1.48264644e-02f, -6.33865691e-02f, -1.01126676e-01f, -7.36485380e-02f, -1.09260065e-02f, 7.08683387e-02f, -1.01622099e-01f },
657 { 5.00000000e-02f, -5.00000000e-02f, 5.00000000e-02f, -5.00000000e-02f, 6.45497224e-02f, -6.45497224e-02f, 0.00000000e+00f, -6.45497224e-02f, 0.00000000e+00f, -1.48264644e-02f, 6.33865691e-02f, -1.01126676e-01f, -7.36485380e-02f, 1.09260065e-02f, 7.08683387e-02f, 1.01622099e-01f },
658 { 5.00000000e-02f, 5.00000000e-02f, 5.00000000e-02f, -5.00000000e-02f, -6.45497224e-02f, 6.45497224e-02f, 0.00000000e+00f, -6.45497224e-02f, 0.00000000e+00f, 1.48264644e-02f, -6.33865691e-02f, 1.01126676e-01f, -7.36485380e-02f, 1.09260065e-02f, 7.08683387e-02f, 1.01622099e-01f },
659 { 5.00000000e-02f, 5.00000000e-02f, -5.00000000e-02f, 5.00000000e-02f, 6.45497224e-02f, -6.45497224e-02f, 0.00000000e+00f, -6.45497224e-02f, 0.00000000e+00f, 1.48264644e-02f, -6.33865691e-02f, 1.01126676e-01f, 7.36485380e-02f, -1.09260065e-02f, -7.08683387e-02f, -1.01622099e-01f },
660 { 5.00000000e-02f, -5.00000000e-02f, -5.00000000e-02f, 5.00000000e-02f, -6.45497224e-02f, 6.45497224e-02f, 0.00000000e+00f, -6.45497224e-02f, 0.00000000e+00f, -1.48264644e-02f, 6.33865691e-02f, -1.01126676e-01f, 7.36485380e-02f, -1.09260065e-02f, -7.08683387e-02f, -1.01622099e-01f },
661 { 5.00000000e-02f, -5.00000000e-02f, -5.00000000e-02f, -5.00000000e-02f, 6.45497224e-02f, 6.45497224e-02f, 0.00000000e+00f, 6.45497224e-02f, 0.00000000e+00f, -1.48264644e-02f, -6.33865691e-02f, -1.01126676e-01f, 7.36485380e-02f, 1.09260065e-02f, -7.08683387e-02f, 1.01622099e-01f },
662 { 5.00000000e-02f, 5.00000000e-02f, -5.00000000e-02f, -5.00000000e-02f, -6.45497224e-02f, -6.45497224e-02f, 0.00000000e+00f, 6.45497224e-02f, 0.00000000e+00f, 1.48264644e-02f, 6.33865691e-02f, 1.01126676e-01f, 7.36485380e-02f, 1.09260065e-02f, -7.08683387e-02f, 1.01622099e-01f },
663 { 5.00000000e-02f, 3.09016994e-02f, 0.00000000e+00f, 8.09016994e-02f, 6.45497224e-02f, 0.00000000e+00f, -5.59016994e-02f, 0.00000000e+00f, 7.21687836e-02f, 7.76323754e-02f, 0.00000000e+00f, -1.49775925e-01f, 0.00000000e+00f, -2.95083663e-02f, 0.00000000e+00f, 7.76323754e-02f },
664 { 5.00000000e-02f, -3.09016994e-02f, 0.00000000e+00f, 8.09016994e-02f, -6.45497224e-02f, 0.00000000e+00f, -5.59016994e-02f, 0.00000000e+00f, 7.21687836e-02f, -7.76323754e-02f, 0.00000000e+00f, 1.49775925e-01f, 0.00000000e+00f, -2.95083663e-02f, 0.00000000e+00f, 7.76323754e-02f },
665 { 5.00000000e-02f, -3.09016994e-02f, 0.00000000e+00f, -8.09016994e-02f, 6.45497224e-02f, 0.00000000e+00f, -5.59016994e-02f, 0.00000000e+00f, 7.21687836e-02f, -7.76323754e-02f, 0.00000000e+00f, 1.49775925e-01f, 0.00000000e+00f, 2.95083663e-02f, 0.00000000e+00f, -7.76323754e-02f },
666 { 5.00000000e-02f, 3.09016994e-02f, 0.00000000e+00f, -8.09016994e-02f, -6.45497224e-02f, 0.00000000e+00f, -5.59016994e-02f, 0.00000000e+00f, 7.21687836e-02f, 7.76323754e-02f, 0.00000000e+00f, -1.49775925e-01f, 0.00000000e+00f, 2.95083663e-02f, 0.00000000e+00f, -7.76323754e-02f },
667 { 5.00000000e-02f, 8.09016994e-02f, 3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 6.45497224e-02f, -3.45491503e-02f, 0.00000000e+00f, -8.44966837e-02f, -4.79794466e-02f, 0.00000000e+00f, -6.77901327e-02f, 3.03448665e-02f, 0.00000000e+00f, -1.65948192e-01f, 0.00000000e+00f },
668 { 5.00000000e-02f, 8.09016994e-02f, -3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, -6.45497224e-02f, -3.45491503e-02f, 0.00000000e+00f, -8.44966837e-02f, -4.79794466e-02f, 0.00000000e+00f, -6.77901327e-02f, -3.03448665e-02f, 0.00000000e+00f, 1.65948192e-01f, 0.00000000e+00f },
669 { 5.00000000e-02f, -8.09016994e-02f, -3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 6.45497224e-02f, -3.45491503e-02f, 0.00000000e+00f, -8.44966837e-02f, 4.79794466e-02f, 0.00000000e+00f, 6.77901327e-02f, -3.03448665e-02f, 0.00000000e+00f, 1.65948192e-01f, 0.00000000e+00f },
670 { 5.00000000e-02f, -8.09016994e-02f, 3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, -6.45497224e-02f, -3.45491503e-02f, 0.00000000e+00f, -8.44966837e-02f, 4.79794466e-02f, 0.00000000e+00f, 6.77901327e-02f, 3.03448665e-02f, 0.00000000e+00f, -1.65948192e-01f, 0.00000000e+00f },
671 { 5.00000000e-02f, 0.00000000e+00f, 8.09016994e-02f, 3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 9.04508497e-02f, 6.45497224e-02f, 1.23279000e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, 7.94438918e-02f, 1.12611206e-01f, -2.42115150e-02f, 1.25611822e-01f },
672 { 5.00000000e-02f, 0.00000000e+00f, -8.09016994e-02f, 3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 9.04508497e-02f, -6.45497224e-02f, 1.23279000e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -7.94438918e-02f, 1.12611206e-01f, 2.42115150e-02f, 1.25611822e-01f },
673 { 5.00000000e-02f, 0.00000000e+00f, -8.09016994e-02f, -3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 9.04508497e-02f, 6.45497224e-02f, 1.23279000e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -7.94438918e-02f, -1.12611206e-01f, 2.42115150e-02f, -1.25611822e-01f },
674 { 5.00000000e-02f, 0.00000000e+00f, 8.09016994e-02f, -3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 9.04508497e-02f, -6.45497224e-02f, 1.23279000e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, 7.94438918e-02f, -1.12611206e-01f, -2.42115150e-02f, -1.25611822e-01f }
676 static constexpr ALfloat AmbiOrderHFGainFOA[MAX_AMBI_ORDER+1]{
677 3.16227766e+00f, 1.82574186e+00f
678 }, AmbiOrderHFGainHOA[MAX_AMBI_ORDER+1]{
679 2.35702260e+00f, 1.82574186e+00f, 9.42809042e-01f
680 /* 1.86508671e+00f, 1.60609389e+00f, 1.14205530e+00f, 5.68379553e-01f */
682 static constexpr ALsizei IndexMap[9]{ 0, 1, 2, 3, 4, 5, 6, 7, 8 };
683 static constexpr ALsizei ChansPerOrder[MAX_AMBI_ORDER+1]{ 1, 3, 5, 0 };
684 const ALfloat *AmbiOrderHFGain{AmbiOrderHFGainFOA};
685 ALsizei count{4};
687 static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrix), "Ambisonic HRTF mismatch");
689 /* Don't bother with HOA when using full HRTF rendering. Nothing needs it,
690 * and it eases the CPU/memory load.
692 if(device->mRenderMode != HrtfRender)
694 device->AmbiUp = al::make_unique<AmbiUpsampler>();
696 AmbiOrderHFGain = AmbiOrderHFGainHOA;
697 count = static_cast<ALsizei>(COUNTOF(IndexMap));
700 device->mHrtfState = DirectHrtfState::Create(count);
702 std::transform(std::begin(IndexMap), std::begin(IndexMap)+count, std::begin(device->Dry.AmbiMap),
703 [](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
705 device->Dry.NumChannels = count;
707 if(device->AmbiUp)
709 device->FOAOut.AmbiMap.fill(BFChannelConfig{});
710 std::transform(std::begin(IndexMap), std::begin(IndexMap)+4, std::begin(device->FOAOut.AmbiMap),
711 [](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
713 device->FOAOut.NumChannels = 4;
715 device->AmbiUp->reset(2, 400.0f / static_cast<ALfloat>(device->Frequency));
717 else
719 device->FOAOut.AmbiMap = device->Dry.AmbiMap;
720 device->FOAOut.NumChannels = 0;
723 device->RealOut.NumChannels = device->channelsFromFmt();
725 BuildBFormatHrtf(device->mHrtf,
726 device->mHrtfState.get(), device->Dry.NumChannels, AmbiPoints, AmbiMatrix,
727 static_cast<ALsizei>(COUNTOF(AmbiPoints)), AmbiOrderHFGain
730 InitNearFieldCtrl(device, device->mHrtf->distance, device->AmbiUp ? 2 : 1,
731 ChansPerOrder);
734 void InitUhjPanning(ALCdevice *device)
736 static constexpr ALsizei count{3};
738 auto acnmap_end = AmbiIndex::FromFuMa.begin() + count;
739 std::transform(AmbiIndex::FromFuMa.begin(), acnmap_end, std::begin(device->Dry.AmbiMap),
740 [](const ALsizei &acn) noexcept -> BFChannelConfig
741 { return BFChannelConfig{1.0f/AmbiScale::FromFuMa[acn], acn}; }
743 device->Dry.NumChannels = count;
745 device->FOAOut.AmbiMap = device->Dry.AmbiMap;
746 device->FOAOut.NumChannels = 0;
748 device->RealOut.NumChannels = device->channelsFromFmt();
751 } // namespace
754 void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALfloat spread,
755 ALfloat (&coeffs)[MAX_AMBI_COEFFS])
757 /* Zeroth-order */
758 coeffs[0] = 1.0f; /* ACN 0 = 1 */
759 /* First-order */
760 coeffs[1] = 1.732050808f * y; /* ACN 1 = sqrt(3) * Y */
761 coeffs[2] = 1.732050808f * z; /* ACN 2 = sqrt(3) * Z */
762 coeffs[3] = 1.732050808f * x; /* ACN 3 = sqrt(3) * X */
763 /* Second-order */
764 coeffs[4] = 3.872983346f * x * y; /* ACN 4 = sqrt(15) * X * Y */
765 coeffs[5] = 3.872983346f * y * z; /* ACN 5 = sqrt(15) * Y * Z */
766 coeffs[6] = 1.118033989f * (z*z*3.0f - 1.0f); /* ACN 6 = sqrt(5)/2 * (3*Z*Z - 1) */
767 coeffs[7] = 3.872983346f * x * z; /* ACN 7 = sqrt(15) * X * Z */
768 coeffs[8] = 1.936491673f * (x*x - y*y); /* ACN 8 = sqrt(15)/2 * (X*X - Y*Y) */
769 /* Third-order */
770 coeffs[9] = 2.091650066f * y * (x*x*3.0f - y*y); /* ACN 9 = sqrt(35/8) * Y * (3*X*X - Y*Y) */
771 coeffs[10] = 10.246950766f * z * x * y; /* ACN 10 = sqrt(105) * Z * X * Y */
772 coeffs[11] = 1.620185175f * y * (z*z*5.0f - 1.0f); /* ACN 11 = sqrt(21/8) * Y * (5*Z*Z - 1) */
773 coeffs[12] = 1.322875656f * z * (z*z*5.0f - 3.0f); /* ACN 12 = sqrt(7)/2 * Z * (5*Z*Z - 3) */
774 coeffs[13] = 1.620185175f * x * (z*z*5.0f - 1.0f); /* ACN 13 = sqrt(21/8) * X * (5*Z*Z - 1) */
775 coeffs[14] = 5.123475383f * z * (x*x - y*y); /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */
776 coeffs[15] = 2.091650066f * x * (x*x - y*y*3.0f); /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */
777 /* Fourth-order */
778 /* ACN 16 = sqrt(35)*3/2 * X * Y * (X*X - Y*Y) */
779 /* ACN 17 = sqrt(35/2)*3/2 * (3*X*X - Y*Y) * Y * Z */
780 /* ACN 18 = sqrt(5)*3/2 * X * Y * (7*Z*Z - 1) */
781 /* ACN 19 = sqrt(5/2)*3/2 * Y * Z * (7*Z*Z - 3) */
782 /* ACN 20 = 3/8 * (35*Z*Z*Z*Z - 30*Z*Z + 3) */
783 /* ACN 21 = sqrt(5/2)*3/2 * X * Z * (7*Z*Z - 3) */
784 /* ACN 22 = sqrt(5)*3/4 * (X*X - Y*Y) * (7*Z*Z - 1) */
785 /* ACN 23 = sqrt(35/2)*3/2 * (X*X - 3*Y*Y) * X * Z */
786 /* ACN 24 = sqrt(35)*3/8 * (X*X*X*X - 6*X*X*Y*Y + Y*Y*Y*Y) */
788 if(spread > 0.0f)
790 /* Implement the spread by using a spherical source that subtends the
791 * angle spread. See:
792 * http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3
794 * When adjusted for N3D normalization instead of SN3D, these
795 * calculations are:
797 * ZH0 = -sqrt(pi) * (-1+ca);
798 * ZH1 = 0.5*sqrt(pi) * sa*sa;
799 * ZH2 = -0.5*sqrt(pi) * ca*(-1+ca)*(ca+1);
800 * ZH3 = -0.125*sqrt(pi) * (-1+ca)*(ca+1)*(5*ca*ca - 1);
801 * ZH4 = -0.125*sqrt(pi) * ca*(-1+ca)*(ca+1)*(7*ca*ca - 3);
802 * ZH5 = -0.0625*sqrt(pi) * (-1+ca)*(ca+1)*(21*ca*ca*ca*ca - 14*ca*ca + 1);
804 * The gain of the source is compensated for size, so that the
805 * loudness doesn't depend on the spread. Thus:
807 * ZH0 = 1.0f;
808 * ZH1 = 0.5f * (ca+1.0f);
809 * ZH2 = 0.5f * (ca+1.0f)*ca;
810 * ZH3 = 0.125f * (ca+1.0f)*(5.0f*ca*ca - 1.0f);
811 * ZH4 = 0.125f * (ca+1.0f)*(7.0f*ca*ca - 3.0f)*ca;
812 * ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f);
814 ALfloat ca = std::cos(spread * 0.5f);
815 /* Increase the source volume by up to +3dB for a full spread. */
816 ALfloat scale = std::sqrt(1.0f + spread/al::MathDefs<float>::Tau());
818 ALfloat ZH0_norm = scale;
819 ALfloat ZH1_norm = 0.5f * (ca+1.f) * scale;
820 ALfloat ZH2_norm = 0.5f * (ca+1.f)*ca * scale;
821 ALfloat ZH3_norm = 0.125f * (ca+1.f)*(5.f*ca*ca-1.f) * scale;
823 /* Zeroth-order */
824 coeffs[0] *= ZH0_norm;
825 /* First-order */
826 coeffs[1] *= ZH1_norm;
827 coeffs[2] *= ZH1_norm;
828 coeffs[3] *= ZH1_norm;
829 /* Second-order */
830 coeffs[4] *= ZH2_norm;
831 coeffs[5] *= ZH2_norm;
832 coeffs[6] *= ZH2_norm;
833 coeffs[7] *= ZH2_norm;
834 coeffs[8] *= ZH2_norm;
835 /* Third-order */
836 coeffs[9] *= ZH3_norm;
837 coeffs[10] *= ZH3_norm;
838 coeffs[11] *= ZH3_norm;
839 coeffs[12] *= ZH3_norm;
840 coeffs[13] *= ZH3_norm;
841 coeffs[14] *= ZH3_norm;
842 coeffs[15] *= ZH3_norm;
847 void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat*RESTRICT coeffs, ALfloat ingain, ALfloat (&gains)[MAX_OUTPUT_CHANNELS])
849 ASSUME(numchans > 0);
850 auto iter = std::transform(chanmap, chanmap+numchans, std::begin(gains),
851 [coeffs,ingain](const BFChannelConfig &chanmap) noexcept -> ALfloat
853 ASSUME(chanmap.Index >= 0);
854 return chanmap.Scale * coeffs[chanmap.Index] * ingain;
857 std::fill(iter, std::end(gains), 0.0f);
860 void ComputePanGains(const ALeffectslot *slot, const ALfloat*RESTRICT coeffs, ALfloat ingain, ALfloat (&gains)[MAX_OUTPUT_CHANNELS])
861 { ComputePanningGainsBF(slot->ChanMap, slot->NumChannels, coeffs, ingain, gains); }
864 void aluInitRenderer(ALCdevice *device, ALint hrtf_id, HrtfRequestMode hrtf_appreq, HrtfRequestMode hrtf_userreq)
866 /* Hold the HRTF the device last used, in case it's used again. */
867 HrtfEntry *old_hrtf{device->mHrtf};
869 device->mHrtfState = nullptr;
870 device->mHrtf = nullptr;
871 device->HrtfName.clear();
872 device->mRenderMode = NormalRender;
874 device->Dry.AmbiMap.fill(BFChannelConfig{});
875 device->Dry.NumChannels = 0;
876 std::fill(std::begin(device->NumChannelsPerOrder), std::end(device->NumChannelsPerOrder), 0);
878 device->AvgSpeakerDist = 0.0f;
879 device->ChannelDelay.clear();
881 device->AmbiDecoder = nullptr;
882 device->AmbiUp = nullptr;
883 device->Stablizer = nullptr;
885 if(device->FmtChans != DevFmtStereo)
887 if(old_hrtf)
888 Hrtf_DecRef(old_hrtf);
889 old_hrtf = nullptr;
890 if(hrtf_appreq == Hrtf_Enable)
891 device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
893 const char *layout{nullptr};
894 switch(device->FmtChans)
896 case DevFmtQuad: layout = "quad"; break;
897 case DevFmtX51: /* fall-through */
898 case DevFmtX51Rear: layout = "surround51"; break;
899 case DevFmtX61: layout = "surround61"; break;
900 case DevFmtX71: layout = "surround71"; break;
901 /* Mono, Stereo, and Ambisonics output don't use custom decoders. */
902 case DevFmtMono:
903 case DevFmtStereo:
904 case DevFmtAmbi3D:
905 break;
908 const char *devname{device->DeviceName.c_str()};
909 ALsizei speakermap[MAX_OUTPUT_CHANNELS];
910 AmbDecConf *pconf{nullptr};
911 AmbDecConf conf{};
912 if(layout)
914 const char *fname;
915 if(ConfigValueStr(devname, "decoder", layout, &fname))
917 if(!conf.load(fname))
918 ERR("Failed to load layout file %s\n", fname);
919 else if(conf.Speakers.size() > MAX_OUTPUT_CHANNELS)
920 ERR("Unsupported speaker count " SZFMT " (max %d)\n", conf.Speakers.size(),
921 MAX_OUTPUT_CHANNELS);
922 else if(conf.ChanMask > AMBI_3ORDER_MASK)
923 ERR("Unsupported channel mask 0x%04x (max 0x%x)\n", conf.ChanMask,
924 AMBI_3ORDER_MASK);
925 else if(MakeSpeakerMap(device, &conf, speakermap))
926 pconf = &conf;
930 if(!pconf)
931 InitPanning(device);
932 else if(GetConfigValueBool(devname, "decoder", "hq-mode", 0))
933 InitHQPanning(device, pconf, speakermap);
934 else
935 InitCustomPanning(device, pconf, speakermap);
937 /* Enable the stablizer only for formats that have front-left, front-
938 * right, and front-center outputs.
940 switch(device->FmtChans)
942 case DevFmtX51:
943 case DevFmtX51Rear:
944 case DevFmtX61:
945 case DevFmtX71:
946 if(GetConfigValueBool(devname, nullptr, "front-stablizer", 0))
948 auto stablizer = al::make_unique<FrontStablizer>();
949 /* Initialize band-splitting filters for the front-left and
950 * front-right channels, with a crossover at 5khz (could be
951 * higher).
953 const ALfloat scale{static_cast<ALfloat>(5000.0 / device->Frequency)};
955 stablizer->LFilter.init(scale);
956 stablizer->RFilter = stablizer->LFilter;
958 /* Initialize all-pass filters for all other channels. */
959 stablizer->APFilter[0].init(scale);
960 std::fill(std::begin(stablizer->APFilter)+1, std::end(stablizer->APFilter),
961 stablizer->APFilter[0]);
963 device->Stablizer = std::move(stablizer);
965 break;
966 case DevFmtMono:
967 case DevFmtStereo:
968 case DevFmtQuad:
969 case DevFmtAmbi3D:
970 break;
972 TRACE("Front stablizer %s\n", device->Stablizer ? "enabled" : "disabled");
974 return;
977 device->AmbiDecoder = nullptr;
979 bool headphones{device->IsHeadphones != AL_FALSE};
980 if(device->Type != Loopback)
982 const char *mode;
983 if(ConfigValueStr(device->DeviceName.c_str(), nullptr, "stereo-mode", &mode))
985 if(strcasecmp(mode, "headphones") == 0)
986 headphones = true;
987 else if(strcasecmp(mode, "speakers") == 0)
988 headphones = false;
989 else if(strcasecmp(mode, "auto") != 0)
990 ERR("Unexpected stereo-mode: %s\n", mode);
994 if(hrtf_userreq == Hrtf_Default)
996 bool usehrtf = (headphones && hrtf_appreq != Hrtf_Disable) ||
997 (hrtf_appreq == Hrtf_Enable);
998 if(!usehrtf) goto no_hrtf;
1000 device->HrtfStatus = ALC_HRTF_ENABLED_SOFT;
1001 if(headphones && hrtf_appreq != Hrtf_Disable)
1002 device->HrtfStatus = ALC_HRTF_HEADPHONES_DETECTED_SOFT;
1004 else
1006 if(hrtf_userreq != Hrtf_Enable)
1008 if(hrtf_appreq == Hrtf_Enable)
1009 device->HrtfStatus = ALC_HRTF_DENIED_SOFT;
1010 goto no_hrtf;
1012 device->HrtfStatus = ALC_HRTF_REQUIRED_SOFT;
1015 if(device->HrtfList.empty())
1016 device->HrtfList = EnumerateHrtf(device->DeviceName.c_str());
1018 if(hrtf_id >= 0 && static_cast<size_t>(hrtf_id) < device->HrtfList.size())
1020 const EnumeratedHrtf &entry = device->HrtfList[hrtf_id];
1021 HrtfEntry *hrtf{GetLoadedHrtf(entry.hrtf)};
1022 if(hrtf && hrtf->sampleRate == device->Frequency)
1024 device->mHrtf = hrtf;
1025 device->HrtfName = entry.name;
1027 else if(hrtf)
1028 Hrtf_DecRef(hrtf);
1031 if(!device->mHrtf)
1033 auto find_hrtf = [device](const EnumeratedHrtf &entry) -> bool
1035 HrtfEntry *hrtf{GetLoadedHrtf(entry.hrtf)};
1036 if(!hrtf) return false;
1037 if(hrtf->sampleRate != device->Frequency)
1039 Hrtf_DecRef(hrtf);
1040 return false;
1042 device->mHrtf = hrtf;
1043 device->HrtfName = entry.name;
1044 return true;
1046 std::find_if(device->HrtfList.cbegin(), device->HrtfList.cend(), find_hrtf);
1049 if(device->mHrtf)
1051 if(old_hrtf)
1052 Hrtf_DecRef(old_hrtf);
1053 old_hrtf = nullptr;
1055 device->mRenderMode = HrtfRender;
1056 const char *mode;
1057 if(ConfigValueStr(device->DeviceName.c_str(), nullptr, "hrtf-mode", &mode))
1059 if(strcasecmp(mode, "full") == 0)
1060 device->mRenderMode = HrtfRender;
1061 else if(strcasecmp(mode, "basic") == 0)
1062 device->mRenderMode = NormalRender;
1063 else
1064 ERR("Unexpected hrtf-mode: %s\n", mode);
1067 TRACE("%s HRTF rendering enabled, using \"%s\"\n",
1068 ((device->mRenderMode == HrtfRender) ? "Full" : "Basic"), device->HrtfName.c_str()
1070 InitHrtfPanning(device);
1071 return;
1073 device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
1075 no_hrtf:
1076 if(old_hrtf)
1077 Hrtf_DecRef(old_hrtf);
1078 old_hrtf = nullptr;
1079 TRACE("HRTF disabled\n");
1081 device->mRenderMode = StereoPair;
1083 int bs2blevel{((headphones && hrtf_appreq != Hrtf_Disable) ||
1084 (hrtf_appreq == Hrtf_Enable)) ? 5 : 0};
1085 if(device->Type != Loopback)
1086 ConfigValueInt(device->DeviceName.c_str(), nullptr, "cf_level", &bs2blevel);
1087 if(bs2blevel > 0 && bs2blevel <= 6)
1089 device->Bs2b = al::make_unique<bs2b>();
1090 bs2b_set_params(device->Bs2b.get(), bs2blevel, device->Frequency);
1091 TRACE("BS2B enabled\n");
1092 InitPanning(device);
1093 return;
1096 TRACE("BS2B disabled\n");
1098 const char *mode;
1099 if(ConfigValueStr(device->DeviceName.c_str(), nullptr, "stereo-encoding", &mode))
1101 if(strcasecmp(mode, "uhj") == 0)
1102 device->mRenderMode = NormalRender;
1103 else if(strcasecmp(mode, "panpot") != 0)
1104 ERR("Unexpected stereo-encoding: %s\n", mode);
1106 if(device->mRenderMode == NormalRender)
1108 device->Uhj_Encoder = al::make_unique<Uhj2Encoder>();
1109 TRACE("UHJ enabled\n");
1110 InitUhjPanning(device);
1111 return;
1114 TRACE("UHJ disabled\n");
1115 InitPanning(device);
1119 void aluInitEffectPanning(ALeffectslot *slot)
1121 const size_t count{countof(slot->ChanMap)};
1122 auto acnmap_end = AmbiIndex::From3D.begin() + count;
1123 std::transform(AmbiIndex::From3D.begin(), acnmap_end, std::begin(slot->ChanMap),
1124 [](const ALsizei &acn) noexcept { return BFChannelConfig{1.0f, acn}; }
1126 slot->NumChannels = static_cast<ALsizei>(count);