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
35 #include "alAuxEffectSlot.h"
39 #include "bformatdec.h"
40 #include "filters/splitter.h"
41 #include "uhjfilter.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
;
56 inline const char *GetLabelFromChannel(Channel 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;
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:
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,
132 if(speaker
.Name
== "LF")
134 else if(speaker
.Name
== "RF")
136 else if(speaker
.Name
== "CE")
138 else if(speaker
.Name
== "LS")
140 if(device
->FmtChans
== DevFmtX51Rear
)
145 else if(speaker
.Name
== "RS")
147 if(device
->FmtChans
== DevFmtX51Rear
)
152 else if(speaker
.Name
== "LB")
154 if(device
->FmtChans
== DevFmtX51
)
159 else if(speaker
.Name
== "RB")
161 if(device
->FmtChans
== DevFmtX51
)
166 else if(speaker
.Name
== "CB")
170 const char *name
{speaker
.Name
.c_str()};
174 if(sscanf(name
, "AUX%u%c", &n
, &c
) == 1 && n
< 16)
175 ch
= static_cast<Channel
>(Aux0
+n
);
178 ERR("AmbDec speaker label \"%s\" not recognized\n", name
);
182 const int chidx
{GetChannelIdxByName(device
->RealOut
, ch
)};
184 ERR("Failed to lookup AmbDec speaker label %s\n", speaker
.Name
.c_str());
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
} },
197 { FrontLeft
, { 5.00000000e-1f
, 2.88675135e-1f
, 5.52305643e-2f
} },
198 { FrontRight
, { 5.00000000e-1f
, -2.88675135e-1f
, 5.52305643e-2f
} },
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
} },
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
} },
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
} },
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
} },
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
))
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
))
260 auto srate
= static_cast<ALfloat
>(device
->Frequency
);
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.
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
292 total
+= RoundUp(device
->ChannelDelay
[chan
].Length
, 4);
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);
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};
331 switch(device
->FmtChans
)
334 count
= static_cast<ALsizei
>(COUNTOF(MonoCfg
));
340 count
= static_cast<ALsizei
>(COUNTOF(StereoCfg
));
346 count
= static_cast<ALsizei
>(COUNTOF(QuadCfg
));
352 count
= static_cast<ALsizei
>(COUNTOF(X51SideCfg
));
353 chanmap
= X51SideCfg
;
358 count
= static_cast<ALsizei
>(COUNTOF(X51RearCfg
));
359 chanmap
= X51RearCfg
;
364 count
= static_cast<ALsizei
>(COUNTOF(X61Cfg
));
370 count
= static_cast<ALsizei
>(COUNTOF(X71Cfg
));
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;
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;
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
)};
433 ERR("Failed to find %s channel in device\n",
434 GetLabelFromChannel(chanmap
[i
].ChanName
));
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
);
457 device
->FOAOut
.AmbiMap
= device
->Dry
.AmbiMap
;
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",
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
}; }
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
;
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
}; }
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 };
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
}; }
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;
590 device
->FOAOut
.AmbiMap
.fill(BFChannelConfig
{});
591 if((conf
->ChanMask
&AMBI_PERIPHONIC_MASK
))
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
}; }
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
};
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
;
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
));
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,
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();
754 void CalcAmbiCoeffs(const ALfloat y
, const ALfloat z
, const ALfloat x
, const ALfloat spread
,
755 ALfloat (&coeffs
)[MAX_AMBI_COEFFS
])
758 coeffs
[0] = 1.0f
; /* ACN 0 = 1 */
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 */
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) */
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) */
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) */
790 /* Implement the spread by using a spherical source that subtends the
792 * http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3
794 * When adjusted for N3D normalization instead of SN3D, these
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:
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
;
824 coeffs
[0] *= ZH0_norm
;
826 coeffs
[1] *= ZH1_norm
;
827 coeffs
[2] *= ZH1_norm
;
828 coeffs
[3] *= ZH1_norm
;
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
;
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
)
888 Hrtf_DecRef(old_hrtf
);
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. */
908 const char *devname
{device
->DeviceName
.c_str()};
909 ALsizei speakermap
[MAX_OUTPUT_CHANNELS
];
910 AmbDecConf
*pconf
{nullptr};
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
,
925 else if(MakeSpeakerMap(device
, &conf
, speakermap
))
932 else if(GetConfigValueBool(devname
, "decoder", "hq-mode", 0))
933 InitHQPanning(device
, pconf
, speakermap
);
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
)
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
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
);
972 TRACE("Front stablizer %s\n", device
->Stablizer
? "enabled" : "disabled");
977 device
->AmbiDecoder
= nullptr;
979 bool headphones
{device
->IsHeadphones
!= AL_FALSE
};
980 if(device
->Type
!= Loopback
)
983 if(ConfigValueStr(device
->DeviceName
.c_str(), nullptr, "stereo-mode", &mode
))
985 if(strcasecmp(mode
, "headphones") == 0)
987 else if(strcasecmp(mode
, "speakers") == 0)
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
;
1006 if(hrtf_userreq
!= Hrtf_Enable
)
1008 if(hrtf_appreq
== Hrtf_Enable
)
1009 device
->HrtfStatus
= ALC_HRTF_DENIED_SOFT
;
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
;
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
)
1042 device
->mHrtf
= hrtf
;
1043 device
->HrtfName
= entry
.name
;
1046 std::find_if(device
->HrtfList
.cbegin(), device
->HrtfList
.cend(), find_hrtf
);
1052 Hrtf_DecRef(old_hrtf
);
1055 device
->mRenderMode
= HrtfRender
;
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
;
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
);
1073 device
->HrtfStatus
= ALC_HRTF_UNSUPPORTED_FORMAT_SOFT
;
1077 Hrtf_DecRef(old_hrtf
);
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
);
1096 TRACE("BS2B disabled\n");
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
);
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
);