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
40 #include "al/auxeffectslot.h"
44 #include "alnumeric.h"
45 #include "aloptional.h"
51 #include "bformatdec.h"
53 #include "devformat.h"
56 #include "math_defs.h"
57 #include "opthelpers.h"
58 #include "uhjfilter.h"
61 constexpr std::array
<float,MAX_AMBI_CHANNELS
> AmbiScale::FromN3D
;
62 constexpr std::array
<float,MAX_AMBI_CHANNELS
> AmbiScale::FromSN3D
;
63 constexpr std::array
<float,MAX_AMBI_CHANNELS
> AmbiScale::FromFuMa
;
64 constexpr std::array
<uint8_t,MAX_AMBI_CHANNELS
> AmbiIndex::FromFuMa
;
65 constexpr std::array
<uint8_t,MAX_AMBI_CHANNELS
> AmbiIndex::FromACN
;
66 constexpr std::array
<uint8_t,MAX_AMBI2D_CHANNELS
> AmbiIndex::From2D
;
67 constexpr std::array
<uint8_t,MAX_AMBI_CHANNELS
> AmbiIndex::From3D
;
72 using namespace std::placeholders
;
73 using std::chrono::seconds
;
74 using std::chrono::nanoseconds
;
76 inline const char *GetLabelFromChannel(Channel channel
)
80 case FrontLeft
: return "front-left";
81 case FrontRight
: return "front-right";
82 case FrontCenter
: return "front-center";
83 case LFE
: return "lfe";
84 case BackLeft
: return "back-left";
85 case BackRight
: return "back-right";
86 case BackCenter
: return "back-center";
87 case SideLeft
: return "side-left";
88 case SideRight
: return "side-right";
90 case UpperFrontLeft
: return "upper-front-left";
91 case UpperFrontRight
: return "upper-front-right";
92 case UpperBackLeft
: return "upper-back-left";
93 case UpperBackRight
: return "upper-back-right";
94 case LowerFrontLeft
: return "lower-front-left";
95 case LowerFrontRight
: return "lower-front-right";
96 case LowerBackLeft
: return "lower-back-left";
97 case LowerBackRight
: return "lower-back-right";
99 case Aux0
: return "aux-0";
100 case Aux1
: return "aux-1";
101 case Aux2
: return "aux-2";
102 case Aux3
: return "aux-3";
103 case Aux4
: return "aux-4";
104 case Aux5
: return "aux-5";
105 case Aux6
: return "aux-6";
106 case Aux7
: return "aux-7";
107 case Aux8
: return "aux-8";
108 case Aux9
: return "aux-9";
109 case Aux10
: return "aux-10";
110 case Aux11
: return "aux-11";
111 case Aux12
: return "aux-12";
112 case Aux13
: return "aux-13";
113 case Aux14
: return "aux-14";
114 case Aux15
: return "aux-15";
116 case MaxChannels
: break;
122 void AllocChannels(ALCdevice
*device
, const ALuint main_chans
, const ALuint real_chans
)
124 TRACE("Channel config, Main: %u, Real: %u\n", main_chans
, real_chans
);
126 /* Allocate extra channels for any post-filter output. */
127 const ALuint num_chans
{main_chans
+ real_chans
};
129 TRACE("Allocating %u channels, %zu bytes\n", num_chans
,
130 num_chans
*sizeof(device
->MixBuffer
[0]));
131 device
->MixBuffer
.resize(num_chans
);
132 al::span
<FloatBufferLine
> buffer
{device
->MixBuffer
.data(), device
->MixBuffer
.size()};
134 device
->Dry
.Buffer
= buffer
.first(main_chans
);
135 buffer
= buffer
.subspan(main_chans
);
138 device
->RealOut
.Buffer
= buffer
.first(real_chans
);
139 buffer
= buffer
.subspan(real_chans
);
142 device
->RealOut
.Buffer
= device
->Dry
.Buffer
;
148 ALfloat Config
[MAX_AMBI2D_CHANNELS
];
151 bool MakeSpeakerMap(ALCdevice
*device
, const AmbDecConf
*conf
, ALuint (&speakermap
)[MAX_OUTPUT_CHANNELS
])
153 auto map_spkr
= [device
](const AmbDecConf::SpeakerConf
&speaker
) -> ALuint
155 /* NOTE: AmbDec does not define any standard speaker names, however
156 * for this to work we have to by able to find the output channel
157 * the speaker definition corresponds to. Therefore, OpenAL Soft
158 * requires these channel labels to be recognized:
169 * Additionally, surround51 will acknowledge back speakers for side
170 * channels, and surround51rear will acknowledge side speakers for
171 * back channels, to avoid issues with an ambdec expecting 5.1 to
172 * use the side channels when the device is configured for back,
176 if(speaker
.Name
== "LF")
178 else if(speaker
.Name
== "RF")
180 else if(speaker
.Name
== "CE")
182 else if(speaker
.Name
== "LS")
184 if(device
->FmtChans
== DevFmtX51Rear
)
189 else if(speaker
.Name
== "RS")
191 if(device
->FmtChans
== DevFmtX51Rear
)
196 else if(speaker
.Name
== "LB")
198 if(device
->FmtChans
== DevFmtX51
)
203 else if(speaker
.Name
== "RB")
205 if(device
->FmtChans
== DevFmtX51
)
210 else if(speaker
.Name
== "CB")
214 const char *name
{speaker
.Name
.c_str()};
218 if(sscanf(name
, "AUX%u%c", &n
, &c
) == 1 && n
< 16)
219 ch
= static_cast<Channel
>(Aux0
+n
);
222 ERR("AmbDec speaker label \"%s\" not recognized\n", name
);
223 return INVALID_CHANNEL_INDEX
;
226 const ALuint chidx
{GetChannelIdxByName(device
->RealOut
, ch
)};
227 if(chidx
== INVALID_CHANNEL_INDEX
)
228 ERR("Failed to lookup AmbDec speaker label %s\n", speaker
.Name
.c_str());
231 std::transform(conf
->Speakers
.begin(), conf
->Speakers
.end(), std::begin(speakermap
), map_spkr
);
232 /* Return success if no invalid entries are found. */
233 auto spkrmap_end
= std::begin(speakermap
) + conf
->Speakers
.size();
234 return std::find(std::begin(speakermap
), spkrmap_end
, INVALID_CHANNEL_INDEX
) == spkrmap_end
;
238 constexpr ChannelMap MonoCfg
[1] = {
239 { FrontCenter
, { 1.0f
} },
241 { FrontLeft
, { 5.00000000e-1f
, 2.88675135e-1f
, 5.52305643e-2f
} },
242 { FrontRight
, { 5.00000000e-1f
, -2.88675135e-1f
, 5.52305643e-2f
} },
244 { BackLeft
, { 3.53553391e-1f
, 2.04124145e-1f
, -2.04124145e-1f
} },
245 { FrontLeft
, { 3.53553391e-1f
, 2.04124145e-1f
, 2.04124145e-1f
} },
246 { FrontRight
, { 3.53553391e-1f
, -2.04124145e-1f
, 2.04124145e-1f
} },
247 { BackRight
, { 3.53553391e-1f
, -2.04124145e-1f
, -2.04124145e-1f
} },
249 { SideLeft
, { 3.33000782e-1f
, 1.89084803e-1f
, -2.00042375e-1f
, -2.12307769e-2f
, -1.14579885e-2f
} },
250 { FrontLeft
, { 1.88542860e-1f
, 1.27709292e-1f
, 1.66295695e-1f
, 7.30571517e-2f
, 2.10901184e-2f
} },
251 { FrontRight
, { 1.88542860e-1f
, -1.27709292e-1f
, 1.66295695e-1f
, -7.30571517e-2f
, 2.10901184e-2f
} },
252 { SideRight
, { 3.33000782e-1f
, -1.89084803e-1f
, -2.00042375e-1f
, 2.12307769e-2f
, -1.14579885e-2f
} },
254 { BackLeft
, { 3.33000782e-1f
, 1.89084803e-1f
, -2.00042375e-1f
, -2.12307769e-2f
, -1.14579885e-2f
} },
255 { FrontLeft
, { 1.88542860e-1f
, 1.27709292e-1f
, 1.66295695e-1f
, 7.30571517e-2f
, 2.10901184e-2f
} },
256 { FrontRight
, { 1.88542860e-1f
, -1.27709292e-1f
, 1.66295695e-1f
, -7.30571517e-2f
, 2.10901184e-2f
} },
257 { BackRight
, { 3.33000782e-1f
, -1.89084803e-1f
, -2.00042375e-1f
, 2.12307769e-2f
, -1.14579885e-2f
} },
259 { SideLeft
, { 2.04460341e-1f
, 2.17177926e-1f
, -4.39996780e-2f
, -2.60790269e-2f
, -6.87239792e-2f
} },
260 { FrontLeft
, { 1.58923161e-1f
, 9.21772680e-2f
, 1.59658796e-1f
, 6.66278083e-2f
, 3.84686854e-2f
} },
261 { FrontRight
, { 1.58923161e-1f
, -9.21772680e-2f
, 1.59658796e-1f
, -6.66278083e-2f
, 3.84686854e-2f
} },
262 { SideRight
, { 2.04460341e-1f
, -2.17177926e-1f
, -4.39996780e-2f
, 2.60790269e-2f
, -6.87239792e-2f
} },
263 { BackCenter
, { 2.50001688e-1f
, 0.00000000e+0f
, -2.50000094e-1f
, 0.00000000e+0f
, 6.05133395e-2f
} },
265 { BackLeft
, { 2.04124145e-1f
, 1.08880247e-1f
, -1.88586120e-1f
, -1.29099444e-1f
, 7.45355993e-2f
, 3.73460789e-2f
, 0.00000000e+0f
} },
266 { SideLeft
, { 2.04124145e-1f
, 2.17760495e-1f
, 0.00000000e+0f
, 0.00000000e+0f
, -1.49071198e-1f
, -3.73460789e-2f
, 0.00000000e+0f
} },
267 { FrontLeft
, { 2.04124145e-1f
, 1.08880247e-1f
, 1.88586120e-1f
, 1.29099444e-1f
, 7.45355993e-2f
, 3.73460789e-2f
, 0.00000000e+0f
} },
268 { FrontRight
, { 2.04124145e-1f
, -1.08880247e-1f
, 1.88586120e-1f
, -1.29099444e-1f
, 7.45355993e-2f
, -3.73460789e-2f
, 0.00000000e+0f
} },
269 { SideRight
, { 2.04124145e-1f
, -2.17760495e-1f
, 0.00000000e+0f
, 0.00000000e+0f
, -1.49071198e-1f
, 3.73460789e-2f
, 0.00000000e+0f
} },
270 { BackRight
, { 2.04124145e-1f
, -1.08880247e-1f
, -1.88586120e-1f
, 1.29099444e-1f
, 7.45355993e-2f
, -3.73460789e-2f
, 0.00000000e+0f
} },
273 void InitNearFieldCtrl(ALCdevice
*device
, ALfloat ctrl_dist
, ALuint order
,
274 const al::span
<const ALuint
,MAX_AMBI_ORDER
+1> chans_per_order
)
276 /* NFC is only used when AvgSpeakerDist is greater than 0. */
277 const char *devname
{device
->DeviceName
.c_str()};
278 if(!GetConfigValueBool(devname
, "decoder", "nfc", 0) || !(ctrl_dist
> 0.0f
))
281 device
->AvgSpeakerDist
= clampf(ctrl_dist
, 0.1f
, 10.0f
);
282 TRACE("Using near-field reference distance: %.2f meters\n", device
->AvgSpeakerDist
);
284 auto iter
= std::copy(chans_per_order
.begin(), chans_per_order
.begin()+order
+1,
285 std::begin(device
->NumChannelsPerOrder
));
286 std::fill(iter
, std::end(device
->NumChannelsPerOrder
), 0u);
289 void InitDistanceComp(ALCdevice
*device
, const AmbDecConf
*conf
,
290 const ALuint (&speakermap
)[MAX_OUTPUT_CHANNELS
])
292 auto get_max
= std::bind(maxf
, _1
,
293 std::bind(std::mem_fn(&AmbDecConf::SpeakerConf::Distance
), _2
));
294 const ALfloat maxdist
{
295 std::accumulate(conf
->Speakers
.begin(), conf
->Speakers
.end(), float{0.0f
}, get_max
)};
297 const char *devname
{device
->DeviceName
.c_str()};
298 if(!GetConfigValueBool(devname
, "decoder", "distance-comp", 1) || !(maxdist
> 0.0f
))
301 const auto distSampleScale
= static_cast<ALfloat
>(device
->Frequency
)/SPEEDOFSOUNDMETRESPERSEC
;
302 const auto ChanDelay
= device
->ChannelDelay
.as_span();
304 for(size_t i
{0u};i
< conf
->Speakers
.size();i
++)
306 const AmbDecConf::SpeakerConf
&speaker
= conf
->Speakers
[i
];
307 const ALuint chan
{speakermap
[i
]};
309 /* Distance compensation only delays in steps of the sample rate. This
310 * is a bit less accurate since the delay time falls to the nearest
311 * sample time, but it's far simpler as it doesn't have to deal with
312 * phase offsets. This means at 48khz, for instance, the distance delay
313 * will be in steps of about 7 millimeters.
315 ALfloat delay
{std::floor((maxdist
- speaker
.Distance
)*distSampleScale
+ 0.5f
)};
316 if(delay
> ALfloat
{MAX_DELAY_LENGTH
-1})
318 ERR("Delay for speaker \"%s\" exceeds buffer length (%f > %d)\n",
319 speaker
.Name
.c_str(), delay
, MAX_DELAY_LENGTH
-1);
320 delay
= ALfloat
{MAX_DELAY_LENGTH
-1};
323 ChanDelay
[chan
].Length
= static_cast<ALuint
>(delay
);
324 ChanDelay
[chan
].Gain
= speaker
.Distance
/ maxdist
;
325 TRACE("Channel %u \"%s\" distance compensation: %u samples, %f gain\n", chan
,
326 speaker
.Name
.c_str(), ChanDelay
[chan
].Length
, ChanDelay
[chan
].Gain
);
328 /* Round up to the next 4th sample, so each channel buffer starts
331 total
+= RoundUp(ChanDelay
[chan
].Length
, 4);
336 device
->ChannelDelay
.setSampleCount(total
);
337 ChanDelay
[0].Buffer
= device
->ChannelDelay
.getSamples();
338 auto set_bufptr
= [](const DistanceComp::DistData
&last
, const DistanceComp::DistData
&cur
) -> DistanceComp::DistData
340 DistanceComp::DistData ret
{cur
};
341 ret
.Buffer
= last
.Buffer
+ RoundUp(last
.Length
, 4);
344 std::partial_sum(ChanDelay
.begin(), ChanDelay
.end(), ChanDelay
.begin(), set_bufptr
);
349 auto GetAmbiScales(AmbiNorm scaletype
) noexcept
-> const std::array
<float,MAX_AMBI_CHANNELS
>&
351 if(scaletype
== AmbiNorm::FuMa
) return AmbiScale::FromFuMa
;
352 if(scaletype
== AmbiNorm::SN3D
) return AmbiScale::FromSN3D
;
353 return AmbiScale::FromN3D
;
356 auto GetAmbiLayout(AmbiLayout layouttype
) noexcept
-> const std::array
<uint8_t,MAX_AMBI_CHANNELS
>&
358 if(layouttype
== AmbiLayout::FuMa
) return AmbiIndex::FromFuMa
;
359 return AmbiIndex::FromACN
;
363 void InitPanning(ALCdevice
*device
)
365 al::span
<const ChannelMap
> chanmap
;
368 switch(device
->FmtChans
)
386 chanmap
= X51SideCfg
;
391 chanmap
= X51RearCfg
;
409 if(device
->FmtChans
== DevFmtAmbi3D
)
411 const char *devname
{device
->DeviceName
.c_str()};
412 const std::array
<uint8_t,MAX_AMBI_CHANNELS
> &acnmap
= GetAmbiLayout(device
->mAmbiLayout
);
413 const std::array
<float,MAX_AMBI_CHANNELS
> &n3dscale
= GetAmbiScales(device
->mAmbiScale
);
415 /* For DevFmtAmbi3D, the ambisonic order is already set. */
416 const size_t count
{AmbiChannelsFromOrder(device
->mAmbiOrder
)};
417 std::transform(acnmap
.begin(), acnmap
.begin()+count
, std::begin(device
->Dry
.AmbiMap
),
418 [&n3dscale
](const uint8_t &acn
) noexcept
-> BFChannelConfig
419 { return BFChannelConfig
{1.0f
/n3dscale
[acn
], acn
}; }
421 AllocChannels(device
, static_cast<ALuint
>(count
), 0);
423 ALfloat nfc_delay
{ConfigValueFloat(devname
, "decoder", "nfc-ref-delay").value_or(0.0f
)};
426 static const ALuint chans_per_order
[MAX_AMBI_ORDER
+1]{ 1, 3, 5, 7 };
427 InitNearFieldCtrl(device
, nfc_delay
* SPEEDOFSOUNDMETRESPERSEC
, device
->mAmbiOrder
,
433 ChannelDec chancoeffs
[MAX_OUTPUT_CHANNELS
]{};
434 ALuint idxmap
[MAX_OUTPUT_CHANNELS
]{};
435 for(size_t i
{0u};i
< chanmap
.size();++i
)
437 const ALuint idx
{GetChannelIdxByName(device
->RealOut
, chanmap
[i
].ChanName
)};
438 if(idx
== INVALID_CHANNEL_INDEX
)
440 ERR("Failed to find %s channel in device\n",
441 GetLabelFromChannel(chanmap
[i
].ChanName
));
445 std::copy_n(chanmap
[i
].Config
, coeffcount
, chancoeffs
[i
]);
448 /* For non-DevFmtAmbi3D, set the ambisonic order given the mixing
449 * channel count. Built-in speaker decoders are always 2D, so just
450 * reverse that calculation.
452 device
->mAmbiOrder
= (coeffcount
-1) / 2;
454 std::transform(AmbiIndex::From2D
.begin(), AmbiIndex::From2D
.begin()+coeffcount
,
455 std::begin(device
->Dry
.AmbiMap
),
456 [](const uint8_t &index
) noexcept
{ return BFChannelConfig
{1.0f
, index
}; }
458 AllocChannels(device
, coeffcount
, device
->channelsFromFmt());
460 TRACE("Enabling %s-order%s ambisonic decoder\n",
461 (coeffcount
> 5) ? "third" :
462 (coeffcount
> 3) ? "second" : "first",
465 device
->AmbiDecoder
= al::make_unique
<BFormatDec
>(coeffcount
,
466 static_cast<ALsizei
>(chanmap
.size()), chancoeffs
, idxmap
);
470 void InitCustomPanning(ALCdevice
*device
, bool hqdec
, const AmbDecConf
*conf
,
471 const ALuint (&speakermap
)[MAX_OUTPUT_CHANNELS
])
473 static const ALuint chans_per_order2d
[MAX_AMBI_ORDER
+1] = { 1, 2, 2, 2 };
474 static const ALuint chans_per_order3d
[MAX_AMBI_ORDER
+1] = { 1, 3, 5, 7 };
476 if(!hqdec
&& conf
->FreqBands
!= 1)
477 ERR("Basic renderer uses the high-frequency matrix as single-band (xover_freq = %.0fhz)\n",
480 const ALuint order
{(conf
->ChanMask
> AMBI_2ORDER_MASK
) ? 3u :
481 (conf
->ChanMask
> AMBI_1ORDER_MASK
) ? 2u : 1u};
482 device
->mAmbiOrder
= order
;
485 if((conf
->ChanMask
&AMBI_PERIPHONIC_MASK
))
487 count
= static_cast<ALuint
>(AmbiChannelsFromOrder(order
));
488 std::transform(AmbiIndex::From3D
.begin(), AmbiIndex::From3D
.begin()+count
,
489 std::begin(device
->Dry
.AmbiMap
),
490 [](const uint8_t &index
) noexcept
{ return BFChannelConfig
{1.0f
, index
}; }
495 count
= static_cast<ALuint
>(Ambi2DChannelsFromOrder(order
));
496 std::transform(AmbiIndex::From2D
.begin(), AmbiIndex::From2D
.begin()+count
,
497 std::begin(device
->Dry
.AmbiMap
),
498 [](const uint8_t &index
) noexcept
{ return BFChannelConfig
{1.0f
, index
}; }
501 AllocChannels(device
, count
, device
->channelsFromFmt());
503 TRACE("Enabling %s-band %s-order%s ambisonic decoder\n",
504 (!hqdec
|| conf
->FreqBands
== 1) ? "single" : "dual",
505 (conf
->ChanMask
> AMBI_2ORDER_MASK
) ? "third" :
506 (conf
->ChanMask
> AMBI_1ORDER_MASK
) ? "second" : "first",
507 (conf
->ChanMask
&AMBI_PERIPHONIC_MASK
) ? " periphonic" : ""
509 device
->AmbiDecoder
= al::make_unique
<BFormatDec
>(conf
, hqdec
, count
, device
->Frequency
,
512 auto accum_spkr_dist
= std::bind(std::plus
<float>{}, _1
,
513 std::bind(std::mem_fn(&AmbDecConf::SpeakerConf::Distance
), _2
));
514 const ALfloat avg_dist
{
515 std::accumulate(conf
->Speakers
.begin(), conf
->Speakers
.end(), 0.0f
, accum_spkr_dist
) /
516 static_cast<ALfloat
>(conf
->Speakers
.size())};
517 InitNearFieldCtrl(device
, avg_dist
, order
,
518 (conf
->ChanMask
&AMBI_PERIPHONIC_MASK
) ? chans_per_order3d
: chans_per_order2d
);
520 InitDistanceComp(device
, conf
, speakermap
);
523 void InitHrtfPanning(ALCdevice
*device
)
525 static const AngularPoint AmbiPoints
[]{
526 { Deg2Rad( 0.000000f
), Deg2Rad( 0.000000f
) },
527 { Deg2Rad( 0.000000f
), Deg2Rad( 180.000000f
) },
528 { Deg2Rad( 0.000000f
), Deg2Rad( -90.000000f
) },
529 { Deg2Rad( 0.000000f
), Deg2Rad( 90.000000f
) },
530 { Deg2Rad( 90.000000f
), Deg2Rad( 0.000000f
) },
531 { Deg2Rad(-90.000000f
), Deg2Rad( 0.000000f
) },
532 { Deg2Rad( 45.000000f
), Deg2Rad( -90.000000f
) },
533 { Deg2Rad(-45.000000f
), Deg2Rad( -90.000000f
) },
534 { Deg2Rad( 45.000000f
), Deg2Rad( 90.000000f
) },
535 { Deg2Rad(-45.000000f
), Deg2Rad( 90.000000f
) },
536 { Deg2Rad( 45.000000f
), Deg2Rad( 0.000000f
) },
537 { Deg2Rad(-45.000000f
), Deg2Rad( 0.000000f
) },
538 { Deg2Rad( 45.000000f
), Deg2Rad( 180.000000f
) },
539 { Deg2Rad(-45.000000f
), Deg2Rad( 180.000000f
) },
540 { Deg2Rad( 0.000000f
), Deg2Rad( -45.000000f
) },
541 { Deg2Rad( 0.000000f
), Deg2Rad( 45.000000f
) },
542 { Deg2Rad( 0.000000f
), Deg2Rad(-135.000000f
) },
543 { Deg2Rad( 0.000000f
), Deg2Rad( 135.000000f
) },
544 { Deg2Rad( 35.264390f
), Deg2Rad( -45.000000f
) },
545 { Deg2Rad(-35.264390f
), Deg2Rad( -45.000000f
) },
546 { Deg2Rad( 35.264390f
), Deg2Rad( 45.000000f
) },
547 { Deg2Rad(-35.264390f
), Deg2Rad( 45.000000f
) },
548 { Deg2Rad( 35.264390f
), Deg2Rad(-135.000000f
) },
549 { Deg2Rad(-35.264390f
), Deg2Rad(-135.000000f
) },
550 { Deg2Rad( 35.264390f
), Deg2Rad( 135.000000f
) },
551 { Deg2Rad(-35.264390f
), Deg2Rad( 135.000000f
) },
553 static const float AmbiMatrix
[][MAX_AMBI_CHANNELS
]{
554 { 3.84615387e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 8.33950391e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, -4.96903997e-02f
, 0.00000000e+00f
, 8.60662966e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, -7.49473409e-02f
, 0.00000000e+00f
, 9.67566016e-02f
},
555 { 3.84615387e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, -8.33950391e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, -4.96903997e-02f
, 0.00000000e+00f
, 8.60662966e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, 7.49473409e-02f
, 0.00000000e+00f
, -9.67566016e-02f
},
556 { 3.84615387e-02f
, 8.33950391e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, -4.96903997e-02f
, 0.00000000e+00f
, -8.60662966e-02f
, -9.67566016e-02f
, 0.00000000e+00f
, -7.49473409e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
},
557 { 3.84615387e-02f
, -8.33950391e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, -4.96903997e-02f
, 0.00000000e+00f
, -8.60662966e-02f
, 9.67566016e-02f
, 0.00000000e+00f
, 7.49473409e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
},
558 { 3.84615379e-02f
, 0.00000000e+00f
, 8.33950384e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, 9.93807988e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, 1.22388497e-01f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
},
559 { 3.84615379e-02f
, 0.00000000e+00f
, -8.33950384e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, 9.93807988e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, -1.22388497e-01f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
},
560 { 3.84615383e-02f
, 4.53609209e-02f
, 4.53609209e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 6.83467647e-02f
, 2.48451995e-02f
, 0.00000000e+00f
, -4.30331483e-02f
, -3.21963528e-02f
, 0.00000000e+00f
, 6.23479680e-02f
, -1.27267260e-02f
, 0.00000000e+00f
, -6.90065559e-02f
, 0.00000000e+00f
},
561 { 3.84615383e-02f
, 4.53609209e-02f
, -4.53609209e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, -6.83467647e-02f
, 2.48451995e-02f
, 0.00000000e+00f
, -4.30331483e-02f
, -3.21963528e-02f
, 0.00000000e+00f
, 6.23479680e-02f
, 1.27267260e-02f
, 0.00000000e+00f
, 6.90065559e-02f
, 0.00000000e+00f
},
562 { 3.84615383e-02f
, -4.53609209e-02f
, 4.53609209e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, -6.83467647e-02f
, 2.48451995e-02f
, 0.00000000e+00f
, -4.30331483e-02f
, 3.21963528e-02f
, 0.00000000e+00f
, -6.23479680e-02f
, -1.27267260e-02f
, 0.00000000e+00f
, -6.90065559e-02f
, 0.00000000e+00f
},
563 { 3.84615383e-02f
, -4.53609209e-02f
, -4.53609209e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 6.83467647e-02f
, 2.48451995e-02f
, 0.00000000e+00f
, -4.30331483e-02f
, 3.21963528e-02f
, 0.00000000e+00f
, -6.23479680e-02f
, 1.27267260e-02f
, 0.00000000e+00f
, 6.90065559e-02f
, 0.00000000e+00f
},
564 { 3.84615383e-02f
, 0.00000000e+00f
, 4.53609209e-02f
, 4.53609209e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 2.48451995e-02f
, 6.83467647e-02f
, 4.30331483e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, -1.27267260e-02f
, 6.23479680e-02f
, 6.90065559e-02f
, 3.21963528e-02f
},
565 { 3.84615383e-02f
, 0.00000000e+00f
, -4.53609209e-02f
, 4.53609209e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 2.48451995e-02f
, -6.83467647e-02f
, 4.30331483e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, 1.27267260e-02f
, 6.23479680e-02f
, -6.90065559e-02f
, 3.21963528e-02f
},
566 { 3.84615383e-02f
, 0.00000000e+00f
, 4.53609209e-02f
, -4.53609209e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 2.48451995e-02f
, -6.83467647e-02f
, 4.30331483e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, -1.27267260e-02f
, -6.23479680e-02f
, 6.90065559e-02f
, -3.21963528e-02f
},
567 { 3.84615383e-02f
, 0.00000000e+00f
, -4.53609209e-02f
, -4.53609209e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 2.48451995e-02f
, 6.83467647e-02f
, 4.30331483e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 0.00000000e+00f
, 1.27267260e-02f
, -6.23479680e-02f
, -6.90065559e-02f
, -3.21963528e-02f
},
568 { 3.84615387e-02f
, 4.53609217e-02f
, 0.00000000e+00f
, 4.53609217e-02f
, 6.83467654e-02f
, 0.00000000e+00f
, -4.96903997e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 5.23190735e-02f
, 0.00000000e+00f
, -4.67609766e-02f
, 0.00000000e+00f
, -4.67609766e-02f
, 0.00000000e+00f
, -5.23190735e-02f
},
569 { 3.84615387e-02f
, -4.53609217e-02f
, 0.00000000e+00f
, 4.53609217e-02f
, -6.83467654e-02f
, 0.00000000e+00f
, -4.96903997e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, -5.23190735e-02f
, 0.00000000e+00f
, 4.67609766e-02f
, 0.00000000e+00f
, -4.67609766e-02f
, 0.00000000e+00f
, -5.23190735e-02f
},
570 { 3.84615387e-02f
, 4.53609217e-02f
, 0.00000000e+00f
, -4.53609217e-02f
, -6.83467654e-02f
, 0.00000000e+00f
, -4.96903997e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, 5.23190735e-02f
, 0.00000000e+00f
, -4.67609766e-02f
, 0.00000000e+00f
, 4.67609766e-02f
, 0.00000000e+00f
, 5.23190735e-02f
},
571 { 3.84615387e-02f
, -4.53609217e-02f
, 0.00000000e+00f
, -4.53609217e-02f
, 6.83467654e-02f
, 0.00000000e+00f
, -4.96903997e-02f
, 0.00000000e+00f
, 0.00000000e+00f
, -5.23190735e-02f
, 0.00000000e+00f
, 4.67609766e-02f
, 0.00000000e+00f
, 4.67609766e-02f
, 0.00000000e+00f
, 5.23190735e-02f
},
572 { 3.84615385e-02f
, 3.33333332e-02f
, 3.33333335e-02f
, 3.33333332e-02f
, 4.55645099e-02f
, 4.55645100e-02f
, 5.38752469e-10f
, 4.55645100e-02f
, 0.00000000e+00f
, 2.95742381e-02f
, 6.33865691e-02f
, 2.29081068e-02f
, -3.74087810e-02f
, 2.29081068e-02f
, 0.00000000e+00f
, -2.95742381e-02f
},
573 { 3.84615385e-02f
, 3.33333332e-02f
, -3.33333335e-02f
, 3.33333332e-02f
, 4.55645099e-02f
, -4.55645100e-02f
, 5.38752469e-10f
, -4.55645100e-02f
, 0.00000000e+00f
, 2.95742381e-02f
, -6.33865691e-02f
, 2.29081068e-02f
, 3.74087810e-02f
, 2.29081068e-02f
, 4.53051106e-20f
, -2.95742381e-02f
},
574 { 3.84615385e-02f
, -3.33333332e-02f
, 3.33333335e-02f
, 3.33333332e-02f
, -4.55645099e-02f
, -4.55645100e-02f
, 5.38752452e-10f
, 4.55645100e-02f
, 0.00000000e+00f
, -2.95742381e-02f
, -6.33865691e-02f
, -2.29081068e-02f
, -3.74087810e-02f
, 2.29081068e-02f
, 0.00000000e+00f
, -2.95742381e-02f
},
575 { 3.84615385e-02f
, -3.33333332e-02f
, -3.33333335e-02f
, 3.33333332e-02f
, -4.55645099e-02f
, 4.55645100e-02f
, 5.38752451e-10f
, -4.55645100e-02f
, 0.00000000e+00f
, -2.95742381e-02f
, 6.33865691e-02f
, -2.29081068e-02f
, 3.74087810e-02f
, 2.29081068e-02f
, 0.00000000e+00f
, -2.95742381e-02f
},
576 { 3.84615385e-02f
, 3.33333332e-02f
, 3.33333335e-02f
, -3.33333332e-02f
, -4.55645099e-02f
, 4.55645100e-02f
, 5.38752451e-10f
, -4.55645100e-02f
, 0.00000000e+00f
, 2.95742381e-02f
, -6.33865691e-02f
, 2.29081068e-02f
, -3.74087810e-02f
, -2.29081068e-02f
, 0.00000000e+00f
, 2.95742381e-02f
},
577 { 3.84615385e-02f
, 3.33333332e-02f
, -3.33333335e-02f
, -3.33333332e-02f
, -4.55645099e-02f
, -4.55645100e-02f
, 5.38752451e-10f
, 4.55645100e-02f
, 0.00000000e+00f
, 2.95742381e-02f
, 6.33865691e-02f
, 2.29081068e-02f
, 3.74087810e-02f
, -2.29081068e-02f
, 0.00000000e+00f
, 2.95742381e-02f
},
578 { 3.84615385e-02f
, -3.33333332e-02f
, 3.33333335e-02f
, -3.33333332e-02f
, 4.55645099e-02f
, -4.55645100e-02f
, 5.38752428e-10f
, -4.55645100e-02f
, 0.00000000e+00f
, -2.95742381e-02f
, 6.33865691e-02f
, -2.29081068e-02f
, -3.74087810e-02f
, -2.29081068e-02f
, 0.00000000e+00f
, 2.95742381e-02f
},
579 { 3.84615385e-02f
, -3.33333332e-02f
, -3.33333335e-02f
, -3.33333332e-02f
, 4.55645099e-02f
, 4.55645100e-02f
, 5.38752429e-10f
, 4.55645100e-02f
, 0.00000000e+00f
, -2.95742381e-02f
, -6.33865691e-02f
, -2.29081068e-02f
, 3.74087810e-02f
, -2.29081068e-02f
, 0.00000000e+00f
, 2.95742381e-02f
},
581 static const float AmbiOrderHFGain1O
[MAX_AMBI_ORDER
+1]{
582 3.60555128e+00f
, 2.08166600e+00f
583 }, AmbiOrderHFGain2O
[MAX_AMBI_ORDER
+1]{
584 2.68741925e+00f
, 2.08166600e+00f
, 1.07496770e+00f
585 }, AmbiOrderHFGain3O
[MAX_AMBI_ORDER
+1]{
586 2.12652604e+00f
, 1.83122879e+00f
, 1.30214339e+00f
, 6.48052398e-01f
588 static const ALuint ChansPerOrder
[MAX_AMBI_ORDER
+1]{ 1, 3, 5, 7 };
589 const float *AmbiOrderHFGain
{AmbiOrderHFGain1O
};
591 static_assert(al::size(AmbiPoints
) == al::size(AmbiMatrix
), "Ambisonic HRTF mismatch");
593 /* Don't bother with HOA when using full HRTF rendering. Nothing needs it,
594 * and it eases the CPU/memory load.
596 device
->mRenderMode
= HrtfRender
;
597 ALuint ambi_order
{1};
598 if(auto modeopt
= ConfigValueStr(device
->DeviceName
.c_str(), nullptr, "hrtf-mode"))
600 struct HrtfModeEntry
{
605 static const HrtfModeEntry hrtf_modes
[]{
606 { "full", HrtfRender
, 1 },
607 { "ambi1", NormalRender
, 1 },
608 { "ambi2", NormalRender
, 2 },
609 { "ambi3", NormalRender
, 3 },
612 const char *mode
{modeopt
->c_str()};
613 if(al::strcasecmp(mode
, "basic") == 0)
615 ERR("HRTF mode \"%s\" deprecated, substituting \"%s\"\n", mode
, "ambi2");
619 auto match_entry
= [mode
](const HrtfModeEntry
&entry
) -> bool
620 { return al::strcasecmp(mode
, entry
.name
) == 0; };
621 auto iter
= std::find_if(std::begin(hrtf_modes
), std::end(hrtf_modes
), match_entry
);
622 if(iter
== std::end(hrtf_modes
))
623 ERR("Unexpected hrtf-mode: %s\n", mode
);
626 device
->mRenderMode
= iter
->mode
;
627 ambi_order
= iter
->order
;
630 TRACE("%u%s order %sHRTF rendering enabled, using \"%s\"\n", ambi_order
,
631 (((ambi_order
%100)/10) == 1) ? "th" :
632 ((ambi_order
%10) == 1) ? "st" :
633 ((ambi_order
%10) == 2) ? "nd" :
634 ((ambi_order
%10) == 3) ? "rd" : "th",
635 (device
->mRenderMode
== HrtfRender
) ? "+ Full " : "",
636 device
->HrtfName
.c_str());
639 AmbiOrderHFGain
= AmbiOrderHFGain3O
;
640 else if(ambi_order
== 2)
641 AmbiOrderHFGain
= AmbiOrderHFGain2O
;
642 else if(ambi_order
== 1)
643 AmbiOrderHFGain
= AmbiOrderHFGain1O
;
644 device
->mAmbiOrder
= ambi_order
;
646 const size_t count
{AmbiChannelsFromOrder(ambi_order
)};
647 device
->mHrtfState
= DirectHrtfState::Create(count
);
649 std::transform(AmbiIndex::From3D
.begin(), AmbiIndex::From3D
.begin()+count
,
650 std::begin(device
->Dry
.AmbiMap
),
651 [](const uint8_t &index
) noexcept
{ return BFChannelConfig
{1.0f
, index
}; }
653 AllocChannels(device
, static_cast<ALuint
>(count
), device
->channelsFromFmt());
655 BuildBFormatHrtf(device
->mHrtf
, device
->mHrtfState
.get(), AmbiPoints
, AmbiMatrix
,
658 HrtfEntry
*Hrtf
{device
->mHrtf
};
659 InitNearFieldCtrl(device
, Hrtf
->field
[0].distance
, ambi_order
, ChansPerOrder
);
662 void InitUhjPanning(ALCdevice
*device
)
664 /* UHJ is always 2D first-order. */
665 constexpr size_t count
{Ambi2DChannelsFromOrder(1)};
667 device
->mAmbiOrder
= 1;
669 auto acnmap_end
= AmbiIndex::FromFuMa
.begin() + count
;
670 std::transform(AmbiIndex::FromFuMa
.begin(), acnmap_end
, std::begin(device
->Dry
.AmbiMap
),
671 [](const uint8_t &acn
) noexcept
-> BFChannelConfig
672 { return BFChannelConfig
{1.0f
/AmbiScale::FromFuMa
[acn
], acn
}; }
674 AllocChannels(device
, ALuint
{count
}, device
->channelsFromFmt());
679 void aluInitRenderer(ALCdevice
*device
, ALint hrtf_id
, HrtfRequestMode hrtf_appreq
, HrtfRequestMode hrtf_userreq
)
681 /* Hold the HRTF the device last used, in case it's used again. */
682 HrtfEntry
*old_hrtf
{device
->mHrtf
};
684 device
->mHrtfState
= nullptr;
685 device
->mHrtf
= nullptr;
686 device
->HrtfName
.clear();
687 device
->mRenderMode
= NormalRender
;
689 if(device
->FmtChans
!= DevFmtStereo
)
694 if(hrtf_appreq
== Hrtf_Enable
)
695 device
->HrtfStatus
= ALC_HRTF_UNSUPPORTED_FORMAT_SOFT
;
697 const char *layout
{nullptr};
698 switch(device
->FmtChans
)
700 case DevFmtQuad
: layout
= "quad"; break;
701 case DevFmtX51
: /* fall-through */
702 case DevFmtX51Rear
: layout
= "surround51"; break;
703 case DevFmtX61
: layout
= "surround61"; break;
704 case DevFmtX71
: layout
= "surround71"; break;
705 /* Mono, Stereo, and Ambisonics output don't use custom decoders. */
712 const char *devname
{device
->DeviceName
.c_str()};
713 ALuint speakermap
[MAX_OUTPUT_CHANNELS
];
714 AmbDecConf
*pconf
{nullptr};
718 if(auto decopt
= ConfigValueStr(devname
, "decoder", layout
))
720 if(!conf
.load(decopt
->c_str()))
721 ERR("Failed to load layout file %s\n", decopt
->c_str());
722 else if(conf
.Speakers
.size() > MAX_OUTPUT_CHANNELS
)
723 ERR("Unsupported speaker count %zu (max %d)\n", conf
.Speakers
.size(),
724 MAX_OUTPUT_CHANNELS
);
725 else if(conf
.ChanMask
> AMBI_3ORDER_MASK
)
726 ERR("Unsupported channel mask 0x%04x (max 0x%x)\n", conf
.ChanMask
,
728 else if(MakeSpeakerMap(device
, &conf
, speakermap
))
737 int hqdec
{GetConfigValueBool(devname
, "decoder", "hq-mode", 1)};
738 InitCustomPanning(device
, !!hqdec
, pconf
, speakermap
);
740 if(device
->AmbiDecoder
)
741 device
->PostProcess
= &ALCdevice::ProcessAmbiDec
;
745 bool headphones
{device
->IsHeadphones
!= AL_FALSE
};
746 if(device
->Type
!= Loopback
)
748 if(auto modeopt
= ConfigValueStr(device
->DeviceName
.c_str(), nullptr, "stereo-mode"))
750 const char *mode
{modeopt
->c_str()};
751 if(al::strcasecmp(mode
, "headphones") == 0)
753 else if(al::strcasecmp(mode
, "speakers") == 0)
755 else if(al::strcasecmp(mode
, "auto") != 0)
756 ERR("Unexpected stereo-mode: %s\n", mode
);
760 if(hrtf_userreq
== Hrtf_Default
)
762 bool usehrtf
= (headphones
&& hrtf_appreq
!= Hrtf_Disable
) ||
763 (hrtf_appreq
== Hrtf_Enable
);
764 if(!usehrtf
) goto no_hrtf
;
766 device
->HrtfStatus
= ALC_HRTF_ENABLED_SOFT
;
767 if(headphones
&& hrtf_appreq
!= Hrtf_Disable
)
768 device
->HrtfStatus
= ALC_HRTF_HEADPHONES_DETECTED_SOFT
;
772 if(hrtf_userreq
!= Hrtf_Enable
)
774 if(hrtf_appreq
== Hrtf_Enable
)
775 device
->HrtfStatus
= ALC_HRTF_DENIED_SOFT
;
778 device
->HrtfStatus
= ALC_HRTF_REQUIRED_SOFT
;
781 if(device
->HrtfList
.empty())
782 device
->HrtfList
= EnumerateHrtf(device
->DeviceName
.c_str());
784 if(hrtf_id
>= 0 && static_cast<ALuint
>(hrtf_id
) < device
->HrtfList
.size())
786 const EnumeratedHrtf
&entry
= device
->HrtfList
[static_cast<ALuint
>(hrtf_id
)];
787 HrtfEntry
*hrtf
{GetLoadedHrtf(entry
.hrtf
)};
788 if(hrtf
&& hrtf
->sampleRate
== device
->Frequency
)
790 device
->mHrtf
= hrtf
;
791 device
->HrtfName
= entry
.name
;
799 auto find_hrtf
= [device
](const EnumeratedHrtf
&entry
) -> bool
801 HrtfEntry
*hrtf
{GetLoadedHrtf(entry
.hrtf
)};
802 if(!hrtf
) return false;
803 if(hrtf
->sampleRate
!= device
->Frequency
)
808 device
->mHrtf
= hrtf
;
809 device
->HrtfName
= entry
.name
;
812 std::find_if(device
->HrtfList
.cbegin(), device
->HrtfList
.cend(), find_hrtf
);
821 InitHrtfPanning(device
);
822 device
->PostProcess
= &ALCdevice::ProcessHrtf
;
825 device
->HrtfStatus
= ALC_HRTF_UNSUPPORTED_FORMAT_SOFT
;
832 device
->mRenderMode
= StereoPair
;
834 if(device
->Type
!= Loopback
)
836 if(auto cflevopt
= ConfigValueInt(device
->DeviceName
.c_str(), nullptr, "cf_level"))
838 if(*cflevopt
> 0 && *cflevopt
<= 6)
840 device
->Bs2b
= al::make_unique
<bs2b
>();
841 bs2b_set_params(device
->Bs2b
.get(), *cflevopt
,
842 static_cast<int>(device
->Frequency
));
843 TRACE("BS2B enabled\n");
845 device
->PostProcess
= &ALCdevice::ProcessBs2b
;
851 if(auto encopt
= ConfigValueStr(device
->DeviceName
.c_str(), nullptr, "stereo-encoding"))
853 const char *mode
{encopt
->c_str()};
854 if(al::strcasecmp(mode
, "uhj") == 0)
855 device
->mRenderMode
= NormalRender
;
856 else if(al::strcasecmp(mode
, "panpot") != 0)
857 ERR("Unexpected stereo-encoding: %s\n", mode
);
859 if(device
->mRenderMode
== NormalRender
)
861 device
->Uhj_Encoder
= al::make_unique
<Uhj2Encoder
>();
862 TRACE("UHJ enabled\n");
863 InitUhjPanning(device
);
864 device
->PostProcess
= &ALCdevice::ProcessUhj
;
868 TRACE("Stereo rendering\n");
870 device
->PostProcess
= &ALCdevice::ProcessAmbiDec
;
874 void aluInitEffectPanning(ALeffectslot
*slot
, ALCdevice
*device
)
876 const size_t count
{AmbiChannelsFromOrder(device
->mAmbiOrder
)};
877 slot
->MixBuffer
.resize(count
);
878 slot
->MixBuffer
.shrink_to_fit();
880 auto acnmap_end
= AmbiIndex::From3D
.begin() + count
;
881 auto iter
= std::transform(AmbiIndex::From3D
.begin(), acnmap_end
, slot
->Wet
.AmbiMap
.begin(),
882 [](const uint8_t &acn
) noexcept
-> BFChannelConfig
883 { return BFChannelConfig
{1.0f
, acn
}; }
885 std::fill(iter
, slot
->Wet
.AmbiMap
.end(), BFChannelConfig
{});
886 slot
->Wet
.Buffer
= {slot
->MixBuffer
.data(), slot
->MixBuffer
.size()};
890 void CalcAmbiCoeffs(const float y
, const float z
, const float x
, const float spread
,
891 const al::span
<float,MAX_AMBI_CHANNELS
> coeffs
)
894 coeffs
[0] = 1.0f
; /* ACN 0 = 1 */
896 coeffs
[1] = 1.732050808f
* y
; /* ACN 1 = sqrt(3) * Y */
897 coeffs
[2] = 1.732050808f
* z
; /* ACN 2 = sqrt(3) * Z */
898 coeffs
[3] = 1.732050808f
* x
; /* ACN 3 = sqrt(3) * X */
900 coeffs
[4] = 3.872983346f
* x
* y
; /* ACN 4 = sqrt(15) * X * Y */
901 coeffs
[5] = 3.872983346f
* y
* z
; /* ACN 5 = sqrt(15) * Y * Z */
902 coeffs
[6] = 1.118033989f
* (z
*z
*3.0f
- 1.0f
); /* ACN 6 = sqrt(5)/2 * (3*Z*Z - 1) */
903 coeffs
[7] = 3.872983346f
* x
* z
; /* ACN 7 = sqrt(15) * X * Z */
904 coeffs
[8] = 1.936491673f
* (x
*x
- y
*y
); /* ACN 8 = sqrt(15)/2 * (X*X - Y*Y) */
906 coeffs
[9] = 2.091650066f
* y
* (x
*x
*3.0f
- y
*y
); /* ACN 9 = sqrt(35/8) * Y * (3*X*X - Y*Y) */
907 coeffs
[10] = 10.246950766f
* z
* x
* y
; /* ACN 10 = sqrt(105) * Z * X * Y */
908 coeffs
[11] = 1.620185175f
* y
* (z
*z
*5.0f
- 1.0f
); /* ACN 11 = sqrt(21/8) * Y * (5*Z*Z - 1) */
909 coeffs
[12] = 1.322875656f
* z
* (z
*z
*5.0f
- 3.0f
); /* ACN 12 = sqrt(7)/2 * Z * (5*Z*Z - 3) */
910 coeffs
[13] = 1.620185175f
* x
* (z
*z
*5.0f
- 1.0f
); /* ACN 13 = sqrt(21/8) * X * (5*Z*Z - 1) */
911 coeffs
[14] = 5.123475383f
* z
* (x
*x
- y
*y
); /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */
912 coeffs
[15] = 2.091650066f
* x
* (x
*x
- y
*y
*3.0f
); /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */
914 /* ACN 16 = sqrt(35)*3/2 * X * Y * (X*X - Y*Y) */
915 /* ACN 17 = sqrt(35/2)*3/2 * (3*X*X - Y*Y) * Y * Z */
916 /* ACN 18 = sqrt(5)*3/2 * X * Y * (7*Z*Z - 1) */
917 /* ACN 19 = sqrt(5/2)*3/2 * Y * Z * (7*Z*Z - 3) */
918 /* ACN 20 = 3/8 * (35*Z*Z*Z*Z - 30*Z*Z + 3) */
919 /* ACN 21 = sqrt(5/2)*3/2 * X * Z * (7*Z*Z - 3) */
920 /* ACN 22 = sqrt(5)*3/4 * (X*X - Y*Y) * (7*Z*Z - 1) */
921 /* ACN 23 = sqrt(35/2)*3/2 * (X*X - 3*Y*Y) * X * Z */
922 /* ACN 24 = sqrt(35)*3/8 * (X*X*X*X - 6*X*X*Y*Y + Y*Y*Y*Y) */
926 /* Implement the spread by using a spherical source that subtends the
928 * http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3
930 * When adjusted for N3D normalization instead of SN3D, these
933 * ZH0 = -sqrt(pi) * (-1+ca);
934 * ZH1 = 0.5*sqrt(pi) * sa*sa;
935 * ZH2 = -0.5*sqrt(pi) * ca*(-1+ca)*(ca+1);
936 * ZH3 = -0.125*sqrt(pi) * (-1+ca)*(ca+1)*(5*ca*ca - 1);
937 * ZH4 = -0.125*sqrt(pi) * ca*(-1+ca)*(ca+1)*(7*ca*ca - 3);
938 * ZH5 = -0.0625*sqrt(pi) * (-1+ca)*(ca+1)*(21*ca*ca*ca*ca - 14*ca*ca + 1);
940 * The gain of the source is compensated for size, so that the
941 * loudness doesn't depend on the spread. Thus:
944 * ZH1 = 0.5f * (ca+1.0f);
945 * ZH2 = 0.5f * (ca+1.0f)*ca;
946 * ZH3 = 0.125f * (ca+1.0f)*(5.0f*ca*ca - 1.0f);
947 * ZH4 = 0.125f * (ca+1.0f)*(7.0f*ca*ca - 3.0f)*ca;
948 * ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f);
950 const float ca
{std::cos(spread
* 0.5f
)};
951 /* Increase the source volume by up to +3dB for a full spread. */
952 const float scale
{std::sqrt(1.0f
+ spread
/al::MathDefs
<float>::Tau())};
954 const float ZH0_norm
{scale
};
955 const float ZH1_norm
{scale
* 0.5f
* (ca
+1.f
)};
956 const float ZH2_norm
{scale
* 0.5f
* (ca
+1.f
)*ca
};
957 const float ZH3_norm
{scale
* 0.125f
* (ca
+1.f
)*(5.f
*ca
*ca
-1.f
)};
960 coeffs
[0] *= ZH0_norm
;
962 coeffs
[1] *= ZH1_norm
;
963 coeffs
[2] *= ZH1_norm
;
964 coeffs
[3] *= ZH1_norm
;
966 coeffs
[4] *= ZH2_norm
;
967 coeffs
[5] *= ZH2_norm
;
968 coeffs
[6] *= ZH2_norm
;
969 coeffs
[7] *= ZH2_norm
;
970 coeffs
[8] *= ZH2_norm
;
972 coeffs
[9] *= ZH3_norm
;
973 coeffs
[10] *= ZH3_norm
;
974 coeffs
[11] *= ZH3_norm
;
975 coeffs
[12] *= ZH3_norm
;
976 coeffs
[13] *= ZH3_norm
;
977 coeffs
[14] *= ZH3_norm
;
978 coeffs
[15] *= ZH3_norm
;
982 void ComputePanGains(const MixParams
*mix
, const float*RESTRICT coeffs
, const float ingain
,
983 const al::span
<float,MAX_OUTPUT_CHANNELS
> gains
)
985 auto ambimap
= mix
->AmbiMap
.cbegin();
987 auto iter
= std::transform(ambimap
, ambimap
+mix
->Buffer
.size(), gains
.begin(),
988 [coeffs
,ingain
](const BFChannelConfig
&chanmap
) noexcept
-> float
989 { return chanmap
.Scale
* coeffs
[chanmap
.Index
] * ingain
; }
991 std::fill(iter
, gains
.end(), 0.0f
);